PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 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 All 508 releases
← All changes | modules/sitemaps/sitemap-buffer.php +313 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,313 @@
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 +}