Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
50 | class Jetpack_Sitemap_Manager { |
||
51 | |||
52 | /** |
||
53 | * @see Jetpack_Sitemap_Librarian |
||
54 | * @since 4.8.0 |
||
55 | * @var Jetpack_Sitemap_Librarian $librarian Librarian object for storing and retrieving sitemap data. |
||
56 | */ |
||
57 | private $librarian; |
||
58 | |||
59 | /** |
||
60 | * @see Jetpack_Sitemap_Logger |
||
61 | * @since 4.8.0 |
||
62 | * @var Jetpack_Sitemap_Logger $logger Logger object for reporting debug messages. |
||
63 | */ |
||
64 | private $logger; |
||
65 | |||
66 | /** |
||
67 | * @see Jetpack_Sitemap_Finder |
||
68 | * @since 4.8.0 |
||
69 | * @var Jetpack_Sitemap_Finder $finder Finder object for dealing with sitemap URIs. |
||
70 | */ |
||
71 | private $finder; |
||
72 | |||
73 | /** |
||
74 | * Construct a new Jetpack_Sitemap_Manager. |
||
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' ) && ( true === WP_DEBUG ) ) { |
||
84 | $this->logger = new Jetpack_Sitemap_Logger(); |
||
85 | } |
||
86 | |||
87 | // Add callback for sitemap URL handler. |
||
88 | add_action( |
||
89 | 'init', |
||
90 | array( $this, 'callback_action_catch_sitemap_urls' ), |
||
91 | defined( 'IS_WPCOM' ) && IS_WPCOM ? 100 : 10 |
||
92 | ); |
||
93 | |||
94 | // Add generator to wp_cron task list. |
||
95 | $this->schedule_sitemap_generation(); |
||
96 | |||
97 | // Add sitemap to robots.txt. |
||
98 | add_action( |
||
99 | 'do_robotstxt', |
||
100 | array( $this, 'callback_action_do_robotstxt' ), |
||
101 | 20 |
||
102 | ); |
||
103 | |||
104 | // The news sitemap is cached; here we add a callback to |
||
105 | // flush the cached news sitemap when a post is published. |
||
106 | add_action( |
||
107 | 'publish_post', |
||
108 | array( $this, 'callback_action_flush_news_sitemap_cache' ), |
||
109 | 10 |
||
110 | ); |
||
111 | |||
112 | // In case we need to purge all sitemaps, we do this. |
||
113 | add_action( |
||
114 | 'jetpack_sitemaps_purge_data', |
||
115 | array( $this, 'callback_action_purge_data' ) |
||
116 | ); |
||
117 | |||
118 | /* |
||
119 | * Module parameters are stored as options in the database. |
||
120 | * This allows us to avoid having to process all of init |
||
121 | * before serving the sitemap data. The following actions |
||
122 | * process and store these filters. |
||
123 | */ |
||
124 | |||
125 | // Process filters and store location string for sitemap. |
||
126 | add_action( |
||
127 | 'init', |
||
128 | array( $this, 'callback_action_filter_sitemap_location' ), |
||
129 | 999 |
||
130 | ); |
||
131 | |||
132 | return; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Echo a raw string of given content-type. |
||
137 | * |
||
138 | * @access private |
||
139 | * @since 4.8.0 |
||
140 | * |
||
141 | * @param string $the_content_type The content type to be served. |
||
142 | * @param string $the_content The string to be echoed. |
||
143 | */ |
||
144 | private function serve_raw_and_die( $the_content_type, $the_content ) { |
||
145 | header( 'Content-Type: ' . $the_content_type . '; charset=UTF-8' ); |
||
146 | |||
147 | global $wp_query; |
||
148 | $wp_query->is_feed = true; |
||
149 | set_query_var( 'feed', 'sitemap' ); |
||
150 | |||
151 | if ( '' === $the_content ) { |
||
152 | wp_die( |
||
153 | esc_html__( "No sitemap found. Maybe it's being generated. Please try again later.", 'jetpack' ), |
||
154 | esc_html__( 'Sitemaps', 'jetpack' ), |
||
155 | array( |
||
156 | 'response' => 404, |
||
157 | ) |
||
158 | ); |
||
159 | } |
||
160 | |||
161 | echo $the_content; |
||
162 | |||
163 | die(); |
||
|
|||
164 | } |
||
165 | |||
166 | /** |
||
167 | * Callback to intercept sitemap url requests and serve sitemap files. |
||
168 | * |
||
169 | * @access public |
||
170 | * @since 4.8.0 |
||
171 | */ |
||
172 | public function callback_action_catch_sitemap_urls() { |
||
173 | // Regular expressions for sitemap URL routing. |
||
174 | $regex = array( |
||
175 | 'master' => '/^sitemap\.xml$/', |
||
176 | 'sitemap' => '/^sitemap-[1-9][0-9]*\.xml$/', |
||
177 | 'index' => '/^sitemap-index-[1-9][0-9]*\.xml$/', |
||
178 | 'sitemap-style' => '/^sitemap\.xsl$/', |
||
179 | 'index-style' => '/^sitemap-index\.xsl$/', |
||
180 | 'image' => '/^image-sitemap-[1-9][0-9]*\.xml$/', |
||
181 | 'image-index' => '/^image-sitemap-index-[1-9][0-9]*\.xml$/', |
||
182 | 'image-style' => '/^image-sitemap\.xsl$/', |
||
183 | 'video' => '/^video-sitemap-[1-9][0-9]*\.xml$/', |
||
184 | 'video-index' => '/^video-sitemap-index-[1-9][0-9]*\.xml$/', |
||
185 | 'video-style' => '/^video-sitemap\.xsl$/', |
||
186 | 'news' => '/^news-sitemap\.xml$/', |
||
187 | 'news-style' => '/^news-sitemap\.xsl$/', |
||
188 | ); |
||
189 | |||
190 | // The raw path(+query) of the requested URI. |
||
191 | if ( isset( $_SERVER['REQUEST_URI'] ) ) { // WPCS: Input var okay. |
||
192 | $raw_uri = sanitize_text_field( |
||
193 | wp_unslash( $_SERVER['REQUEST_URI'] ) // WPCS: Input var okay. |
||
194 | ); |
||
195 | } else { |
||
196 | $raw_uri = ''; |
||
197 | } |
||
198 | |||
199 | $request = $this->finder->recognize_sitemap_uri( $raw_uri ); |
||
200 | |||
201 | if ( isset( $request['sitemap_name'] ) ) { |
||
202 | |||
203 | /** |
||
204 | * Filter the content type used to serve the sitemap XML files. |
||
205 | * |
||
206 | * @module sitemaps |
||
207 | * |
||
208 | * @since 3.9.0 |
||
209 | * |
||
210 | * @param string $xml_content_type By default, it's 'text/xml'. |
||
211 | */ |
||
212 | $xml_content_type = apply_filters( 'jetpack_sitemap_content_type', 'text/xml' ); |
||
213 | |||
214 | // Catch master sitemap xml. |
||
215 | if ( preg_match( $regex['master'], $request['sitemap_name'] ) ) { |
||
216 | $this->serve_raw_and_die( |
||
217 | $xml_content_type, |
||
218 | $this->librarian->get_sitemap_text( |
||
219 | jp_sitemap_filename( JP_MASTER_SITEMAP_TYPE, 0 ), |
||
220 | JP_MASTER_SITEMAP_TYPE |
||
221 | ) |
||
222 | ); |
||
223 | } |
||
224 | |||
225 | // Catch sitemap xml. |
||
226 | View Code Duplication | if ( preg_match( $regex['sitemap'], $request['sitemap_name'] ) ) { |
|
227 | $this->serve_raw_and_die( |
||
228 | $xml_content_type, |
||
229 | $this->librarian->get_sitemap_text( |
||
230 | $request['sitemap_name'], |
||
231 | JP_PAGE_SITEMAP_TYPE |
||
232 | ) |
||
233 | ); |
||
234 | } |
||
235 | |||
236 | // Catch sitemap index xml. |
||
237 | View Code Duplication | if ( preg_match( $regex['index'], $request['sitemap_name'] ) ) { |
|
238 | $this->serve_raw_and_die( |
||
239 | $xml_content_type, |
||
240 | $this->librarian->get_sitemap_text( |
||
241 | $request['sitemap_name'], |
||
242 | JP_PAGE_SITEMAP_INDEX_TYPE |
||
243 | ) |
||
244 | ); |
||
245 | } |
||
246 | |||
247 | // Catch sitemap xsl. |
||
248 | if ( preg_match( $regex['sitemap-style'], $request['sitemap_name'] ) ) { |
||
249 | $this->serve_raw_and_die( |
||
250 | 'application/xml', |
||
251 | Jetpack_Sitemap_Stylist::sitemap_xsl() |
||
252 | ); |
||
253 | } |
||
254 | |||
255 | // Catch sitemap index xsl. |
||
256 | if ( preg_match( $regex['index-style'], $request['sitemap_name'] ) ) { |
||
257 | $this->serve_raw_and_die( |
||
258 | 'application/xml', |
||
259 | Jetpack_Sitemap_Stylist::sitemap_index_xsl() |
||
260 | ); |
||
261 | } |
||
262 | |||
263 | // Catch image sitemap xml. |
||
264 | View Code Duplication | if ( preg_match( $regex['image'], $request['sitemap_name'] ) ) { |
|
265 | $this->serve_raw_and_die( |
||
266 | $xml_content_type, |
||
267 | $this->librarian->get_sitemap_text( |
||
268 | $request['sitemap_name'], |
||
269 | JP_IMAGE_SITEMAP_TYPE |
||
270 | ) |
||
271 | ); |
||
272 | } |
||
273 | |||
274 | // Catch image sitemap index xml. |
||
275 | View Code Duplication | if ( preg_match( $regex['image-index'], $request['sitemap_name'] ) ) { |
|
276 | $this->serve_raw_and_die( |
||
277 | $xml_content_type, |
||
278 | $this->librarian->get_sitemap_text( |
||
279 | $request['sitemap_name'], |
||
280 | JP_IMAGE_SITEMAP_INDEX_TYPE |
||
281 | ) |
||
282 | ); |
||
283 | } |
||
284 | |||
285 | // Catch image sitemap xsl. |
||
286 | if ( preg_match( $regex['image-style'], $request['sitemap_name'] ) ) { |
||
287 | $this->serve_raw_and_die( |
||
288 | 'application/xml', |
||
289 | Jetpack_Sitemap_Stylist::image_sitemap_xsl() |
||
290 | ); |
||
291 | } |
||
292 | |||
293 | // Catch video sitemap xml. |
||
294 | View Code Duplication | if ( preg_match( $regex['video'], $request['sitemap_name'] ) ) { |
|
295 | $this->serve_raw_and_die( |
||
296 | $xml_content_type, |
||
297 | $this->librarian->get_sitemap_text( |
||
298 | $request['sitemap_name'], |
||
299 | JP_VIDEO_SITEMAP_TYPE |
||
300 | ) |
||
301 | ); |
||
302 | } |
||
303 | |||
304 | // Catch video sitemap index xml. |
||
305 | View Code Duplication | if ( preg_match( $regex['video-index'], $request['sitemap_name'] ) ) { |
|
306 | $this->serve_raw_and_die( |
||
307 | $xml_content_type, |
||
308 | $this->librarian->get_sitemap_text( |
||
309 | $request['sitemap_name'], |
||
310 | JP_VIDEO_SITEMAP_INDEX_TYPE |
||
311 | ) |
||
312 | ); |
||
313 | } |
||
314 | |||
315 | // Catch video sitemap xsl. |
||
316 | if ( preg_match( $regex['video-style'], $request['sitemap_name'] ) ) { |
||
317 | $this->serve_raw_and_die( |
||
318 | 'application/xml', |
||
319 | Jetpack_Sitemap_Stylist::video_sitemap_xsl() |
||
320 | ); |
||
321 | } |
||
322 | |||
323 | // Catch news sitemap xml. |
||
324 | if ( preg_match( $regex['news'], $request['sitemap_name'] ) ) { |
||
325 | $sitemap_builder = new Jetpack_Sitemap_Builder(); |
||
326 | $this->serve_raw_and_die( |
||
327 | $xml_content_type, |
||
328 | $sitemap_builder->news_sitemap_xml() |
||
329 | ); |
||
330 | } |
||
331 | |||
332 | // Catch news sitemap xsl. |
||
333 | if ( preg_match( $regex['news-style'], $request['sitemap_name'] ) ) { |
||
334 | $this->serve_raw_and_die( |
||
335 | 'application/xml', |
||
336 | Jetpack_Sitemap_Stylist::news_sitemap_xsl() |
||
337 | ); |
||
338 | } |
||
339 | } |
||
340 | |||
341 | // URL did not match any sitemap patterns. |
||
342 | return; |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Callback for adding sitemap-interval to the list of schedules. |
||
347 | * |
||
348 | * @access public |
||
349 | * @since 4.8.0 |
||
350 | * |
||
351 | * @param array $schedules The array of WP_Cron schedules. |
||
352 | * |
||
353 | * @return array The updated array of WP_Cron schedules. |
||
354 | */ |
||
355 | public function callback_add_sitemap_schedule( $schedules ) { |
||
362 | |||
363 | /** |
||
364 | * Add actions to schedule sitemap generation. |
||
365 | * Should only be called once, in the constructor. |
||
366 | * |
||
367 | * @access private |
||
368 | * @since 4.8.0 |
||
369 | */ |
||
370 | private function schedule_sitemap_generation() { |
||
391 | |||
392 | /** |
||
393 | * Callback to add sitemap to robots.txt. |
||
394 | * |
||
395 | * @access public |
||
396 | * @since 4.8.0 |
||
397 | */ |
||
398 | public function callback_action_do_robotstxt() { |
||
432 | |||
433 | /** |
||
434 | * Callback to delete the news sitemap cache. |
||
435 | * |
||
436 | * @access public |
||
437 | * @since 4.8.0 |
||
438 | */ |
||
439 | public function callback_action_flush_news_sitemap_cache() { |
||
442 | |||
443 | /** |
||
444 | * Callback for resetting stored sitemap data. |
||
445 | * |
||
446 | * @access public |
||
447 | * @since 5.3.0 |
||
448 | */ |
||
449 | public function callback_action_purge_data() { |
||
453 | |||
454 | /** |
||
455 | * Callback to set the sitemap location. |
||
456 | * |
||
457 | * @access public |
||
458 | * @since 4.8.0 |
||
459 | */ |
||
460 | public function callback_action_filter_sitemap_location() { |
||
502 | |||
503 | } // End Jetpack_Sitemap_Manager class. |
||
504 | |||
544 |
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.