Completed
Push — update/sitemaps-libxml ( 292726 )
by
unknown
11:41
created

Jetpack_Sitemap_Builder::__construct()   B

Complexity

Conditions 3
Paths 2

Size

Total Lines 28
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 2
nop 0
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
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
require_once dirname( __FILE__ ) . '/sitemap-buffer-image.php';
13
require_once dirname( __FILE__ ) . '/sitemap-buffer-master.php';
14
require_once dirname( __FILE__ ) . '/sitemap-buffer-news.php';
15
require_once dirname( __FILE__ ) . '/sitemap-buffer-page.php';
16
require_once dirname( __FILE__ ) . '/sitemap-buffer-video.php';
17
require_once dirname( __FILE__ ) . '/sitemap-librarian.php';
18
require_once dirname( __FILE__ ) . '/sitemap-finder.php';
19
require_once dirname( __FILE__ ) . '/sitemap-state.php';
20
21
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
22
	require_once dirname( __FILE__ ) . '/sitemap-logger.php';
23
}
24
25
/**
26
 * The Jetpack_Sitemap_Builder object handles the construction of
27
 * all sitemap files (except the XSL files, which are handled by
28
 * Jetpack_Sitemap_Stylist.) Other than the constructor, there are
29
 * only two public functions: build_all_sitemaps and news_sitemap_xml.
30
 *
31
 * @since 4.8.0
32
 */
33
class Jetpack_Sitemap_Builder {
34
35
	/**
36
	 * Librarian object for storing and retrieving sitemap data.
37
	 *
38
	 * @access private
39
	 * @since 4.8.0
40
	 * @var $librarian Jetpack_Sitemap_Librarian
41
	 */
42
	private $librarian;
43
44
	/**
45
	 * Logger object for reporting debug messages.
46
	 *
47
	 * @access private
48
	 * @since 4.8.0
49
	 * @var $logger Jetpack_Sitemap_Logger
50
	 */
51
	private $logger = false;
52
53
	/**
54
	 * Finder object for dealing with sitemap URIs.
55
	 *
56
	 * @access private
57
	 * @since 4.8.0
58
	 * @var $finder Jetpack_Sitemap_Finder
59
	 */
60
	private $finder;
61
62
	/**
63
	 * Construct a new Jetpack_Sitemap_Builder object.
64
	 *
65
	 * @access public
66
	 * @since 4.8.0
67
	 */
68
	public function __construct() {
69
		$this->librarian = new Jetpack_Sitemap_Librarian();
70
		$this->finder = new Jetpack_Sitemap_Finder();
71
72
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
73
			$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...
74
		}
75
76
		update_option(
77
			'jetpack_sitemap_post_types',
78
			/**
79
			 * The array of post types to be included in the sitemap.
80
			 *
81
			 * Add your custom post type name to the array to have posts of
82
			 * that type included in the sitemap. The default array includes
83
			 * 'page' and 'post'.
84
			 *
85
			 * The result of this filter is cached in an option, 'jetpack_sitemap_post_types',
86
			 * so this filter only has to be applied once per generation.
87
			 *
88
			 * @since 4.8.0
89
			 */
90
			apply_filters(
91
				'jetpack_sitemap_post_types',
92
				array( 'post', 'page' )
93
			)
94
		);
95
	}
96
97
	/**
98
	 * Update the sitemap.
99
	 *
100
	 * All we do here is call build_next_sitemap_file a bunch of times.
101
	 *
102
	 * @since 4.8.0
103
	 */
104
	public function update_sitemap() {
105
		if ( $this->logger ) {
106
			$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...
107
		}
108
109
		for ( $i = 1; $i <= JP_SITEMAP_UPDATE_SIZE; $i++ ) {
110
			$this->build_next_sitemap_file();
111
		}
112
113
		if ( $this->logger ) {
114
			$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...
115
			$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...
116
		}
117
	}
118
119
	/**
120
	 * Generate the next sitemap file.
121
	 *
122
	 * Reads the most recent state of the sitemap generation phase,
123
	 * constructs the next file, and updates the state.
124
	 *
125
	 * @since 4.8.0
126
	 */
127
	private function build_next_sitemap_file() {
128
		// Get the most recent state, and lock the state.
129
		$state = Jetpack_Sitemap_State::check_out();
130
131
		// Do nothing if the state was locked.
132
		if ( false === $state ) {
133
			return;
134
		}
135
136
		// Otherwise, branch on the sitemap-type key of $state.
137
		switch ( $state['sitemap-type'] ) {
138
			case JP_PAGE_SITEMAP_TYPE:
139
				$this->build_next_sitemap_of_type(
140
					JP_PAGE_SITEMAP_TYPE,
141
					array( $this, 'build_one_page_sitemap' ),
142
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 129 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...
143
				);
144
				break;
145
146
			case JP_PAGE_SITEMAP_INDEX_TYPE:
147
				$this->build_next_sitemap_index_of_type(
148
					JP_PAGE_SITEMAP_INDEX_TYPE,
149
					JP_IMAGE_SITEMAP_TYPE,
150
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 129 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...
151
				);
152
				break;
153
154
			case JP_IMAGE_SITEMAP_TYPE:
155
				$this->build_next_sitemap_of_type(
156
					JP_IMAGE_SITEMAP_TYPE,
157
					array( $this, 'build_one_image_sitemap' ),
158
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 129 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...
159
				);
160
				break;
161
162
			case JP_IMAGE_SITEMAP_INDEX_TYPE:
163
				$this->build_next_sitemap_index_of_type(
164
					JP_IMAGE_SITEMAP_INDEX_TYPE,
165
					JP_VIDEO_SITEMAP_TYPE,
166
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 129 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...
167
				);
168
				break;
169
170
			case JP_VIDEO_SITEMAP_TYPE:
171
				$this->build_next_sitemap_of_type(
172
					JP_VIDEO_SITEMAP_TYPE,
173
					array( $this, 'build_one_video_sitemap' ),
174
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 129 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...
175
				);
176
				break;
177
178
			case JP_VIDEO_SITEMAP_INDEX_TYPE:
179
				$this->build_next_sitemap_index_of_type(
180
					JP_VIDEO_SITEMAP_INDEX_TYPE,
181
					JP_MASTER_SITEMAP_TYPE,
182
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 129 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...
183
				);
184
				break;
185
186
			case JP_MASTER_SITEMAP_TYPE:
187
				$this->build_master_sitemap( $state['max'] );
188
189
				// Reset the state and quit.
190
				Jetpack_Sitemap_State::reset(
191
					JP_PAGE_SITEMAP_TYPE
192
				);
193
194
				if ( $this->logger ) {
195
					$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...
196
					$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...
197
				}
198
199
				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...
200
201
			default:
202
				// Otherwise, reset the state.
203
				Jetpack_Sitemap_State::reset(
204
					JP_PAGE_SITEMAP_TYPE
205
				);
206
				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...
207
		} // End switch().
208
209
		// Unlock the state.
210
		Jetpack_Sitemap_State::unlock();
211
	}
212
213
	/**
214
	 * Build the next sitemap of a given type and update the sitemap state.
215
	 *
216
	 * @since 4.8.0
217
	 *
218
	 * @param string   $sitemap_type The type of the sitemap being generated.
219
	 * @param callback $build_one    A callback which builds a single sitemap file.
220
	 * @param array    $state        A sitemap state.
221
	 */
222
	private function build_next_sitemap_of_type( $sitemap_type, $build_one, $state ) {
223
		$index_type = jp_sitemap_index_type_of( $sitemap_type );
224
225
		// Try to build a sitemap.
226
		$result = call_user_func_array(
227
			$build_one,
228
			array(
229
				$state['number'] + 1,
230
				$state['last-added'],
231
			)
232
		);
233
234 View Code Duplication
		if ( false === $result ) {
235
			// If no sitemap was generated, advance to the next type.
236
			Jetpack_Sitemap_State::check_in( array(
237
				'sitemap-type'  => $index_type,
238
				'last-added'    => 0,
239
				'number'        => 0,
240
				'last-modified' => '1970-01-01 00:00:00',
241
			) );
242
243
			if ( $this->logger ) {
244
				$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...
245
			}
246
247
			// Clean up old files.
248
			$this->librarian->delete_numbered_sitemap_rows_after(
249
				$state['number'], $sitemap_type
250
			);
251
252
			return;
253
		}
254
255
		// Otherwise, update the state.
256
		Jetpack_Sitemap_State::check_in( array(
257
			'sitemap-type'  => $state['sitemap-type'],
258
			'last-added'    => $result['last_id'],
259
			'number'        => $state['number'] + 1,
260
			'last-modified' => $result['last_modified'],
261
		) );
262
263
		if ( true === $result['any_left'] ) {
264
			// If there's more work to be done with this type, return.
265
			return;
266
		}
267
268
		// Otherwise, advance state to the next sitemap type.
269
		Jetpack_Sitemap_State::check_in( array(
270
			'sitemap-type'  => $index_type,
271
			'last-added'    => 0,
272
			'number'        => 0,
273
			'last-modified' => '1970-01-01 00:00:00',
274
		) );
275
276
		if ( $this->logger ) {
277
			$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...
278
		}
279
280
		// Clean up old files.
281
		$this->librarian->delete_numbered_sitemap_rows_after(
282
			$state['number'] + 1, $sitemap_type
283
		);
284
	}
285
286
	/**
287
	 * Build the next sitemap index of a given type and update the state.
288
	 *
289
	 * @since 4.8.0
290
	 *
291
	 * @param string $index_type The type of index being generated.
292
	 * @param string $next_type  The next type to generate after this one.
293
	 * @param array  $state      A sitemap state.
294
	 */
295
	private function build_next_sitemap_index_of_type( $index_type, $next_type, $state ) {
296
		$sitemap_type = jp_sitemap_child_type_of( $index_type );
297
298
		// If only 0 or 1 sitemaps were built, advance to the next type and return.
299
		if ( 1 >= $state['max'][ $sitemap_type ]['number'] ) {
300
			Jetpack_Sitemap_State::check_in( array(
301
				'sitemap-type'  => $next_type,
302
				'last-added'    => 0,
303
				'number'        => 0,
304
				'last-modified' => '1970-01-01 00:00:00',
305
			) );
306
307
			if ( $this->logger ) {
308
				$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...
309
			}
310
311
			// There are no indices of this type.
312
			$this->librarian->delete_numbered_sitemap_rows_after(
313
				0, $index_type
314
			);
315
316
			return;
317
		}
318
319
		// Otherwise, try to build a sitemap index.
320
		$result = $this->build_one_sitemap_index(
321
			$state['number'] + 1,
322
			$state['last-added'],
323
			$state['last-modified'],
324
			$index_type
325
		);
326
327
		// If no index was built, advance to the next type and return.
328 View Code Duplication
		if ( false === $result ) {
329
			Jetpack_Sitemap_State::check_in( array(
330
				'sitemap-type'  => $next_type,
331
				'last-added'    => 0,
332
				'number'        => 0,
333
				'last-modified' => '1970-01-01 00:00:00',
334
			) );
335
336
			if ( $this->logger ) {
337
				$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...
338
			}
339
340
			// Clean up old files.
341
			$this->librarian->delete_numbered_sitemap_rows_after(
342
				$state['number'], $index_type
343
			);
344
345
			return;
346
		}
347
348
		// Otherwise, check in the state.
349
		Jetpack_Sitemap_State::check_in( array(
350
			'sitemap-type'  => $index_type,
351
			'last-added'    => $result['last_id'],
352
			'number'        => $state['number'] + 1,
353
			'last-modified' => $result['last_modified'],
354
		) );
355
356
		// If there are still sitemaps left to index, return.
357
		if ( true === $result['any_left'] ) {
358
			return;
359
		}
360
361
		// Otherwise, advance to the next type.
362
		Jetpack_Sitemap_State::check_in( array(
363
			'sitemap-type'  => $next_type,
364
			'last-added'    => 0,
365
			'number'        => 0,
366
			'last-modified' => '1970-01-01 00:00:00',
367
		) );
368
369
		if ( $this->logger ) {
370
			$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...
371
		}
372
373
		// We're done generating indices of this type.
374
		$this->librarian->delete_numbered_sitemap_rows_after(
375
			$state['number'] + 1, $index_type
376
		);
377
378
		return;
379
	}
380
381
	/**
382
	 * Builds the master sitemap index.
383
	 *
384
	 * @param array $max Array of sitemap types with max index and datetime.
385
	 *
386
	 * @since 4.8.0
387
	 */
388
	private function build_master_sitemap( $max ) {
389
		if ( $this->logger ) {
390
			$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...
391
		}
392
393
		$buffer = new Jetpack_Sitemap_Buffer_Master(
394
			JP_SITEMAP_MAX_ITEMS,
395
			JP_SITEMAP_MAX_BYTES
396
		);
397
398 View Code Duplication
		if ( 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
399
			if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
400
				$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...
401
				$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] );
402
			} else {
403
				$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...
404
					JP_PAGE_SITEMAP_INDEX_TYPE,
405
					$max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number']
406
				);
407
				$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
408
			}
409
410
			$buffer->append(
411
				array(
412
					'sitemap' => array(
413
						'loc'     => $this->finder->construct_sitemap_url( $page['filename'] ),
414
						'lastmod' => $page['last_modified'],
415
					),
416
				)
417
			);
418
		}
419
420 View Code Duplication
		if ( 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
421
			if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
422
				$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...
423
				$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] );
424
			} else {
425
				$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...
426
					JP_IMAGE_SITEMAP_INDEX_TYPE,
427
					$max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number']
428
				);
429
				$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
430
			}
431
432
			$buffer->append(
433
				array(
434
					'sitemap' => array(
435
						'loc'     => $this->finder->construct_sitemap_url( $image['filename'] ),
436
						'lastmod' => $image['last_modified'],
437
					),
438
				)
439
			);
440
		}
441
442 View Code Duplication
		if ( 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
443
			if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
444
				$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...
445
				$video['last_modified'] = $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod'];
446
			} else {
447
				$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...
448
					JP_VIDEO_SITEMAP_INDEX_TYPE,
449
					$max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number']
450
				);
451
				$video['last_modified'] = $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod'];
452
			}
453
454
			$buffer->append(
455
				array(
456
					'sitemap' => array(
457
						'loc'     => $this->finder->construct_sitemap_url( $video['filename'] ),
458
						'lastmod' => $video['last_modified'],
459
					),
460
				)
461
			);
462
		}
463
464
		$this->librarian->store_sitemap_data(
465
			0,
466
			JP_MASTER_SITEMAP_TYPE,
467
			$buffer->contents(),
468
			''
469
		);
470
	}
471
472
	/**
473
	 * Build and store a single page sitemap. Returns false if no sitemap is built.
474
	 *
475
	 * Side effect: Create/update a sitemap row.
476
	 *
477
	 * @access private
478
	 * @since 4.8.0
479
	 *
480
	 * @param int $number The number of the current sitemap.
481
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
482
	 *
483
	 * @return bool|array @args {
484
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
485
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
486
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
487
	 * }
488
	 */
489
	public function build_one_page_sitemap( $number, $from_id ) {
490
		$last_post_id = $from_id;
491
		$any_posts_left = true;
492
493
		if ( $this->logger ) {
494
			$debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number );
495
			$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...
496
		}
497
498
		$buffer = new Jetpack_Sitemap_Buffer_Page(
499
			JP_SITEMAP_MAX_ITEMS,
500
			JP_SITEMAP_MAX_BYTES
501
		);
502
503
		// Add entry for the main page (only if we're at the first one).
504
		if ( 1 === $number ) {
505
			$item_array = array(
506
				'url' => array(
507
					'loc' => home_url(),
508
				),
509
			);
510
511
			/**
512
			 * Filter associative array with data to build <url> node
513
			 * and its descendants for site home.
514
			 *
515
			 * @module sitemaps
516
			 *
517
			 * @since 3.9.0
518
			 *
519
			 * @param array $blog_home Data to build parent and children nodes for site home.
520
			 */
521
			$item_array = apply_filters( 'jetpack_sitemap_url_home', $item_array );
522
523
			$buffer->append( $item_array );
524
		}
525
526
		// Add as many items to the buffer as possible.
527
		while ( false === $buffer->is_full() ) {
528
			$posts = $this->librarian->query_posts_after_id(
529
				$last_post_id, JP_SITEMAP_BATCH_SIZE
530
			);
531
532
			if ( null == $posts ) { // WPCS: loose comparison ok.
533
				$any_posts_left = false;
534
				break;
535
			}
536
537
			foreach ( $posts as $post ) {
538
				$current_item = $this->post_to_sitemap_item( $post );
539
540
				if ( true === $buffer->append( $current_item['xml'] ) ) {
541
					$last_post_id = $post->ID;
542
					$buffer->view_time( $current_item['last_modified'] );
543
				} else {
544
					break;
545
				}
546
			}
547
		}
548
549
		// If no items were added, return false.
550
		if ( true === $buffer->is_empty() ) {
551
			return false;
552
		}
553
554
		/**
555
		 * Filter sitemap before rendering it as XML.
556
		 *
557
		 * @module sitemaps
558
		 *
559
		 * @since 3.9.0
560
		 * @since 5.1.0 returns an element of DOMDocument type instead of SimpleXMLElement
561
		 *
562
		 * @param DOMDocument      $doc Data tree for sitemap.
563
		 * @param string           $last_modified Date of last modification.
564
		 */
565
		$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...
566
			'jetpack_print_sitemap',
567
			$buffer->get_document(),
568
			$buffer->last_modified()
569
		);
570
571
		// Store the buffer as the content of a sitemap row.
572
		$this->librarian->store_sitemap_data(
573
			$number,
574
			JP_PAGE_SITEMAP_TYPE,
575
			$buffer->contents(),
576
			$buffer->last_modified()
577
		);
578
579
		/*
580
		 * Now report back with the ID of the last post ID to be
581
		 * successfully added and whether there are any posts left.
582
		 */
583
		return array(
584
			'last_id'       => $last_post_id,
585
			'any_left'      => $any_posts_left,
586
			'last_modified' => $buffer->last_modified(),
587
		);
588
	}
589
590
	/**
591
	 * Build and store a single image sitemap. Returns false if no sitemap is built.
592
	 *
593
	 * Side effect: Create/update an image sitemap row.
594
	 *
595
	 * @access private
596
	 * @since 4.8.0
597
	 *
598
	 * @param int $number The number of the current sitemap.
599
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
600
	 *
601
	 * @return bool|array @args {
602
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
603
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
604
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
605
	 * }
606
	 */
607 View Code Duplication
	public function build_one_image_sitemap( $number, $from_id ) {
608
		$last_post_id = $from_id;
609
		$any_posts_left = true;
610
611
		if ( $this->logger ) {
612
			$debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number );
613
			$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...
614
		}
615
616
		$buffer = new Jetpack_Sitemap_Buffer_Image(
617
			JP_SITEMAP_MAX_ITEMS,
618
			JP_SITEMAP_MAX_BYTES
619
		);
620
621
		// Add as many items to the buffer as possible.
622
		while ( false === $buffer->is_full() ) {
623
			$posts = $this->librarian->query_images_after_id(
624
				$last_post_id, JP_SITEMAP_BATCH_SIZE
625
			);
626
627
			if ( null == $posts ) { // WPCS: loose comparison ok.
628
				$any_posts_left = false;
629
				break;
630
			}
631
632
			foreach ( $posts as $post ) {
633
				$current_item = $this->image_post_to_sitemap_item( $post );
634
635
				if ( true === $buffer->append( $current_item['xml'] ) ) {
636
					$last_post_id = $post->ID;
637
					$buffer->view_time( $current_item['last_modified'] );
638
				} else {
639
					break;
640
				}
641
			}
642
		}
643
644
		// If no items were added, return false.
645
		if ( true === $buffer->is_empty() ) {
646
			return false;
647
		}
648
649
		// Store the buffer as the content of a jp_sitemap post.
650
		$this->librarian->store_sitemap_data(
651
			$number,
652
			JP_IMAGE_SITEMAP_TYPE,
653
			$buffer->contents(),
654
			$buffer->last_modified()
655
		);
656
657
		/*
658
		 * Now report back with the ID of the last post to be
659
		 * successfully added and whether there are any posts left.
660
		 */
661
		return array(
662
			'last_id'       => $last_post_id,
663
			'any_left'      => $any_posts_left,
664
			'last_modified' => $buffer->last_modified(),
665
		);
666
	}
667
668
	/**
669
	 * Build and store a single video sitemap. Returns false if no sitemap is built.
670
	 *
671
	 * Side effect: Create/update an video sitemap row.
672
	 *
673
	 * @access private
674
	 * @since 4.8.0
675
	 *
676
	 * @param int $number The number of the current sitemap.
677
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
678
	 *
679
	 * @return bool|array @args {
680
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
681
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
682
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
683
	 * }
684
	 */
685 View Code Duplication
	public function build_one_video_sitemap( $number, $from_id ) {
686
		$last_post_id = $from_id;
687
		$any_posts_left = true;
688
689
		if ( $this->logger ) {
690
			$debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number );
691
			$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...
692
		}
693
694
		$buffer = new Jetpack_Sitemap_Buffer_Video(
695
			JP_SITEMAP_MAX_ITEMS,
696
			JP_SITEMAP_MAX_BYTES
697
		);
698
699
		// Add as many items to the buffer as possible.
700
		while ( false === $buffer->is_full() ) {
701
			$posts = $this->librarian->query_videos_after_id(
702
				$last_post_id, JP_SITEMAP_BATCH_SIZE
703
			);
704
705
			if ( null == $posts ) { // WPCS: loose comparison ok.
706
				$any_posts_left = false;
707
				break;
708
			}
709
710
			foreach ( $posts as $post ) {
711
				$current_item = $this->video_post_to_sitemap_item( $post );
712
713
				if ( true === $buffer->append( $current_item['xml'] ) ) {
714
					$last_post_id = $post->ID;
715
					$buffer->view_time( $current_item['last_modified'] );
716
				} else {
717
					break;
718
				}
719
			}
720
		}
721
722
		// If no items were added, return false.
723
		if ( true === $buffer->is_empty() ) {
724
			return false;
725
		}
726
727
		if ( false === $buffer->is_empty() ) {
728
			$this->librarian->store_sitemap_data(
729
				$number,
730
				JP_VIDEO_SITEMAP_TYPE,
731
				$buffer->contents(),
732
				$buffer->last_modified()
733
			);
734
		}
735
736
		/*
737
		 * Now report back with the ID of the last post to be
738
		 * successfully added and whether there are any posts left.
739
		 */
740
		return array(
741
			'last_id'       => $last_post_id,
742
			'any_left'      => $any_posts_left,
743
			'last_modified' => $buffer->last_modified(),
744
		);
745
	}
746
747
	/**
748
	 * Build and store a single page sitemap index. Return false if no index is built.
749
	 *
750
	 * Side effect: Create/update a sitemap index row.
751
	 *
752
	 * @access private
753
	 * @since 4.8.0
754
	 *
755
	 * @param int    $number     The number of the current sitemap index.
756
	 * @param int    $from_id    The greatest lower bound of the IDs of the sitemaps to be included.
757
	 * @param string $datetime   Datetime of previous sitemap in 'YYYY-MM-DD hh:mm:ss' format.
758
	 * @param string $index_type Sitemap index type.
759
	 *
760
	 * @return bool|array @args {
761
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
762
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
763
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
764
	 * }
765
	 */
766
	private function build_one_sitemap_index( $number, $from_id, $datetime, $index_type ) {
767
		$last_sitemap_id   = $from_id;
768
		$any_sitemaps_left = true;
769
770
		// Check the datetime format.
771
		$datetime = jp_sitemap_datetime( $datetime );
772
773
		$sitemap_type = jp_sitemap_child_type_of( $index_type );
774
775
		if ( $this->logger ) {
776
			$index_debug_name = jp_sitemap_filename( $index_type, $number );
777
			$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...
778
		}
779
780
		$buffer = new Jetpack_Sitemap_Buffer_Master(
781
			JP_SITEMAP_MAX_ITEMS,
782
			JP_SITEMAP_MAX_BYTES,
783
			$datetime
784
		);
785
786
		// Add pointer to the previous sitemap index (unless we're at the first one).
787
		if ( 1 !== $number ) {
788
			$i = $number - 1;
789
			$prev_index_url = $this->finder->construct_sitemap_url(
790
				jp_sitemap_filename( $index_type, $i )
791
			);
792
793
			$item_array = array(
794
				'sitemap' => array(
795
					'loc'     => $prev_index_url,
796
					'lastmod' => $datetime,
797
				),
798
			);
799
800
			$buffer->append( $item_array );
801
		}
802
803
		// Add as many items to the buffer as possible.
804
		while ( false === $buffer->is_full() ) {
805
			// Retrieve a batch of posts (in order).
806
			$posts = $this->librarian->query_sitemaps_after_id(
807
				$sitemap_type, $last_sitemap_id, JP_SITEMAP_BATCH_SIZE
808
			);
809
810
			// If there were no posts to get, make a note.
811
			if ( null == $posts ) { // WPCS: loose comparison ok.
812
				$any_sitemaps_left = false;
813
				break;
814
			}
815
816
			// Otherwise, loop through each post in the batch.
817
			foreach ( $posts as $post ) {
818
				// Generate the sitemap XML for the post.
819
				$current_item = $this->sitemap_row_to_index_item( (array) $post );
820
821
				// Try adding this item to the buffer.
822
				if ( true === $buffer->append( $current_item['xml'] ) ) {
823
					$last_sitemap_id = $post['ID'];
824
					$buffer->view_time( $current_item['last_modified'] );
825
				} else {
826
					// Otherwise stop looping through posts.
827
					break;
828
				}
829
			}
830
		}
831
832
		// If no items were added, return false.
833
		if ( true === $buffer->is_empty() ) {
834
			return false;
835
		}
836
837
		$this->librarian->store_sitemap_data(
838
			$number,
839
			$index_type,
840
			$buffer->contents(),
841
			$buffer->last_modified()
842
		);
843
844
		/*
845
		 * Now report back with the ID of the last sitemap post ID to
846
		 * be successfully added, whether there are any sitemap posts
847
		 * left, and the most recent modification time seen.
848
		 */
849
		return array(
850
			'last_id'       => $last_sitemap_id,
851
			'any_left'      => $any_sitemaps_left,
852
			'last_modified' => $buffer->last_modified(),
853
		);
854
	}
