Slide 1

Slide 1 text

Best Practices for WordPress Plugin Development Tutorial @ ZendCon2015 Juliette Reinders Folmer / @jrf_nl

Slide 2

Slide 2 text

#zendcon

Slide 3

Slide 3 text

Who Are You ?

Slide 4

Slide 4 text

#zendcon Who Am I ? Self-employed, Independent Consultant Creator PHPCheatsheets phpcheatsheets.com Frequent Contributor to Open Source Projects

Slide 5

Slide 5 text

#zendcon Agenda Introducing Best Practices Applying Best Practices Closing

Slide 6

Slide 6 text

#zendcon Agenda Introducing Best Practices

Slide 7

Slide 7 text

WordPress Basics

Slide 8

Slide 8 text

#zendcon Anatomy of WordPress

Slide 9

Slide 9 text

#zendcon Page footer Admin Bar Page content (loop) Page header (Invisible) HTML Sidebar containing widgets (Main) menu

Slide 10

Slide 10 text

#zendcon Admin Bar (Invisible) HTML Admin footer Admin Menu 1. Post types 2. Custom- izations 3. Extras Admin page (with dashboard widgets)

Slide 11

Slide 11 text

#zendcon Anatomy of WordPress Functionality  Core  Plugins  Themes  Languages  Js Libraries Content  Post Types  Taxonomies  Widgets  Users  Meta data/custom fields  Options  Shortcodes  OEmbeds

Slide 12

Slide 12 text

#zendcon Hooks

Slide 13

Slide 13 text

#zendcon Hooks See:  WordPress Codex & Developer Reference  Hooks database: http://adambrown.info/p/wp_hooks  Debug Bar – Action & filter hooks plugin

Slide 14

Slide 14 text

#zendcon Hooking into WP apply_filter( 'hook_name', 'function_name', $priority = 10, $accepted_args = 1 ); add_action( 'hook_name', 'function_name', $priority = 10, $accepted_args = 1 ); add_action( 'hook_name', array( $this, 'method_name' ), $priority = 10, $accepted_args = 1 ); add_action( 'hook_name', array( __CLASS__, 'static_method_name' ), $priority = 10, $accepted_args = 1 );

Slide 15

Slide 15 text

#zendcon Action Hooks Front-end muplugins _loaded plugins _loaded setup _theme set_current _user after_setup _theme init wp_loaded parse _request posts _selection wp wp_head the_post wp_meta wp_footer admin_bar_ menu

Slide 16

Slide 16 text

#zendcon Action Hooks Back-end muplugins _loaded plugins _loaded setup _theme set_current _user after_setup _theme init wp_loaded admin _menu admin_init current _screen load- {page} posts_select ion wp admin_head admin_bar _menu admin _notices the_post admin _footer

Slide 17

Slide 17 text

#zendcon Action Hooks Back-end muplugins _loaded plugins _loaded setup _theme set_current _user after_setup _theme init wp_loaded admin _menu admin_init current _screen load- {page} posts_select ion wp admin_head admin_bar _menu admin _notices the_post admin _footer

Slide 18

Slide 18 text

#zendcon The Loop

Slide 19

Slide 19 text

#zendcon The Loop

Slide 20

Slide 20 text

#zendcon Putting the Pieces Together

Slide 21

Slide 21 text

#zendcon Putting the Pieces Together wp-config.php Must-use plugins Plugins • [MS] Network-activated plugins • Site-activated plugins Theme • Child-theme functions.php • Parent-theme functions.php

Slide 22

Slide 22 text

Doing It Wrong ™

Slide 23

Slide 23 text

#zendcon Doing It Wrong function wrap_content( $content ) { echo '
' . $content . '
'; } add_filter( 'the_content', 'wrap_content' );

Slide 24

Slide 24 text

#zendcon Doing It Right function my_prefix_wrap_content( $content ) { return '
' . $content . '
'; } add_filter( 'the_content', 'my_prefix_wrap_content' );

Slide 25

Slide 25 text

#zendcon Doing It Wrong function prefix_add_hooks() { add_action( 'init', 'prefix_my_init' ); add_action( 'admin_init', 'prefix_my_admin_init' ); } add_action( 'wp', 'prefix_add_hooks' );

Slide 26

Slide 26 text

#zendcon Doing It Right function prefix_add_hooks() { add_action( 'init', 'prefix_my_init' ); add_action( 'admin_init', 'prefix_my_admin_init' ); } add_action( 'plugins_loaded', 'prefix_add_hooks' );

Slide 27

Slide 27 text

#zendcon Doing It Wrong function unique_prefix_content( $content ) { return $content . '

Copyright Company 2010-' . date( 'Y' ) . '.

'; } add_filter( 'the_content', 'unique_prefix_content' );

Slide 28

Slide 28 text

#zendcon Doing It Right function unique_prefix_content( $content ) { return $content . '

' . sprintf( /* Translators: 1: Company Name, 2: Year. */ esc_html__( 'Copyright %1$s %2$s.', 'text-domain' ), 'Company', date_i18n( 'Y', get_the_date( 'U' ) ) ) . '

'; } add_filter( 'the_content', 'unique_prefix_content' );

Slide 29

Slide 29 text

#zendcon Doing It Wrong function unique_prefix_title( $title, $id ) { $post = get_post( $id ); if ( $post->post_type == 'my_cpt' ) { $title = sprintf( esc_html__( '%s (My Custom Type)', 'text-domain' ), $title ); } return $title; } add_filter( 'the_title', 'unique_prefix_title' );

Slide 30

Slide 30 text

#zendcon Doing It Right function unique_prefix_title( $title, $id ) { $post = get_post( $id ); $type = $post->post_type; if ( ! empty( $type ) && 'my_cpt' === $type ) { $title = sprintf( esc_html__( '%s (My Custom Type)', 'text-domain' ), $title ); } return $title; } add_filter( 'the_title', 'unique_prefix_title', 10, 2 );

Slide 31

Slide 31 text

#zendcon Doing It Wrong public function add_settings_link( $links, $file ) { if ( plugin_basename( __FILE__ ) === $file ) { $mylinks = array( 'Settings', ); $links = array_merge( $links, $mylinks ); } return $links; } add_filter( 'plugin_action_links', array( $this, 'add_settings_link' ), 10, 2 );

Slide 32

Slide 32 text

#zendcon Doing It Right public function add_settings_link( $links ) { if ( current_user_can( 'manage_options' ) ) { $links[] = sprintf( '%3$s', esc_url( $this->settings_url ), esc_attr__( 'Plugin-name Settings', 'text-d..n'), esc_html__( 'Settings', 'text-domain' ) ); } return $links; } add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'add_settings_link' ) );

Slide 33

Slide 33 text

#zendcon Doing It Wrong function my_prefix_scripts() { echo ' '; } add_action( 'wp_head', 'my_prefix_scripts' );

Slide 34

Slide 34 text

#zendcon Doing It Right function my_prefix_scripts() { $suffix = ( ( defined( 'SCRIPT_DEBUG' ) && true === SCRIPT_DEBUG ) ? '' : '.min' ); wp_enqueue_script( 'my-plugin-js', // ID. plugins_url( 'js/my-script' . $suffix . '.js', __FILE__ ), // URL. array( 'jquery' ), // Dependants. PLUGIN_VERSION, // Version. true // Load in footer ? ); } add_action( 'wp_enqueue_scripts', 'my_prefix_scripts' );

Slide 35

Slide 35 text

#zendcon Doing It Wrong public function filter_featured_image( $value, $object_id, $meta_key, $single ) { if ( $meta_key === '_thumbnail_id' && is_home() ) { $field = 'frontpageimage'; $meta_value = get_post_meta( $object_id, $field, $single ); $value = (int) $meta_value; } return $value; } add_filter( 'get_post_metadata', array( $this, 'filter_featured_image' ), 10, 4 );

Slide 36

Slide 36 text

#zendcon Doing It Right public function filter_featured_image( $value, $object_id, $meta_key ) { if ( $meta_key === '_thumbnail_id' && is_home() ) { $field = 'frontpageimage'; remove_filter( 'get_post_metadata', array( $this, 'filter_featured_image' ), 10 ); $meta_value = get_post_meta( $object_id, $field, true ); add_filter( 'get_post_metadata', array( $this, 'filter_featured_image' ), 10, 3 ); if ( is_numeric( $meta_value ) && 0 < (int) $meta_value ) { $value = (int) $meta_value; } } return $value; } add_filter( 'get_post_metadata', array( $this, 'filter_featured_image' ), 10, 3 );

Slide 37

Slide 37 text

#zendcon Doing It Wrong function schedule_my_cron_job() { wp_schedule_event( time(), 'hourly', 'my_cron_function‘ ); } add_action( 'init', 'schedule_my_cron_job' );

Slide 38

Slide 38 text

#zendcon Doing It Right function schedule_my_cron_job() { if ( ! wp_next_scheduled( 'my_cron_function' ) ) { wp_schedule_event( time(), 'hourly', 'my_cron_function' ); } } register_activation_hook( __FILE__ , 'schedule_my_cron_job' );

Slide 39

Slide 39 text

Best Practices

Slide 40

Slide 40 text

#zendcon Know Your Hooks

Slide 41

Slide 41 text

#zendcon Know Your Hooks Hook Order Actions vs Filters Parameters Priority

Slide 42

Slide 42 text

#zendcon Don’t Reinvent the Wheel

Slide 43

Slide 43 text

#zendcon Don’t Reinvent the Wheel Dashboard Widgets API Database API HTTP API File Header API Filesystem API Heartbeat API Metadata API Options API Plugin API Quicktags API REST API * Rewrite API Settings API Shortcode API Theme modification API Theme customization API Transients API Widgets API XML-RPC WordPress API * Expected in WP 4.4

Slide 44

Slide 44 text

#zendcon Use WP Functions PHP  mysqli_...()  file_put_contents()  json_encode()  mail()  unserialize()  htmlspecialchars()  add/stripslashes()  strtotime() / date()  http_build_query()  $_SERVER['HTTPS'] ... WP  $wpdb->....()  $wp_filesystem->put_contents()  wp_json_encode()  wp_mail()  maybe_unserialize()  esc_html()  wp_unslash()  mysql2date() / current_time()  add_query_arg()  is_ssl() ... (I mean it!)

Slide 45

Slide 45 text

#zendcon Avoid Conflict

Slide 46

Slide 46 text

#zendcon Avoid Conflict function_exists() class_exists() Jquery no conflicts mode Use bundled libraries

Slide 47

Slide 47 text

#zendcon Libraries shipped with WP json2 /wp-includes/js/json2.js underscore /wp-includes/js/underscore.js backbone /wp-includes/js/backbone.js prototype // via googleapis.com scriptaculous-root // via googleapis.com scriptaculous-builder // via googleapis.com scriptaculous-dragdrop // via googleapis.com scriptaculous-effects // via googleapis.com scriptaculous-slider // via googleapis.com scriptaculous-sound // via googleapis.com scriptaculous-controls // via googleapis.com cropper /wp-includes/js/crop/cropper.js swfobject /wp-includes/js/swfobject.js plupload /wp-includes/js/plupload/plupload.full.min.js plupload-handlers /wp-includes/js/plupload/handlers.js wp-plupload /wp-includes/js/plupload/wp-plupload.js swfupload /wp-includes/js/swfupload/swfupload.js swfupload-swfobject /wp-includes/js/swfupload/plugins/swfupload.swfobject.js swfupload-queue /wp-includes/js/swfupload/plugins/swfupload.queue.js swfupload-speed /wp-includes/js/swfupload/plugins/swfupload.speed.js swfupload-handlers /wp-includes/js/swfupload/handlers.js comment-reply /wp-includes/js/comment-reply.js quicktags /wp-includes/js/quicktags.js colorpicker /wp-includes/js/colorpicker.js editor /wp-admin/js/editor.js wp-ajax-response /wp-includes/js/wp-ajax-response.js wp-util /wp-includes/js/wp-util.js wp-backbone /wp-includes/js/wp-backbone.js revisions /wp-admin/js/revisions.js imgareaselect /wp-includes/js/imgareaselect/jquery.imgareaselect.js mediaelement /wp-includes/js/mediaelement/mediaelement-....js wp-mediaelement /wp-includes/js/mediaelement/wp-mediaelement.js froogaloop /wp-includes/js/mediaelement/froogaloop.min.js wp-playlist /wp-includes/js/mediaelement/wp-playlist.js zxcvbn-async /wp-includes/js/zxcvbn-async.js password-strength-meter /wp-admin/js/password-strength-meter.js user-profile /wp-admin/js/user-profile.js language-chooser /wp-admin/js/language-chooser.js user-suggest /wp-admin/js/user-suggest.js admin-bar /wp-includes/js/admin-bar.js Wplink /wp-includes/js/wplink.js wpdialogs /wp-includes/js/wpdialog.js word-count /wp-admin/js/word-count.js jquery-core /wp-includes/js/jquery/jquery.js jquery-migrate /wp-includes/js/jquery/jquery-migrate.js jquery-ui-core /wp-includes/js/jquery/ui/core.js jquery-effects-core /wp-includes/js/jquery/ui/effect.js jquery-effects-blind /wp-includes/js/jquery/ui/effect-blind.js jquery-effects-bounce /wp-includes/js/jquery/ui/effect-bounce.js jquery-effects-clip /wp-includes/js/jquery/ui/effect-clip.js jquery-effects-drop /wp-includes/js/jquery/ui/effect-drop.js jquery-effects-explode /wp-includes/js/jquery/ui/effect-explode.js jquery-effects-fade /wp-includes/js/jquery/ui/effect-fade.js jquery-effects-fold /wp-includes/js/jquery/ui/effect-fold.js jquery-effects-highlight /wp-includes/js/jquery/ui/effect-highlight.js jquery-effects-puff /wp-includes/js/jquery/ui/effect-puff.js jquery-effects-pulsate /wp-includes/js/jquery/ui/effect-pulsate.js jquery-effects-scale /wp-includes/js/jquery/ui/effect-scale.js jquery-effects-shake /wp-includes/js/jquery/ui/effect-shake.js jquery-effects-size /wp-includes/js/jquery/ui/effect-size.js jquery-effects-slide /wp-includes/js/jquery/ui/effect-slide.js jquery-effects-transfer /wp-includes/js/jquery/ui/effect-transfer.js jquery-ui-accordion /wp-includes/js/jquery/ui/accordion.js jquery-ui-autocomplete /wp-includes/js/jquery/ui/autocomplete.js jquery-ui-button /wp-includes/js/jquery/ui/button.js jquery-ui-datepicker /wp-includes/js/jquery/ui/datepicker.js jquery-ui-dialog /wp-includes/js/jquery/ui/dialog.js jquery-ui-draggable /wp-includes/js/jquery/ui/draggable.js jquery-ui-droppable /wp-includes/js/jquery/ui/droppable.js jquery-ui-menu /wp-includes/js/jquery/ui/menu.js jquery-ui-mouse /wp-includes/js/jquery/ui/mouse.js jquery-ui-position /wp-includes/js/jquery/ui/position.js jquery-ui-progressbar /wp-includes/js/jquery/ui/progressbar.js jquery-ui-resizable /wp-includes/js/jquery/ui/resizable.js jquery-ui-selectable /wp-includes/js/jquery/ui/selectable.js jquery-ui-selectmenu /wp-includes/js/jquery/ui/selectmenu.js jquery-ui-slider /wp-includes/js/jquery/ui/slider.js jquery-ui-sortable /wp-includes/js/jquery/ui/sortable.js jquery-ui-spinner /wp-includes/js/jquery/ui/spinner.js jquery-ui-tabs /wp-includes/js/jquery/ui/tabs.js jquery-ui-tooltip /wp-includes/js/jquery/ui/tooltip.js jquery-ui-widget /wp-includes/js/jquery/ui/widget.js jquery-form /wp-includes/js/jquery/jquery.form.js jquery-color /wp-includes/js/jquery/jquery.color.min.js suggest /wp-includes/js/jquery/suggest.js schedule /wp-includes/js/jquery/jquery.schedule.js

Slide 48

Slide 48 text

#zendcon Be Unique

Slide 49

Slide 49 text

#zendcon Be Unique  PHP: • Classes • Functions • global vars • (global) constants  WP: • shortcodes • option(s) / meta fields • nonces • settings pages • custom post types • hooks  Filenames  HTML/CSS: • classes, ids  Javascript: • I18n object • functions  Multi-lingual • I18n text domain Choose your plugin name carefully & implement consistently

Slide 50

Slide 50 text

#zendcon Be Lazy, Stay Lean

Slide 51

Slide 51 text

#zendcon Be Lazy, Be Lean is_...() functions Conditional loading Hook in Include files css / js Minify

Slide 52

Slide 52 text

#zendcon Be Safe

Slide 53

Slide 53 text

#zendcon Be Safe Check early & check often current_user_can() Validation all input sanitize_text_field(), sanitize_title(), sanitize_meta(), sanitize_user() etc Prepare all queries $wpdb->prepare() Escape all output wp_kses(), esc_html(), esc_attr(), esc_url(), esc_textarea(), esc_js() etc Use wp_nonce wp_create_nonce(), wp_nonce_url(), wp_nonce_field(), wp_verify_nonce(), check_admin_referer(), check_ajax_referer()

Slide 54

Slide 54 text

#zendcon Be Wordly

Slide 55

Slide 55 text

#zendcon I18n Quiz Time http://is.gd/WRLP9C (https://developer.wordpress.com/2015/04/23/ wordpress-developers-test-your-i18n- internationalization-knowledge/)

Slide 56

Slide 56 text

#zendcon Be Worldly  GetText load_plugin_textdomain( 'my_plugin', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' ); wp_kses_post( sprintf( __( 'Post updated. View post', 'my_plugin' ), esc_url( get_permalink( $post_ID ) ) ) ); esc_html__( 'Custom field updated.', 'my_plugin' );

Slide 57

Slide 57 text

#zendcon Be Worldly  GetText load_plugin_textdomain( 'my_plugin', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' ); wp_kses_post( sprintf( __( 'Post updated. View post', 'my_plugin' ), esc_url( get_permalink( $post_ID ) ) ) ); esc_html__( 'Custom field updated.', 'my_plugin' );

Slide 58

Slide 58 text

#zendcon Be Worldly  GetText  Not only UTF-8 seems_utf8( $str ) wp_check_invalid_utf8( $string, $strip = false ) utf8_uri_encode( $utf8_string, $length = 0 ) convert_chars( $content )

Slide 59

Slide 59 text

#zendcon Let Others Hook In

Slide 60

Slide 60 text

#zendcon Let Others Hook In do_action( 'my_unique_action_hook', $var_to_pass ); $filtered_var = apply_filters( 'my_unique_filter_hook', $var_to_pass, $context_var, $another_context_var );

Slide 61

Slide 61 text

#zendcon Make it Beautiful

Slide 62

Slide 62 text

#zendcon

Slide 63

Slide 63 text

Developing & Debugging

Slide 64

Slide 64 text

#zendcon Don’t Get Discouraged WP_DEBUG Error logging! JS console logging set_ transient()

Slide 65

Slide 65 text

#zendcon Useful Plugins Debug Bar + Extensions Developer Pig Latin User Switching Log Deprecated Notices What’s Running Demo Data Theme Check RTL Tester

Slide 66

Slide 66 text

#zendcon Helpful Infrastructure WordPress Coding Standards CS WP Unit Testing Framework Varying Vagrant Vagrants WP CLI Generate WP WP Gear WPackagist vs TGMPA

Slide 67

Slide 67 text

#zendcon

Slide 68

Slide 68 text

#zendcon Agenda Applying Best Practices

Slide 69

Slide 69 text

Let’s Make Things Better

Slide 70

Slide 70 text

#zendcon “ ” To be prepared is half the victory. - Miguel de Cervantes

Slide 71

Slide 71 text

#zendcon Let’s Make Things Better Copyrighted Post Easy Bootstrap Shortcode Login Logout Search Meter

Slide 72

Slide 72 text

#zendcon Useful Resources Codex Developer Reference Source code (collection of best practices) Hook database Demo Quotes plugin (including handbooks!)

Slide 73

Slide 73 text

#zendcon Agenda Closing

Slide 74

Slide 74 text

Stay Involved

Slide 75

Slide 75 text

#zendcon Help someone who’s struggling

Slide 76

Slide 76 text

#zendcon Find an abandoned project

Slide 77

Slide 77 text

#zendcon Have fun!

Slide 78

Slide 78 text

Thank You! Slides: http://speakerdeck.com/jrf Feedback: https://joind.in/15509 Contact me: @jrf_nl jrf

Slide 79

Slide 79 text

#zendcon Image Credits  WordPress - mkhmarketing (crayons) http://www.flickr.com/photos/mkhmarketing/8469030267/  Billie Holiday – William P. Gottlieb http://lcweb2.loc.gov/diglib/ihas/loc.natlib.gottlieb.04251/default.html  Anatomy - Eva di Martino http://www.pureblacklove.com  Hooks - Raul Lieberwirth http://www.flickr.com/photos/lanier67/185311136/  Loop - Gabe Kinsman http://www.flickr.com/photos/auguris/5286612308/  Dominos – George Hodan http://www.publicdomainpictures.net/view-image.php?image=30522&picture=domino  Hooks - Melissa Maples http://www.flickr.com/photos/melissamaples/3093966940/  Wheel - Pauline Mak http://www.flickr.com/photos/__my__photos/5025541044/  Conflict - Asaf Antman http://www.flickr.com/photos/asafantman/5134136997/  Unique - Luca Volpi (leafs) http://www.flickr.com/photos/luca_volpi/2974346674/  Lazy - Kevin Cauchi http://www.flickr.com/photos/kpcauchi/5376768095/

Slide 80

Slide 80 text

#zendcon Image Credits  Security – kismihok http://www.flickr.com/photos/kismihok/9686252463/  World - Kenneth Lu http://www.flickr.com/photos/toasty/1540997910/  Hooks – Macroman (red background) http://www.flickr.com/photos/macroman/34644959/  Daisies - Steve Wall http://www.flickr.com/photos/stevewall/4780035332/  Alone – Jon http://www.flickr.com/photos/jb-london/3594171841/  Breaktime - Iryna Yeroshko https://www.flickr.com/photos/mandarina94/6389984357/  Help - Green Kozi http://www.flickr.com/photos/themacinator/3445776069/  Bike - Pauline Mak http://www.flickr.com/photos/__my__photos/6399028713/  Fun - Justin Beckley http://www.flickr.com/photos/justinbeckleyphotography/8452437969/  Logos used are the property of and may be trademarked by their respective organizations.