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!=, orswitchconditions), values of different types might be equal.For
integervalues, zero is a special case, in particular the following results might be unexpected: