PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / modules / sitemaps / sitemap-buffer.php

sitemap-buffer.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at modules/sitemaps/sitemap-buffer.php

314 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Sitemaps (per the protocol) are essentially lists of XML fragments;
4 * lists which are subject to size constraints. The Jetpack_Sitemap_Buffer
5 * class abstracts the details of constructing these lists while
6 * maintaining the constraints.
7 *
8 * @since 4.8.0
9 * @package automattic/jetpack
10 */
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit( 0 );
14 }
15
16 /**
17 * A buffer for constructing sitemap xml files.
18 *
19 * Models a list of strings such that
20 *
21 * 1. the list must have a bounded number of entries,
22 * 2. the concatenation of the strings must have bounded
23 * length (including some header and footer strings), and
24 * 3. each item has a timestamp, and we need to keep track
25 * of the most recent timestamp of the items in the list.
26 *
27 * @since 4.8.0
28 */
29 abstract class Jetpack_Sitemap_Buffer {
30
31 /**
32 * Largest number of items the buffer can hold.
33 *
34 * @access protected
35 * @since 4.8.0
36 * @var int $item_capacity The item capacity.
37 */
38 protected $item_capacity;
39
40 /**
41 * Largest number of bytes the buffer can hold.
42 *
43 * @access protected
44 * @since 4.8.0
45 * @var int $byte_capacity The byte capacity.
46 */
47 protected $byte_capacity;
48
49 /**
50 * Flag which detects when the buffer is full.
51 *
52 * @access protected
53 * @since 4.8.0
54 * @var bool $is_full_flag The flag value. This flag is set to false on construction and only flipped to true if we've tried to add something and failed.
55 */
56 protected $is_full_flag;
57
58 /**
59 * Flag which detects when the buffer is empty.
60 *
61 * @access protected
62 * @since 4.8.0
63 * @var bool $is_empty_flag The flag value. This flag is set to true on construction and only flipped to false if we've tried to add something and succeeded.
64 */
65 protected $is_empty_flag;
66
67 /**
68 * The most recent timestamp seen by the buffer.
69 *
70 * @access protected
71 * @since 4.8.0
72 * @var string $timestamp Must be in 'YYYY-MM-DD hh:mm:ss' format.
73 */
74 protected $timestamp;
75
76 /**
77 * The DOM document object that is currently being used to construct the XML doc.
78 *
79 * @access protected
80 * @since 5.3.0
81 * @var DOMDocument $doc
82 */
83 protected $doc = null;
84
85 /**
86 * The root DOM element object that holds everything inside. Do not use directly, call
87 * the get_root_element getter method instead.
88 *
89 * @access protected
90 * @since 5.3.0
91 * @var DOMElement $doc
92 */
93 protected $root = null;
94
95 /**
96 * Helper class to construct sitemap paths.
97 *
98 * @since 5.3.0
99 * @protected
100 * @var Jetpack_Sitemap_Finder
101 */
102 protected $finder;
103
104 /**
105 * Construct a new Jetpack_Sitemap_Buffer.
106 *
107 * @since 4.8.0
108 *
109 * @param int $item_limit The maximum size of the buffer in items.
110 * @param int $byte_limit The maximum size of the buffer in bytes.
111 * @param string $time The initial datetime of the buffer. Must be in 'YYYY-MM-DD hh:mm:ss' format.
112 */
113 public function __construct( $item_limit, $byte_limit, $time ) {
114 $this->is_full_flag = false;
115 $this->timestamp = $time;
116
117 $this->finder = new Jetpack_Sitemap_Finder();
118 $this->doc = new DOMDocument( '1.0', 'UTF-8' );
119 $this->doc->formatOutput = true;
120 $this->doc->preserveWhiteSpace = false;
121
122 $this->item_capacity = max( 1, (int) $item_limit );
123 $this->byte_capacity = max( 1, (int) $byte_limit ) - strlen( $this->doc->saveXML() );
124 }
125
126 /**
127 * Returns a DOM element that contains all sitemap elements.
128 *
129 * @access protected
130 * @since 5.3.0
131 * @return DOMElement $root
132 */
133 abstract protected function get_root_element();
134
135 /**
136 * Append an item to the buffer, if there is room for it,
137 * and set is_empty_flag to false. If there is no room,
138 * we set is_full_flag to true. If $item is null,
139 * don't do anything and report success.
140 *
141 * @since 5.3.0
142 *
143 * @param array $array The item to be added.
144 *
145 * @return bool True if the append succeeded, False if not.
146 */
147 public function append( $array ) {
148 if ( $array === null ) {
149 return true;
150 }
151
152 if ( $this->is_full_flag ) {
153 return false;
154 }
155
156 if ( 0 >= $this->item_capacity || 0 >= $this->byte_capacity ) {
157 $this->is_full_flag = true;
158 return false;
159 } else {
160 $this->item_capacity -= 1;
161 $added_element = $this->array_to_xml_string( $array, $this->get_root_element(), $this->doc );
162
163 $this->byte_capacity -= strlen( $this->doc->saveXML( $added_element ) );
164
165 return true;
166 }
167 }
168
169 /**
170 * Retrieve the contents of the buffer.
171 *
172 * @since 4.8.0
173 *
174 * @return string The contents of the buffer (with the footer included).
175 */
176 public function contents() {
177 if ( $this->is_empty() ) {
178 // The sitemap should have at least the root element added to the DOM.
179 $this->get_root_element();
180 }
181 return $this->doc->saveXML();
182 }
183
184 /**
185 * Retrieve the document object.
186 *
187 * @since 5.3.0
188 * @return DOMDocument $doc
189 */
190 public function get_document() {
191 return $this->doc;
192 }
193
194 /**
195 * Detect whether the buffer is full.
196 *
197 * @since 4.8.0
198 *
199 * @return bool True if the buffer is full, false otherwise.
200 */
201 public function is_full() {
202 return $this->is_full_flag;
203 }
204
205 /**
206 * Detect whether the buffer is empty.
207 *
208 * @since 4.8.0
209 *
210 * @return bool True if the buffer is empty, false otherwise.
211 */
212 public function is_empty() {
213 return (
214 ! isset( $this->root )
215 || ! $this->root->hasChildNodes()
216 );
217 }
218
219 /**
220 * Update the timestamp of the buffer.
221 *
222 * @since 4.8.0
223 *
224 * @param string $new_time A datetime string in 'YYYY-MM-DD hh:mm:ss' format.
225 */
226 public function view_time( $new_time ) {
227 $this->timestamp = max( $this->timestamp, $new_time );
228 }
229
230 /**
231 * Retrieve the timestamp of the buffer.
232 *
233 * @since 4.8.0
234 *
235 * @return string A datetime string in 'YYYY-MM-DD hh:mm:ss' format.
236 */
237 public function last_modified() {
238 return $this->timestamp;
239 }
240
241 /**
242 * Render an associative array as an XML string. This is needed because
243 * SimpleXMLElement only handles valid XML, but we sometimes want to
244 * pass around (possibly invalid) fragments. Note that 'null' values make
245 * a tag self-closing; this is only sometimes correct (depending on the
246 * version of HTML/XML); see the list of 'void tags'.
247 *
248 * Example:
249 *
250 * array(
251 * 'html' => array( |<html xmlns="foo">
252 * 'head' => array( | <head>
253 * 'title' => 'Woo!', | <title>Woo!</title>
254 * ), | </head>
255 * 'body' => array( ==> | <body>
256 * 'h2' => 'Some thing', | <h2>Some thing</h2>
257 * 'p' => 'it's all up ons', | <p>it's all up ons</p>
258 * 'br' => null, | <br />
259 * ), | </body>
260 * ), |</html>
261 * )
262 *
263 * @access protected
264 * @since 3.9.0
265 * @since 4.8.0 Rename, add $depth parameter, and change return type.
266 * @since 5.3.0 Refactor, remove $depth parameter, add $parent and $root, make access protected.
267 *
268 * @param array $array A recursive associative array of tag/child relationships.
269 * @param DOMElement $parent (optional) an element to which new children should be added.
270 * @param DOMDocument $root (optional) the parent document.
271 *
272 * @return string|DOMDocument The rendered XML string or an object if root element is specified.
273 */
274 protected function array_to_xml_string( $array, $parent = null, $root = null ) {
275 $element = null;
276 $return_string = false;
277
278 if ( null === $parent ) {
279 $return_string = true;
280 $root = new DOMDocument();
281 $parent = $root;
282 }
283
284 if ( is_array( $array ) ) {
285
286 foreach ( $array as $key => $value ) {
287 $element = $root->createElement( $key );
288 $parent->appendChild( $element );
289
290 if ( is_array( $value ) ) {
291 foreach ( $value as $child_key => $child_value ) {
292 $child = $root->createElement( $child_key );
293 $element->appendChild( $child );
294 $child->appendChild( self::array_to_xml_string( $child_value, $child, $root ) );
295 }
296 } else {
297 $element->appendChild(
298 $root->createTextNode( $value )
299 );
300 }
301 }
302 } else {
303 $element = $root->createTextNode( $array );
304 $parent->appendChild( $element );
305 }
306
307 if ( $return_string ) {
308 return $root->saveHTML();
309 } else {
310 return $element;
311 }
312 }
313 }
314