855
856
	/**
857
	 * Construct the sitemap index url entry for a sitemap row.
858
	 *
859
	 * @link http://www.sitemaps.org/protocol.html#sitemapIndex_sitemap
860
	 *
861
	 * @access private
862
	 * @since 4.8.0
863
	 *
864
	 * @param array $row The sitemap data to be processed.
865
	 *
866
	 * @return string An XML fragment representing the post URL.
867
	 */
868
	private function sitemap_row_to_index_item( $row ) {
869
		$url = $this->finder->construct_sitemap_url( $row['post_title'] );
870
871
		$item_array = array(
872
			'sitemap' => array(
873
				'loc'     => $url,
874
				'lastmod' => jp_sitemap_datetime( $row['post_date'] ),
875
			),
876
		);
877
878
		return array(
879
			'xml'           => $item_array,
880
			'last_modified' => $row['post_date'],
881
		);
882
	}
883
884
	/**
885
	 * Build and return the news sitemap xml. Note that the result of this
886
	 * function is cached in the transient 'jetpack_news_sitemap_xml'.
887
	 *
888
	 * @access public
889
	 * @since 4.8.0
890
	 *
891
	 * @return string The news sitemap xml.
892
	 */
893
	public function news_sitemap_xml() {
894
		$the_stored_news_sitemap = get_transient( 'jetpack_news_sitemap_xml' );
895
896
		if ( false === $the_stored_news_sitemap ) {
897
898
			if ( $this->logger ) {
899
				$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...
900
			}
901
902
			/**
903
			 * Filter limit of entries to include in news sitemap.
904
			 *
905
			 * @module sitemaps
906
			 *
907
			 * @since 3.9.0
908
			 *
909
			 * @param int $count Number of entries to include in news sitemap.
910
			 */
911
			$item_limit = apply_filters(
912
				'jetpack_sitemap_news_sitemap_count',
913
				JP_NEWS_SITEMAP_MAX_ITEMS
914
			);
915
916
			$buffer = new Jetpack_Sitemap_Buffer_News(
917
				min( $item_limit, JP_NEWS_SITEMAP_MAX_ITEMS ),
918
				JP_SITEMAP_MAX_BYTES
919
			);
920
921
			$posts = $this->librarian->query_most_recent_posts( JP_NEWS_SITEMAP_MAX_ITEMS );
922
923
			foreach ( $posts as $post ) {
924
				$current_item = $this->post_to_news_sitemap_item( $post );
925
926
				if ( false === $buffer->append( $current_item['xml'] ) ) {
927
					break;
928
				}
929
			}
930
931
			if ( $this->logger ) {
932
				$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...
933
			}
934
935
			$the_stored_news_sitemap = $buffer->contents();
936
937
			set_transient(
938
				'jetpack_news_sitemap_xml',
939
				$the_stored_news_sitemap,
940
				JP_NEWS_SITEMAP_INTERVAL
941
			);
942
		} // End if().
943
944
		return $the_stored_news_sitemap;
945
	}
946
947
	/**
948
	 * Construct the sitemap url entry for a WP_Post.
949
	 *
950
	 * @link http://www.sitemaps.org/protocol.html#urldef
951
	 * @access private
952
	 * @since 4.8.0
953
	 *
954
	 * @param WP_Post $post The post to be processed.
955
	 *
956
	 * @return array An array representing the post URL.
957
	 */
958
	private function post_to_sitemap_item( $post ) {
959
960
		/**
961
		 * Filter condition to allow skipping specific posts in sitemap.
962
		 *
963
		 * @module sitemaps
964
		 *
965
		 * @since 3.9.0
966
		 *
967
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
968
		 * @param WP_POST $post Current post object.
969
		 */
970
		if ( true === apply_filters( 'jetpack_sitemap_skip_post', false, $post ) ) {
971
			return array(
972
				'xml'           => null,
973
				'last_modified' => null,
974
			);
975
		}
976
977
		$url = esc_url( get_permalink( $post ) );
978
979
		/*
980
		 * Spec requires the URL to be <=2048 bytes.
981
		 * In practice this constraint is unlikely to be violated.
982
		 */
983
		if ( 2048 < strlen( $url ) ) {
984
			$url = home_url() . '/?p=' . $post->ID;
985
		}
986
987
		$last_modified = $post->post_modified_gmt;
988
989
		// Check for more recent comments.
990
		// Note that 'Y-m-d h:i:s' strings sort lexicographically.
991
		if ( 0 < $post->comment_count ) {
992
			$last_modified = max(
993
				$last_modified,
994
				$this->librarian->query_latest_approved_comment_time_on_post( $post->ID )
995
			);
996
		}
997
998
		$item_array = array(
999
			'url' => array(
1000
				'loc'     => $url,
1001
				'lastmod' => jp_sitemap_datetime( $last_modified ),
1002
			),
1003
		);
1004
1005
		/**
1006
		 * Filter sitemap URL item before rendering it as XML.
1007
		 *
1008
		 * @module sitemaps
1009
		 *
1010
		 * @since 3.9.0
1011
		 *
1012
		 * @param array $tree Associative array representing sitemap URL element.
1013
		 * @param int   $post_id ID of the post being processed.
1014
		 */
1015
		$item_array = apply_filters( 'jetpack_sitemap_url', $item_array, $post->ID );
1016
1017
		return array(
1018
			'xml'           => $item_array,
1019
			'last_modified' => $last_modified,
1020
		);
1021
	}
1022
1023
	/**
1024
	 * Construct the image sitemap url entry for a WP_Post of image type.
1025
	 *
1026
	 * @link http://www.sitemaps.org/protocol.html#urldef
1027
	 *
1028
	 * @access private
1029
	 * @since 4.8.0
1030
	 *
1031
	 * @param WP_Post $post The image post to be processed.
1032
	 *
1033
	 * @return string An XML fragment representing the post URL.
1034
	 */
1035
	private function image_post_to_sitemap_item( $post ) {
1036
1037
		/**
1038
		 * Filter condition to allow skipping specific image posts in the sitemap.
1039
		 *
1040
		 * @module sitemaps
1041
		 *
1042
		 * @since 4.8.0
1043
		 *
1044
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1045
		 * @param WP_POST $post Current post object.
1046
		 */
1047
		if ( apply_filters( 'jetpack_sitemap_image_skip_post', false, $post ) ) {
1048
			return array(
1049
				'xml'           => null,
1050
				'last_modified' => null,
1051
			);
1052
		}
1053
1054
		$url = wp_get_attachment_url( $post->ID );
1055
1056
		$parent_url = get_permalink( get_post( $post->post_parent ) );
1057
		if ( '' == $parent_url ) { // WPCS: loose comparison ok.
1058
			$parent_url = get_permalink( $post );
1059
		}
1060
1061
		$item_array = array(
1062
			'url' => array(
1063
				'loc'         => $parent_url,
1064
				'lastmod'     => jp_sitemap_datetime( $post->post_modified_gmt ),
1065
				'image:image' => array(
1066
					'image:loc' => $url,
1067
				),
1068
			),
1069
		);
1070
1071
		$item_array['url']['image:image']['image:title'] = $post->post_title;
1072
		$item_array['url']['image:image']['image:caption'] = $post->post_excerpt;
1073
1074
		/**
1075
		 * Filter associative array with data to build <url> node
1076
		 * and its descendants for current post in image sitemap.
1077
		 *
1078
		 * @module sitemaps
1079
		 *
1080
		 * @since 4.8.0
1081
		 *
1082
		 * @param array $item_array Data to build parent and children nodes for current post.
1083
		 * @param int   $post_id Current image post ID.
1084
		 */
1085
		$item_array = apply_filters(
1086
			'jetpack_sitemap_image_sitemap_item',
1087
			$item_array,
1088
			$post->ID
1089
		);
1090
1091
		return array(
1092
			'xml'           => $item_array,
1093
			'last_modified' => $post->post_modified_gmt,
1094
		);
1095
	}
1096
1097
	/**
1098
	 * Construct the video sitemap url entry for a WP_Post of video type.
1099
	 *
1100
	 * @link http://www.sitemaps.org/protocol.html#urldef
1101
	 * @link https://developers.google.com/webmasters/videosearch/sitemaps
1102
	 *
1103
	 * @access private
1104
	 * @since 4.8.0
1105
	 *
1106
	 * @param WP_Post $post The video post to be processed.
1107
	 *
1108
	 * @return string An XML fragment representing the post URL.
1109
	 */
1110
	private function video_post_to_sitemap_item( $post ) {
1111
1112
		/**
1113
		 * Filter condition to allow skipping specific image posts in the sitemap.
1114
		 *
1115
		 * @module sitemaps
1116
		 *
1117
		 * @since 4.8.0
1118
		 *
1119
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1120
		 * @param WP_POST $post Current post object.
1121
		 */
1122
		if ( apply_filters( 'jetpack_sitemap_video_skip_post', false, $post ) ) {
1123
			return array(
1124
				'xml'           => null,
1125
				'last_modified' => null,
1126
			);
1127
		}
1128
1129
		$parent_url = esc_url( get_permalink( get_post( $post->post_parent ) ) );
1130
		if ( '' == $parent_url ) { // WPCS: loose comparison ok.
1131
			$parent_url = esc_url( get_permalink( $post ) );
1132
		}
1133
1134
		// Prepare the content like get_the_content_feed().
1135
		$content = $post->post_content;
1136
		/** This filter is already documented in core/wp-includes/post-template.php */
1137
		$content = apply_filters( 'the_content', $content );
1138
1139
		/** This filter is already documented in core/wp-includes/feed.php */
1140
		$content = apply_filters( 'the_content_feed', $content, 'rss2' );
1141
1142
		$item_array = array(
1143
			'url' => array(
1144
				'loc'         => $parent_url,
1145
				'lastmod'     => jp_sitemap_datetime( $post->post_modified_gmt ),
1146
				'video:video' => array(
1147
					/** This filter is already documented in core/wp-includes/feed.php */
1148
					'video:title'         => apply_filters( 'the_title_rss', $post->post_title ),
1149
					'video:thumbnail_loc' => '',
1150
					'video:description'   => $content,
1151
					'video:content_loc'   => esc_url( wp_get_attachment_url( $post->ID ) ),
1152
				),
1153
			),
1154
		);
1155
1156
		// 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...
1157
		// cf. video:player_loc tag in video sitemap spec.
1158
1159
		/**
1160
		 * Filter associative array with data to build <url> node
1161
		 * and its descendants for current post in video sitemap.
1162
		 *
1163
		 * @module sitemaps
1164
		 *
1165
		 * @since 4.8.0
1166
		 *
1167
		 * @param array $item_array Data to build parent and children nodes for current post.
1168
		 * @param int   $post_id Current video post ID.
1169
		 */
1170
		$item_array = apply_filters(
1171
			'jetpack_sitemap_video_sitemap_item',
1172
			$item_array,
1173
			$post->ID
1174
		);
1175
1176
		return array(
1177
			'xml'           => $item_array,
1178
			'last_modified' => $post->post_modified_gmt,
1179
		);
1180
	}
1181
1182
	/**
1183
	 * Construct the news sitemap url entry for a WP_Post.
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 post to be processed.
1191
	 *
1192
	 * @return string An XML fragment representing the post URL.
1193
	 */
1194
	private function post_to_news_sitemap_item( $post ) {
1195
1196
		/**
1197
		 * Filter condition to allow skipping specific posts in news sitemap.
1198
		 *
1199
		 * @module sitemaps
1200
		 *
1201
		 * @since 3.9.0
1202
		 *
1203
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1204
		 * @param WP_POST $post Current post object.
1205
		 */
1206
		if ( apply_filters( 'jetpack_sitemap_news_skip_post', false, $post ) ) {
1207
			return array(
1208
				'xml' => null,
1209
			);
1210
		}
1211
1212
		$url = get_permalink( $post );
1213
1214
		/*
1215
		 * Spec requires the URL to be <=2048 bytes.
1216
		 * In practice this constraint is unlikely to be violated.
1217
		 */
1218
		if ( 2048 < strlen( $url ) ) {
1219
			$url = home_url() . '/?p=' . $post->ID;
1220
		}
1221
1222
		/*
1223
		 * Trim the locale to an ISO 639 language code as required by Google.
1224
		 * Special cases are zh-cn (Simplified Chinese) and zh-tw (Traditional Chinese).
1225
		 * @link http://www.loc.gov/standards/iso639-2/php/code_list.php
1226
		 */
1227
		$language = strtolower( get_locale() );
1228
1229
		if ( in_array( $language, array( 'zh_tw', 'zh_cn' ), true ) ) {
1230
			$language = str_replace( '_', '-', $language );
1231
		} else {
1232
			$language = preg_replace( '/(_.*)$/i', '', $language );
1233
		}
1234
1235
		$item_array = array(
1236
			'url' => array(
1237
				'loc' => $url,
1238
				'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ),
1239
				'news:news' => array(
1240
					'news:publication' => array(
1241
						'news:name'     => get_bloginfo( 'name' ),
1242
						'news:language' => $language,
1243
					),
1244
					/** This filter is already documented in core/wp-includes/feed.php */
1245
					'news:title'            => apply_filters( 'the_title_rss', $post->post_title ),
1246
					'news:publication_date' => jp_sitemap_datetime( $post->post_date_gmt ),
1247
					'news:genres'           => 'Blog',
1248
				),
1249
			),
1250
		);
1251
1252
		/**
1253
		 * Filter associative array with data to build <url> node
1254
		 * and its descendants for current post in news sitemap.
1255
		 *
1256
		 * @module sitemaps
1257
		 *
1258
		 * @since 3.9.0
1259
		 *
1260
		 * @param array $item_array Data to build parent and children nodes for current post.
1261
		 * @param int   $post_id Current post ID.
1262
		 */
1263
		$item_array = apply_filters(
1264
			'jetpack_sitemap_news_sitemap_item',
1265
			$item_array,
1266
			$post->ID
1267
		);
1268
1269
		return array(
1270
			'xml' => $item_array,
1271
		);
1272
	}
1273
}
1274