Developer Workshop: What I Wish I'd Known When I Started

Developer Workshop: What I Wish I'd Known When I Started

WordPress provides a lot of convenience functions, perhaps too many. All too often, new developers reinvent the wheel, as the cliche goes, when Core already has a function or utility to accomplish the same. Six years on and thinking back to when I started building WordPress plugins, I could’ve saved a lot of time and avoided a lot of anxiety if I’d known where to look.


Erick Hitter

July 09, 2016


  1. Developer Workshop:
 What I Wish I’d Known When I

    Started Erick Hitter @ethitter
  2. PREAMBLE Please, interrupt
 and ask questions

  3. INTRO Why should
 developers care?

  4. Why does this matter? • Helps future-proof your code •

    Makes your code easier to maintain • Easier for others to learn and maintain your code • Reduces duplication
  5. Why does this matter? • Security • Performance • Scalability

  6. FIRST Can’t check if the user’s logged in until init

  7. Why? • WordPress checks the user login between the after_setup_theme

    and init actions
  8. So? • WordPress may protect you, and it may work

    earlier, but it can’t be relied on • Can’t load code at the plugins_loaded action based on the user • Theme setup can’t take user into account
  9. SECOND Query results aren’t available until wp

  10. Practical implications • Most conditionals aren’t available before wp •

    Can’t use query conditionals at init or in any earlier hooks • Can conditionally load code in the admin, or not, but that’s about it
  11. THIRD Never build a manual link again

  12. Link Functions •get_permalink() •get_page_by_path( 'about' ) •get_post_type_archive_link( 'waffles' ) •user_trailingslashit()

    •get_year_link() •get_adjacent_post()
  13. Link Functions •add_query_arg() •remove_query_arg()

  14. Link Functions •set_url_scheme()
 Force HTTP URLs to

    HTTPS, vice versa, and more
  15. Link Functions •home_url( '/' )
 Many more

  16. The importance of slashes • WP’s permalink structure controls if

    URLs should end in a slash or not. • WP redirects requests to the “incorrect” form of the permalink. • Therefore, use user_trailingslashit() to avoid unnecessary redirects.
  17. FOURTH Escaping and Sanitization

  18. Why? • Security!!! User input is untrustworthy. • WordPress makes

    this really easy • Escaping functions for handling output • Sanitization functions for cleaning data to save
  19. Escaping Functions •esc_attr() •esc_html() •esc_url() •esc_textarea()

  20. Sanitization Functions •sanitize_text_field() •sanitize_email() •sanitize_user()

  21. An Exception • Normally, almost all output should be escaped

    • WordPress template tags are a rare exception • Otherwise, always assume nefarious intent, even by “trusted” users such as administrators
  22. An Exception • There’s really no excuse for not sanitizing

    user input

  24. FIFTH Nonces

  25. Remember how we
 don’t trust the user? • Intent is

    just as important as input sanitization • Should the user have been able to do that thing, in the way they did so?
  26. Examples • Did the user really click the “delete” button?

    • Did this request originate from where we expected?
  27. nonce Functions •wp_create_nonce() •wp_verify_nonce() •wp_nonce_field()

  28. Caveats • User ID is part of their creation, so

    can’t be used for logged-out requests • Actually valid for a time period, not number of uses • Regardless, they play an important part in protecting against CSRF
  29. SIXTH Ajax is easy with WordPress

  30. Ajax • Hook your function to one of two variable

    actions • Use the same action name with the request to admin-ajax.php •check_ajax_referer() or use a nonce
  31. Ajax •wp_ajax_{$your_action} •wp_ajax_nopriv_{$your_action} • Hook to both if logged-in state

    isn’t relevant.

  33. Why? • Framework introduced in WordPress 4.4 • Doesn’t run

    in admin context, unlike admin-ajax • Much simpler than creating custom REST endpoints • Obeys Core’s permalinks, making requests cacheable
  34. How? •register_rest_route( $namespace, $route, $args )

  35. EIGHTH Database Interactions

  36. Database Interactions • Use custom post types and custom taxonomies

    instead. • If you must, always $wpdb->prepare() your queries.
  37. Database Interactions •$wpdb->get_var() •$wpdb->get_col() •$wpdb->get_row() •$wpdb->insert( $table, $data, $format )

    •$wpdb->update( $table, $data, $where, $format, $where_format ) •$wpdb->query()
  38. NINTH Enqueue All The Things

  39. Register, then enqueue! •wp_register_style() •wp_register_script()

  40. Enqueue! •wp_enqueue_style() •wp_enqueue_script()

  41. But why? • Reusable • Dependencies • Versioning • Minification

    • Concatenation • CDN
  42. TENTH Cache All The Things

  43. Caching • Transients • Object Cache

  44. ELEVENTH Remote Requests

  45. WP HTTP API •wp_remote_get() •wp_remote_post() •wp_remote_head() •wp_remote_request() •wp_remote_retrieve_response_code()

  46. TWELFTH Roles & Capabilities

  47. Roles • Don’t check a user’s role, rather check if

    that user has a specific capability • Roles can change at runtime • Native capabilities can be taken away from roles • Specific user’s capabilities can be filtered • Familiar with user levels? Ignore them too.
  48. Capabilities • Add to specific user roles whose members need

    those abilities • Control access by checking a user’s capabilities • Can add same capability to multiple roles • Can also remove a capability from all roles, blocking all access
  49. Capabilities • Don’t add custom capabilities unless they’re necessary •

    Often, a native capability that WordPress uses for related functionality can be leveraged instead
  50. Functions • current_user_can() • user_can() • add_role() • WP_Role::add_cap()

  51. THIRTEENTH Rewrites

  52. Rewrite Rules • aka “Pretty Permalinks” • They don’t use

    query strings, improving cacheability • Simplifies routing and parsing a user request • Provides access to request variables through WP_Query and other Core APIs
  53. Rewrite Rules • Consider replacing custom rewrite rules with REST

    API endpoints, when feasible
  54. FOURTEENTH Miscellany

  55. Miscellany •get_queried_object() •get_queried_object_id() •validate_file()

  56. Miscellany •wp_parse_args() •wp_list_pluck()

  57. Thanks Erick Hitter @ethitter