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() { |
||
132 | |||
133 | /** |
||
134 | * Removes hooks to disable Markdown conversion on posts |
||
135 | * @return null |
||
136 | */ |
||
137 | public function unload_markdown_for_posts() { |
||
149 | |||
150 | /** |
||
151 | * Set up hooks for enabling Markdown conversion on comments |
||
152 | * @return null |
||
153 | */ |
||
154 | protected function load_markdown_for_comments() { |
||
159 | |||
160 | /** |
||
161 | * Removes hooks to disable Markdown conversion |
||
162 | * @return null |
||
163 | */ |
||
164 | protected function unload_markdown_for_comments() { |
||
167 | |||
168 | /** |
||
169 | * o2 does some of what we do. Let's take precedence. |
||
170 | * @return null |
||
171 | */ |
||
172 | public function add_o2_helpers() { |
||
183 | |||
184 | /** |
||
185 | * If Markdown is enabled for posts on this blog, filter the text for o2 previews |
||
186 | * @param string $text Post text |
||
187 | * @return string Post text transformed through the magic of Markdown |
||
188 | */ |
||
189 | public function o2_preview_post( $text ) { |
||
195 | |||
196 | /** |
||
197 | * If Markdown is enabled for comments on this blog, filter the text for o2 previews |
||
198 | * @param string $text Comment text |
||
199 | * @return string Comment text transformed through the magic of Markdown |
||
200 | */ |
||
201 | public function o2_preview_comment( $text ) { |
||
207 | |||
208 | /** |
||
209 | * Escapes lists so that o2 doesn't trounce them |
||
210 | * @param string $text Post/comment text |
||
211 | * @return string Text escaped with HTML entity for asterisk |
||
212 | */ |
||
213 | public function o2_escape_lists( $text ) { |
||
216 | |||
217 | /** |
||
218 | * Unescapes the token we inserted on o2_escape_lists |
||
219 | * @param string $text Post/comment text with HTML entities for asterisks |
||
220 | * @return string Text with the HTML entity removed |
||
221 | */ |
||
222 | public function o2_unescape_lists( $text ) { |
||
225 | |||
226 | /** |
||
227 | * Preserve code blocks from being munged by KSES before they have a chance |
||
228 | * @param string $text post content |
||
229 | * @return string post content with code blocks escaped |
||
230 | */ |
||
231 | public function preserve_code_blocks( $text ) { |
||
234 | |||
235 | /** |
||
236 | * Remove KSES if it's there. Store the result to manually invoke later if needed. |
||
237 | * @return null |
||
238 | */ |
||
239 | public function maybe_remove_kses() { |
||
244 | |||
245 | /** |
||
246 | * Add our Writing and Discussion settings. |
||
247 | * @return null |
||
248 | */ |
||
249 | public function register_setting() { |
||
255 | |||
256 | /** |
||
257 | * Sanitize setting. Don't really want to store "on" value, so we'll store "1" instead! |
||
258 | * @param string $input Value received by settings API via $_POST |
||
259 | * @return bool Cast to boolean. |
||
260 | */ |
||
261 | public function sanitize_setting( $input ) { |
||
264 | |||
265 | /** |
||
266 | * Prints HTML for the Writing setting |
||
267 | * @return null |
||
268 | */ |
||
269 | View Code Duplication | public function post_field() { |
|
279 | |||
280 | /** |
||
281 | * Prints HTML for the Discussion setting |
||
282 | * @return null |
||
283 | */ |
||
284 | View Code Duplication | public function comment_field() { |
|
294 | |||
295 | /** |
||
296 | * Get the support url for Markdown |
||
297 | * @uses apply_filters |
||
298 | * @return string support url |
||
299 | */ |
||
300 | protected function get_support_url() { |
||
312 | |||
313 | /** |
||
314 | * Is Mardown conversion for posts enabled? |
||
315 | * @return boolean |
||
316 | */ |
||
317 | public function is_posting_enabled() { |
||
320 | |||
321 | /** |
||
322 | * Is Markdown conversion for comments enabled? |
||
323 | * @return boolean |
||
324 | */ |
||
325 | public function is_commenting_enabled() { |
||
328 | |||
329 | /** |
||
330 | * Check if a $post_id has Markdown enabled |
||
331 | * @param int $post_id A post ID. |
||
332 | * @return boolean |
||
333 | */ |
||
334 | public function is_markdown( $post_id ) { |
||
337 | |||
338 | /** |
||
339 | * Set Markdown as enabled on a post_id. We skip over update_postmeta so we |
||
340 | * can sneakily set metadata on post revisions, which we need. |
||
341 | * @param int $post_id A post ID. |
||
342 | * @return bool The metadata was successfully set. |
||
343 | */ |
||
344 | protected function set_as_markdown( $post_id ) { |
||
347 | |||
348 | /** |
||
349 | * Get our Markdown parser object, optionally requiring all of our needed classes and |
||
350 | * instantiating our parser. |
||
351 | * @return object WPCom_GHF_Markdown_Parser instance. |
||
352 | */ |
||
353 | public function get_parser() { |
||
362 | |||
363 | /** |
||
364 | * We don't want Markdown conversion all over the place. |
||
365 | * @return null |
||
366 | */ |
||
367 | public function add_default_post_type_support() { |
||
372 | |||
373 | /** |
||
374 | * Figure out the post type of the post screen we're on |
||
375 | * @return string Current post_type |
||
376 | */ |
||
377 | protected function get_post_screen_post_type() { |
||
388 | |||
389 | /** |
||
390 | * Swap post_content and post_content_filtered for editing |
||
391 | * @param string $content Post content |
||
392 | * @param int $id post ID |
||
393 | * @return string Swapped content |
||
394 | */ |
||
395 | View Code Duplication | public function edit_post_content( $content, $id ) { |
|
405 | |||
406 | /** |
||
407 | * Swap post_content_filtered and post_content for editing |
||
408 | * @param string $content Post content_filtered |
||
409 | * @param int $id post ID |
||
410 | * @return string Swapped content |
||
411 | */ |
||
412 | View Code Duplication | public function edit_post_content_filtered( $content, $id ) { |
|
421 | |||
422 | /** |
||
423 | * Some tags are allowed to have a 'markdown' attribute, allowing them to contain Markdown. |
||
424 | * We need to tell KSES about those tags. |
||
425 | * @param array $tags List of tags that KSES allows. |
||
426 | * @param string $context The context that KSES is allowing these tags. |
||
427 | * @return array The tags that KSES allows, with our extra 'markdown' parameter where necessary. |
||
428 | */ |
||
429 | public function wp_kses_allowed_html( $tags, $context ) { |
||
444 | |||
445 | /** |
||
446 | * TinyMCE needs to know not to strip the 'markdown' attribute. Unfortunately, it doesn't |
||
447 | * really offer a nice API for whitelisting attributes, so we have to manually add it |
||
448 | * to the schema instead. |
||
449 | */ |
||
450 | public function after_wp_tiny_mce() { |
||
467 | |||
468 | /** |
||
469 | * Magic happens here. Markdown is converted and stored on post_content. Original Markdown is stored |
||
470 | * in post_content_filtered so that we can continue editing as Markdown. |
||
471 | * @param array $post_data The post data that will be inserted into the DB. Slashed. |
||
472 | * @param array $postarr All the stuff that was in $_POST. |
||
473 | * @return array $post_data with post_content and post_content_filtered modified |
||
474 | */ |
||
475 | public function wp_insert_post_data( $post_data, $postarr ) { |
||
525 | |||
526 | /** |
||
527 | * Calls on wp_insert_post action, after wp_insert_post_data. This way we can |
||
528 | * still set postmeta on our revisions after it's all been deleted. |
||
529 | * @param int $post_id The post ID that has just been added/updated |
||
530 | * @return null |
||
531 | */ |
||
532 | public function wp_insert_post( $post_id ) { |
||
547 | |||
548 | /** |
||
549 | * Run a comment through Markdown. Easy peasy. |
||
550 | * @param string $content |
||
551 | * @return string |
||
552 | */ |
||
553 | public function pre_comment_content( $content ) { |
||
558 | |||
559 | protected function comment_hash( $content ) { |
||
562 | |||
563 | /** |
||
564 | * Markdown conversion. Some DRYness for repetitive tasks. |
||
565 | * @param string $text Content to be run through Markdown |
||
566 | * @param array $args Arguments, with keys: |
||
567 | * id: provide a string to prefix footnotes with a unique identifier |
||
568 | * unslash: when true, expects and returns slashed data |
||
569 | * decode_code_blocks: when true, assume that text in fenced code blocks is already |
||
570 | * HTML encoded and should be decoded before being passed to Markdown, which does |
||
571 | * its own encoding. |
||
572 | * @return string Markdown-processed content |
||
573 | */ |
||
574 | public function transform( $text, $args = array() ) { |
||
631 | |||
632 | /** |
||
633 | * Shows Markdown in the Revisions screen, and ensures that post_content_filtered |
||
634 | * is maintained on revisions |
||
635 | * @param array $fields Post fields pertinent to revisions |
||
636 | * @return array Modified array to include post_content_filtered |
||
637 | */ |
||
638 | public function _wp_post_revision_fields( $fields ) { |
||
642 | |||
643 | /** |
||
644 | * Do some song and dance to keep all post_content and post_content_filtered content |
||
645 | * in the expected place when a post revision is restored. |
||
646 | * @param int $post_id The post ID have a restore done to it |
||
647 | * @param int $revision_id The revision ID being restored |
||
648 | * @return null |
||
649 | */ |
||
650 | public function wp_restore_post_revision( $post_id, $revision_id ) { |
||
664 | |||
665 | /** |
||
666 | * We need to ensure the last revision has Markdown, not HTML in its post_content_filtered |
||
667 | * column after a restore. |
||
668 | * @param int $post_id The post ID that was just restored. |
||
669 | * @return null |
||
670 | */ |
||
671 | protected function fix_latest_revision_on_restore( $post_id ) { |
||
678 | |||
679 | /** |
||
680 | * Kicks off magic for an XML-RPC session. We want to keep editing Markdown |
||
681 | * and publishing HTML. |
||
682 | * @param string $xmlrpc_method The current XML-RPC method |
||
683 | * @return null |
||
684 | */ |
||
685 | public function xmlrpc_actions( $xmlrpc_method ) { |
||
697 | |||
698 | /** |
||
699 | * metaWeblog.getPost and wp.getPage fire xmlrpc_call action *after* get_post() is called. |
||
700 | * So, we have to detect those methods and prime the post cache early. |
||
701 | * @return null |
||
702 | */ |
||
703 | protected function check_for_early_methods() { |
||
715 | |||
716 | /** |
||
717 | * Prime the post cache with swapped post_content. This is a sneaky way of getting around |
||
718 | * the fact that there are no good hooks to call on the *.getPost xmlrpc methods. |
||
719 | * |
||
720 | * @return null |
||
721 | */ |
||
722 | private function prime_post_cache( $post_id = false ) { |
||
743 | |||
744 | /** |
||
745 | * Swaps `post_content_filtered` back to `post_content` for editing purposes. |
||
746 | * @param object $post WP_Post object |
||
747 | * @return object WP_Post object with swapped `post_content_filtered` and `post_content` |
||
748 | */ |
||
749 | protected function swap_for_editing( $post ) { |
||
759 | |||
760 | |||
761 | /** |
||
762 | * We munge the post cache to serve proper markdown content to XML-RPC clients. |
||
763 | * Uncache these after the XML-RPC session ends. |
||
764 | * @return null |
||
765 | */ |
||
766 | public function uncache_munged_posts() { |
||
772 | |||
773 | /** |
||
774 | * Since *.(get)?[Rr]ecentPosts calls get_posts with suppress filters on, we need to |
||
775 | * turn them back on so that we can swap things for editing. |
||
776 | * @param object $wp_query WP_Query object |
||
777 | * @return null |
||
778 | */ |
||
779 | public function make_filterable( $wp_query ) { |
||
783 | |||
784 | /** |
||
785 | * Swaps post_content and post_content_filtered for editing. |
||
786 | * @param array $posts Posts returned by the just-completed query |
||
787 | * @param object $wp_query Current WP_Query object |
||
788 | * @return array Modified $posts |
||
789 | */ |
||
790 | public function the_posts( $posts, $wp_query ) { |
||
800 | |||
801 | /** |
||
802 | * Singleton silence is golden |
||
803 | */ |
||
804 | private function __construct() {} |
||
805 | } |
||
806 | |||
808 |
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: