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:
Complex classes like WPCom_Markdown often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WPCom_Markdown, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class WPCom_Markdown { |
||
35 | |||
36 | |||
37 | const POST_OPTION = 'wpcom_publish_posts_with_markdown'; |
||
38 | const COMMENT_OPTION = 'wpcom_publish_comments_with_markdown'; |
||
39 | const POST_TYPE_SUPPORT = 'wpcom-markdown'; |
||
40 | const IS_MD_META = '_wpcom_is_markdown'; |
||
41 | |||
42 | private static $parser; |
||
43 | private static $instance; |
||
44 | |||
45 | // to ensure that our munged posts over xml-rpc are removed from the cache |
||
46 | public $posts_to_uncache = array(); |
||
47 | private $monitoring = array( 'post' => array(), 'parent' => array() ); |
||
48 | |||
49 | |||
50 | /** |
||
51 | * Yay singletons! |
||
52 | * @return object WPCom_Markdown instance |
||
53 | */ |
||
54 | public static function get_instance() { |
||
59 | |||
60 | /** |
||
61 | * Kicks things off on `init` action |
||
62 | * @return null |
||
63 | */ |
||
64 | public function load() { |
||
76 | |||
77 | /** |
||
78 | * If we're in a bulk edit session, unload so that we don't lose our markdown metadata |
||
79 | * @return null |
||
80 | */ |
||
81 | public function maybe_unload_for_bulk_edit() { |
||
86 | |||
87 | /** |
||
88 | * Called on init and fires on switch_blog to decide if our actions and filters |
||
89 | * should be running. |
||
90 | * @param int|null $new_blog_id New blog ID |
||
91 | * @param int|null $old_blog_id Old blog ID |
||
92 | * @return null |
||
93 | */ |
||
94 | public function maybe_load_actions_and_filters( $new_blog_id = null, $old_blog_id = null ) { |
||
112 | |||
113 | /** |
||
114 | * Set up hooks for enabling Markdown conversion on posts |
||
115 | * @return null |
||
116 | */ |
||
117 | public function load_markdown_for_posts() { |
||
130 | |||
131 | /** |
||
132 | * Removes hooks to disable Markdown conversion on posts |
||
133 | * @return null |
||
134 | */ |
||
135 | public function unload_markdown_for_posts() { |
||
145 | |||
146 | /** |
||
147 | * Set up hooks for enabling Markdown conversion on comments |
||
148 | * @return null |
||
149 | */ |
||
150 | protected function load_markdown_for_comments() { |
||
155 | |||
156 | /** |
||
157 | * Removes hooks to disable Markdown conversion |
||
158 | * @return null |
||
159 | */ |
||
160 | protected function unload_markdown_for_comments() { |
||
163 | |||
164 | /** |
||
165 | * o2 does some of what we do. Let's take precedence. |
||
166 | * @return null |
||
167 | */ |
||
168 | public function add_o2_helpers() { |
||
179 | |||
180 | /** |
||
181 | * If Markdown is enabled for posts on this blog, filter the text for o2 previews |
||
182 | * @param string $text Post text |
||
183 | * @return string Post text transformed through the magic of Markdown |
||
184 | */ |
||
185 | public function o2_preview_post( $text ) { |
||
191 | |||
192 | /** |
||
193 | * If Markdown is enabled for comments on this blog, filter the text for o2 previews |
||
194 | * @param string $text Comment text |
||
195 | * @return string Comment text transformed through the magic of Markdown |
||
196 | */ |
||
197 | public function o2_preview_comment( $text ) { |
||
203 | |||
204 | /** |
||
205 | * Escapes lists so that o2 doesn't trounce them |
||
206 | * @param string $text Post/comment text |
||
207 | * @return string Text escaped with HTML entity for asterisk |
||
208 | */ |
||
209 | public function o2_escape_lists( $text ) { |
||
212 | |||
213 | /** |
||
214 | * Unescapes the token we inserted on o2_escape_lists |
||
215 | * @param string $text Post/comment text with HTML entities for asterisks |
||
216 | * @return string Text with the HTML entity removed |
||
217 | */ |
||
218 | public function o2_unescape_lists( $text ) { |
||
221 | |||
222 | /** |
||
223 | * Preserve code blocks from being munged by KSES before they have a chance |
||
224 | * @param string $text post content |
||
225 | * @return string post content with code blocks escaped |
||
226 | */ |
||
227 | public function preserve_code_blocks( $text ) { |
||
230 | |||
231 | /** |
||
232 | * Remove KSES if it's there. Store the result to manually invoke later if needed. |
||
233 | * @return null |
||
234 | */ |
||
235 | public function maybe_remove_kses() { |
||
240 | |||
241 | /** |
||
242 | * Add our Writing and Discussion settings. |
||
243 | * @return null |
||
244 | */ |
||
245 | public function register_setting() { |
||
251 | |||
252 | /** |
||
253 | * Sanitize setting. Don't really want to store "on" value, so we'll store "1" instead! |
||
254 | * @param string $input Value received by settings API via $_POST |
||
255 | * @return bool Cast to boolean. |
||
256 | */ |
||
257 | public function sanitize_setting( $input ) { |
||
260 | |||
261 | /** |
||
262 | * Prints HTML for the Writing setting |
||
263 | * @return null |
||
264 | */ |
||
265 | View Code Duplication | public function post_field() { |
|
275 | |||
276 | /** |
||
277 | * Prints HTML for the Discussion setting |
||
278 | * @return null |
||
279 | */ |
||
280 | View Code Duplication | public function comment_field() { |
|
290 | |||
291 | /** |
||
292 | * Get the support url for Markdown |
||
293 | * @uses apply_filters |
||
294 | * @return string support url |
||
295 | */ |
||
296 | protected function get_support_url() { |
||
308 | |||
309 | /** |
||
310 | * Is Mardown conversion for posts enabled? |
||
311 | * @return boolean |
||
312 | */ |
||
313 | public function is_posting_enabled() { |
||
316 | |||
317 | /** |
||
318 | * Is Markdown conversion for comments enabled? |
||
319 | * @return boolean |
||
320 | */ |
||
321 | public function is_commenting_enabled() { |
||
324 | |||
325 | /** |
||
326 | * Check if a $post_id has Markdown enabled |
||
327 | * @param int $post_id A post ID. |
||
328 | * @return boolean |
||
329 | */ |
||
330 | public function is_markdown( $post_id ) { |
||
333 | |||
334 | /** |
||
335 | * Set Markdown as enabled on a post_id. We skip over update_postmeta so we |
||
336 | * can sneakily set metadata on post revisions, which we need. |
||
337 | * @param int $post_id A post ID. |
||
338 | * @return bool The metadata was successfully set. |
||
339 | */ |
||
340 | protected function set_as_markdown( $post_id ) { |
||
343 | |||
344 | /** |
||
345 | * Get our Markdown parser object, optionally requiring all of our needed classes and |
||
346 | * instantiating our parser. |
||
347 | * @return object WPCom_GHF_Markdown_Parser instance. |
||
348 | */ |
||
349 | public function get_parser() { |
||
358 | |||
359 | /** |
||
360 | * We don't want Markdown conversion all over the place. |
||
361 | * @return null |
||
362 | */ |
||
363 | public function add_default_post_type_support() { |
||
368 | |||
369 | /** |
||
370 | * Figure out the post type of the post screen we're on |
||
371 | * @return string Current post_type |
||
372 | */ |
||
373 | protected function get_post_screen_post_type() { |
||
384 | |||
385 | /** |
||
386 | * Swap post_content and post_content_filtered for editing |
||
387 | * @param string $content Post content |
||
388 | * @param int $id post ID |
||
389 | * @return string Swapped content |
||
390 | */ |
||
391 | View Code Duplication | public function edit_post_content( $content, $id ) { |
|
401 | |||
402 | /** |
||
403 | * Swap post_content_filtered and post_content for editing |
||
404 | * @param string $content Post content_filtered |
||
405 | * @param int $id post ID |
||
406 | * @return string Swapped content |
||
407 | */ |
||
408 | View Code Duplication | public function edit_post_content_filtered( $content, $id ) { |
|
417 | |||
418 | /** |
||
419 | * Magic happens here. Markdown is converted and stored on post_content. Original Markdown is stored |
||
420 | * in post_content_filtered so that we can continue editing as Markdown. |
||
421 | * @param array $post_data The post data that will be inserted into the DB. Slashed. |
||
422 | * @param array $postarr All the stuff that was in $_POST. |
||
423 | * @return array $post_data with post_content and post_content_filtered modified |
||
424 | */ |
||
425 | public function wp_insert_post_data( $post_data, $postarr ) { |
||
475 | |||
476 | /** |
||
477 | * Calls on wp_insert_post action, after wp_insert_post_data. This way we can |
||
478 | * still set postmeta on our revisions after it's all been deleted. |
||
479 | * @param int $post_id The post ID that has just been added/updated |
||
480 | * @return null |
||
481 | */ |
||
482 | public function wp_insert_post( $post_id ) { |
||
497 | |||
498 | /** |
||
499 | * Run a comment through Markdown. Easy peasy. |
||
500 | * @param string $content |
||
501 | * @return string |
||
502 | */ |
||
503 | public function pre_comment_content( $content ) { |
||
508 | |||
509 | protected function comment_hash( $content ) { |
||
512 | |||
513 | /** |
||
514 | * Markdown conversion. Some DRYness for repetitive tasks. |
||
515 | * @param string $text Content to be run through Markdown |
||
516 | * @param array $args Arguments, with keys: |
||
517 | * id: provide a string to prefix footnotes with a unique identifier |
||
518 | * unslash: when true, expects and returns slashed data |
||
519 | * decode_code_blocks: when true, assume that text in fenced code blocks is already |
||
520 | * HTML encoded and should be decoded before being passed to Markdown, which does |
||
521 | * its own encoding. |
||
522 | * @return string Markdown-processed content |
||
523 | */ |
||
524 | public function transform( $text, $args = array() ) { |
||
581 | |||
582 | /** |
||
583 | * Shows Markdown in the Revisions screen, and ensures that post_content_filtered |
||
584 | * is maintained on revisions |
||
585 | * @param array $fields Post fields pertinent to revisions |
||
586 | * @return array Modified array to include post_content_filtered |
||
587 | */ |
||
588 | public function _wp_post_revision_fields( $fields ) { |
||
592 | |||
593 | /** |
||
594 | * Do some song and dance to keep all post_content and post_content_filtered content |
||
595 | * in the expected place when a post revision is restored. |
||
596 | * @param int $post_id The post ID have a restore done to it |
||
597 | * @param int $revision_id The revision ID being restored |
||
598 | * @return null |
||
599 | */ |
||
600 | public function wp_restore_post_revision( $post_id, $revision_id ) { |
||
614 | |||
615 | /** |
||
616 | * We need to ensure the last revision has Markdown, not HTML in its post_content_filtered |
||
617 | * column after a restore. |
||
618 | * @param int $post_id The post ID that was just restored. |
||
619 | * @return null |
||
620 | */ |
||
621 | protected function fix_latest_revision_on_restore( $post_id ) { |
||
628 | |||
629 | /** |
||
630 | * Kicks off magic for an XML-RPC session. We want to keep editing Markdown |
||
631 | * and publishing HTML. |
||
632 | * @param string $xmlrpc_method The current XML-RPC method |
||
633 | * @return null |
||
634 | */ |
||
635 | public function xmlrpc_actions( $xmlrpc_method ) { |
||
647 | |||
648 | /** |
||
649 | * metaWeblog.getPost and wp.getPage fire xmlrpc_call action *after* get_post() is called. |
||
650 | * So, we have to detect those methods and prime the post cache early. |
||
651 | * @return null |
||
652 | */ |
||
653 | protected function check_for_early_methods() { |
||
665 | |||
666 | /** |
||
667 | * Prime the post cache with swapped post_content. This is a sneaky way of getting around |
||
668 | * the fact that there are no good hooks to call on the *.getPost xmlrpc methods. |
||
669 | * |
||
670 | * @return null |
||
671 | */ |
||
672 | private function prime_post_cache( $post_id = false ) { |
||
693 | |||
694 | /** |
||
695 | * Swaps `post_content_filtered` back to `post_content` for editing purposes. |
||
696 | * @param object $post WP_Post object |
||
697 | * @return object WP_Post object with swapped `post_content_filtered` and `post_content` |
||
698 | */ |
||
699 | protected function swap_for_editing( $post ) { |
||
709 | |||
710 | |||
711 | /** |
||
712 | * We munge the post cache to serve proper markdown content to XML-RPC clients. |
||
713 | * Uncache these after the XML-RPC session ends. |
||
714 | * @return null |
||
715 | */ |
||
716 | public function uncache_munged_posts() { |
||
722 | |||
723 | /** |
||
724 | * Since *.(get)?[Rr]ecentPosts calls get_posts with suppress filters on, we need to |
||
725 | * turn them back on so that we can swap things for editing. |
||
726 | * @param object $wp_query WP_Query object |
||
727 | * @return null |
||
728 | */ |
||
729 | public function make_filterable( $wp_query ) { |
||
733 | |||
734 | /** |
||
735 | * Swaps post_content and post_content_filtered for editing. |
||
736 | * @param array $posts Posts returned by the just-completed query |
||
737 | * @param object $wp_query Current WP_Query object |
||
738 | * @return array Modified $posts |
||
739 | */ |
||
740 | public function the_posts( $posts, $wp_query ) { |
||
750 | |||
751 | /** |
||
752 | * Singleton silence is golden |
||
753 | */ |
||
754 | private function __construct() {} |
||
755 | } |
||
756 | |||
758 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: