Completed
Push — add/wordads-ads-txt ( b6a14d...7c89f2 )
by
unknown
46:03 queued 34:13
created

Jetpack_Sitemap_Builder::build_one_sitemap_index()   C

Complexity

Conditions 8
Paths 16

Size

Total Lines 89
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

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