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() { |
||
| 465 | |||
| 466 | /** |
||
| 467 | * Magic happens here. Markdown is converted and stored on post_content. Original Markdown is stored |
||
| 468 | * in post_content_filtered so that we can continue editing as Markdown. |
||
| 469 | * @param array $post_data The post data that will be inserted into the DB. Slashed. |
||
| 470 | * @param array $postarr All the stuff that was in $_POST. |
||
| 471 | * @return array $post_data with post_content and post_content_filtered modified |
||
| 472 | */ |
||
| 473 | public function wp_insert_post_data( $post_data, $postarr ) { |
||
| 523 | |||
| 524 | /** |
||
| 525 | * Calls on wp_insert_post action, after wp_insert_post_data. This way we can |
||
| 526 | * still set postmeta on our revisions after it's all been deleted. |
||
| 527 | * @param int $post_id The post ID that has just been added/updated |
||
| 528 | * @return null |
||
| 529 | */ |
||
| 530 | public function wp_insert_post( $post_id ) { |
||
| 545 | |||
| 546 | /** |
||
| 547 | * Run a comment through Markdown. Easy peasy. |
||
| 548 | * @param string $content |
||
| 549 | * @return string |
||
| 550 | */ |
||
| 551 | public function pre_comment_content( $content ) { |
||
| 556 | |||
| 557 | protected function comment_hash( $content ) { |
||
| 560 | |||
| 561 | /** |
||
| 562 | * Markdown conversion. Some DRYness for repetitive tasks. |
||
| 563 | * @param string $text Content to be run through Markdown |
||
| 564 | * @param array $args Arguments, with keys: |
||
| 565 | * id: provide a string to prefix footnotes with a unique identifier |
||
| 566 | * unslash: when true, expects and returns slashed data |
||
| 567 | * decode_code_blocks: when true, assume that text in fenced code blocks is already |
||
| 568 | * HTML encoded and should be decoded before being passed to Markdown, which does |
||
| 569 | * its own encoding. |
||
| 570 | * @return string Markdown-processed content |
||
| 571 | */ |
||
| 572 | public function transform( $text, $args = array() ) { |
||
| 629 | |||
| 630 | /** |
||
| 631 | * Shows Markdown in the Revisions screen, and ensures that post_content_filtered |
||
| 632 | * is maintained on revisions |
||
| 633 | * @param array $fields Post fields pertinent to revisions |
||
| 634 | * @return array Modified array to include post_content_filtered |
||
| 635 | */ |
||
| 636 | public function _wp_post_revision_fields( $fields ) { |
||
| 640 | |||
| 641 | /** |
||
| 642 | * Do some song and dance to keep all post_content and post_content_filtered content |
||
| 643 | * in the expected place when a post revision is restored. |
||
| 644 | * @param int $post_id The post ID have a restore done to it |
||
| 645 | * @param int $revision_id The revision ID being restored |
||
| 646 | * @return null |
||
| 647 | */ |
||
| 648 | public function wp_restore_post_revision( $post_id, $revision_id ) { |
||
| 662 | |||
| 663 | /** |
||
| 664 | * We need to ensure the last revision has Markdown, not HTML in its post_content_filtered |
||
| 665 | * column after a restore. |
||
| 666 | * @param int $post_id The post ID that was just restored. |
||
| 667 | * @return null |
||
| 668 | */ |
||
| 669 | protected function fix_latest_revision_on_restore( $post_id ) { |
||
| 676 | |||
| 677 | /** |
||
| 678 | * Kicks off magic for an XML-RPC session. We want to keep editing Markdown |
||
| 679 | * and publishing HTML. |
||
| 680 | * @param string $xmlrpc_method The current XML-RPC method |
||
| 681 | * @return null |
||
| 682 | */ |
||
| 683 | public function xmlrpc_actions( $xmlrpc_method ) { |
||
| 695 | |||
| 696 | /** |
||
| 697 | * metaWeblog.getPost and wp.getPage fire xmlrpc_call action *after* get_post() is called. |
||
| 698 | * So, we have to detect those methods and prime the post cache early. |
||
| 699 | * @return null |
||
| 700 | */ |
||
| 701 | protected function check_for_early_methods() { |
||
| 713 | |||
| 714 | /** |
||
| 715 | * Prime the post cache with swapped post_content. This is a sneaky way of getting around |
||
| 716 | * the fact that there are no good hooks to call on the *.getPost xmlrpc methods. |
||
| 717 | * |
||
| 718 | * @return null |
||
| 719 | */ |
||
| 720 | private function prime_post_cache( $post_id = false ) { |
||
| 741 | |||
| 742 | /** |
||
| 743 | * Swaps `post_content_filtered` back to `post_content` for editing purposes. |
||
| 744 | * @param object $post WP_Post object |
||
| 745 | * @return object WP_Post object with swapped `post_content_filtered` and `post_content` |
||
| 746 | */ |
||
| 747 | protected function swap_for_editing( $post ) { |
||
| 757 | |||
| 758 | |||
| 759 | /** |
||
| 760 | * We munge the post cache to serve proper markdown content to XML-RPC clients. |
||
| 761 | * Uncache these after the XML-RPC session ends. |
||
| 762 | * @return null |
||
| 763 | */ |
||
| 764 | public function uncache_munged_posts() { |
||
| 770 | |||
| 771 | /** |
||
| 772 | * Since *.(get)?[Rr]ecentPosts calls get_posts with suppress filters on, we need to |
||
| 773 | * turn them back on so that we can swap things for editing. |
||
| 774 | * @param object $wp_query WP_Query object |
||
| 775 | * @return null |
||
| 776 | */ |
||
| 777 | public function make_filterable( $wp_query ) { |
||
| 781 | |||
| 782 | /** |
||
| 783 | * Swaps post_content and post_content_filtered for editing. |
||
| 784 | * @param array $posts Posts returned by the just-completed query |
||
| 785 | * @param object $wp_query Current WP_Query object |
||
| 786 | * @return array Modified $posts |
||
| 787 | */ |
||
| 788 | public function the_posts( $posts, $wp_query ) { |
||
| 798 | |||
| 799 | /** |
||
| 800 | * Singleton silence is golden |
||
| 801 | */ |
||
| 802 | private function __construct() {} |
||
| 803 | } |
||
| 804 | |||
| 806 |
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: