WordCamp Jerusalem - Doing it Wrong

Preview:

DESCRIPTION

Things developers tend to do wrong with WordPress. Based on amazing presentations from many great WordPress developers across the world. Slides mostly in English, presentation was in Hebrew at WordCamp Jerusalem (September 2011). הערות והפניות בבלוג בכתובת http://wp.me/pD4bk-Jn

Citation preview

You’re doing it wrong!עשר טעויות נפוצות בפיתוח לוורדפרס

Monday, September 12, 11

You’re doing it wrong!עשר תשע טעויות נפוצות בפיתוח לוורדפרס

Monday, September 12, 11

Monday, September 12, 11

Monday, September 12, 11

מפתח VIP בחברת Automattic

צוות ה-VIP מפקח בין WordPress השאר על קוד

שיוצר מעל ל-1,000,000,000 דפים

נצפים בחודש

http://yoavfarhi.com @yoavf

Monday, September 12, 11

Monday, September 12, 11

Monday, September 12, 11

/me activates WP_DEBUG...Monday, September 12, 11

Monday, September 12, 11

1. WP_DEBUG

(wsod)

Monday, September 12, 11

WP_DEBUG 1/2

• Include notices

• Bypasses some error suppression (ie WPDB )

• Triggers notices for deprecated functions ex:

define('WP_DEBUG', true);

PHP Notice: get_links is deprecated since version 2.1! Use get_bookmarks() instead. in /Users/yoavfarhi/Sites/trunk/wp-includes/functions.php on line 3409

Monday, September 12, 11

WP_DEBUG 2/2

define('WP_DEBUG_DISPLAY', false);

define('WP_DEBUG_LOG', true);

Output to wp-content/debug.log

Don’t display

Monday, September 12, 11

2. Function Naming

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

Fatal Error

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

Angry Client

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

Call From Client

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

Time / Money

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

We don’t want that!

Monday, September 12, 11

Imagine...

Plugin 1function custom_script() { //...}

Theme / Plugin 2function custom_script() { //...}

Solution: Prefix

Monday, September 12, 11

Prefix!

Plugin 1function plugin1_custom_script() { //...}

Theme / Plugin 2function themename_custom_script() { //...}

Monday, September 12, 11

Prefix!

Plugin 1function plugin1_custom_script() { //...}

Theme / Plugin 2function themename_custom_script() { //...}

Happy!

Monday, September 12, 11

Or: Use a Classclass my_plugin {

function custom_script() { //... }

function whatever() { //... }

}

new my_plugin;

Monday, September 12, 11

nomy_id AND {$table_prefix}terms.term_id = {$table_prefix}term_taxonomy.term_taxonomy_idonomy.term_id AND {$table_prefix}posts.ID = {$table_prefix}postmetata.post_id AND {$table_prefix}

3. Direct SQL Queries

Monday, September 12, 11

$last_posts = (array)$wpdb->get_results(" SELECT post_date, ID, post_title, name, {$table_prefix}terms.term_id FROM {$table_prefix}posts, {$table_prefix}term_relationships, {$table_prefix}terms, {$table_prefix}postmeta, {$table_prefix}term_taxonomy WHERE {$table_prefix}posts.ID = {$table_prefix}term_relationships.object_id AND {$table_prefix}term_taxonomy.term_taxonomy_id = {$table_prefix}term_relationships.term_taxonomy_id AND {$table_prefix}terms.term_id = {$table_prefix}term_taxonomy.term_id AND {$table_prefix}posts.ID = {$table_prefix}postmeta.post_id AND {$table_prefix}postmeta.meta_key = '_mini_post' AND {$table_prefix}postmeta.meta_value = 0 AND post_status = 'publish' AND {$table_prefix}terms.term_id != 25 AND {$table_prefix}terms.term_id != 24 AND {$table_prefix}term_taxonomy.taxonomy = 'category' ");Monday, September 12, 11

$last_posts = (array)$wpdb->get_results(" SELECT post_date, ID, post_title, name, {$table_prefix}terms.term_id FROM {$table_prefix}posts, {$table_prefix}term_relationships, {$table_prefix}terms, {$table_prefix}postmeta, {$table_prefix}term_taxonomy WHERE {$table_prefix}posts.ID = {$table_prefix}term_relationships.object_id AND {$table_prefix}term_taxonomy.term_taxonomy_id = {$table_prefix}term_relationships.term_taxonomy_id AND {$table_prefix}terms.term_id = {$table_prefix}term_taxonomy.term_id AND {$table_prefix}posts.ID = {$table_prefix}postmeta.post_id AND {$table_prefix}postmeta.meta_key = '_mini_post' AND {$table_prefix}postmeta.meta_value = 0 AND post_status = 'publish' AND {$table_prefix}terms.term_id != 25 AND {$table_prefix}terms.term_id != 24 AND {$table_prefix}term_taxonomy.taxonomy = 'category' ");

WRONG!

Monday, September 12, 11

$args = array( 'post_type' => 'post', 'post_status' => 'publish', 'tax_query' => array( array( 'taxonomy' => 'category', 'terms' => array( '25', '24'), 'field' => 'id', 'operator' => 'NOT IN', ) ), 'meta_query' => array( array( 'key' => '_mini_post', 'value' => 0, ) ));

$last_posts = new WP_Query( $args );

Monday, September 12, 11

$args = array( 'post_type' => 'post', 'post_status' => 'publish', 'tax_query' => array( array( 'taxonomy' => 'category', 'terms' => array( '25', '24'), 'field' => 'id', 'operator' => 'NOT IN', ) ), 'meta_query' => array( array( 'key' => '_mini_post', 'value' => 0, ) ));

$last_posts = new WP_Query( $args );

Monday, September 12, 11

This is so much better because:

Monday, September 12, 11

This is so much better because:

• It is easier to read, easier to change

Monday, September 12, 11

This is so much better because:

• It is easier to read, easier to change

• It is more secure

Monday, September 12, 11

This is so much better because:

• It is easier to read, easier to change

• It is more secure

• It Uses WP object caching and so faster

Monday, September 12, 11

This is so much better because:

• It is easier to read, easier to change

• It is more secure

• It Uses WP object caching and so faster

• It is platform indifferent

Monday, September 12, 11

This is so much better because:

• It is easier to read, easier to change

• It is more secure

• It Uses WP object caching and so faster

• It is platform indifferent

• It is future proof

Monday, September 12, 11

This is so much better because:

• It is easier to read, easier to change

• It is more secure

• It Uses WP object caching and so faster

• It is platform indifferent

• It is future proof

• It is bound to be improved

Monday, September 12, 11

4. Loading JS/CSS/etc

Monday, September 12, 11

Javascript

Monday, September 12, 11

<script type='text/javascript' src='http://thisismysite.com/wp-content/themes/mytheme/js/newsletter.js'></script>

Wrong!

function mytheme_load_script() { wp_enqueue_script( 'newsletter', 'http://thisismysite.com/wp-content/themes/mytheme/js/newsletter.js', array( 'jquery' ), '0.1' ); }

add_action( 'wp_enqueue_scripts', 'mytheme_load_script');

Javascript

Monday, September 12, 11

<script type='text/javascript' src='http://thisismysite.com/wp-content/themes/mytheme/js/newsletter.js'></script>

Wrong!

function mytheme_load_script() { wp_enqueue_script( 'newsletter', 'http://thisismysite.com/wp-content/themes/mytheme/js/newsletter.js', array( 'jquery' ), '0.1' ); }

add_action( 'wp_enqueue_scripts', 'mytheme_load_script');

Ok, but what if ...

Javascript

Monday, September 12, 11

Wrong!

Right!

Javascript

<script type='text/javascript' src='http://thisismysite.com/wp-content/themes/mytheme/js/newsletter.js'></script>

function mytheme_load_script() { wp_enqueue_script( 'newsletter', get_template_directory_uri() . '/js/newsletter.js', array( 'jquery' ), '0.1' ); }

add_action( 'wp_enqueue_scripts', 'mytheme_load_script');

Monday, September 12, 11

Use wp_enqueue_* because:

• DRY - don’t repeat yourself

• You can set dependencies

• And manage versions

• And easily move to the footer

• And control where it loads (admin/page/...)

Monday, September 12, 11

Never assume a file location

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()plugins_url()

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()plugins_url()content_url()

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()plugins_url()content_url()get_theme_directory() *

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()plugins_url()content_url()get_theme_directory() *get_theme_directory_uri() *

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()plugins_url()content_url()get_theme_directory() *get_theme_directory_uri() *includes_url()

Monday, September 12, 11

Never assume a file location

• There’s a function for that:

site_url()plugins_url()content_url()get_theme_directory() *get_theme_directory_uri() *includes_url()admin_url()

Monday, September 12, 11

5. Fetching remote content

Monday, September 12, 11

What’s your transport?

file_get_contents() ?CURL ?

fsockopen() ?stream_socket_client() ?

Monday, September 12, 11

What’s your transport?

file_get_contents() ?CURL ?

fsockopen() ?stream_socket_client() ?

WRONG!

Monday, September 12, 11

Helper functions

//Simple Get$resp = wp_remote_get( 'http://example.com/' );

//Simple Post$args = array( 'body' => 'some data', 'user-agent' => 'your plugin');$resp = wp_remote_post( 'http://example.com', $args );

The WP_Http class

Monday, September 12, 11

The WP_Http class

• Cookies

• Headers

• Authentication

• Timeouts

• Etc...

* Add your own caching

Supports

Monday, September 12, 11

6. Doing Ajax

Monday, September 12, 11

Doing Ajax

Monday, September 12, 11

Doing Ajax<script>var ajaxurl = <?php echo plugins_url( 'ajax.php', __FILE__ ); ?>

$('#button').click(function(){ $.post(ajaxurl, function(data) { $('.result').html(data); }); });</script>

front-end:

Monday, September 12, 11

Doing Ajax<script>var ajaxurl = <?php echo plugins_url( 'ajax.php', __FILE__ ); ?>

$('#button').click(function(){ $.post(ajaxurl, function(data) { $('.result').html(data); }); });</script>

<?phprequire_once('../../../wp-load.php');//...

front-end:

ajax.php:

Monday, September 12, 11

Doing Ajax<script>var ajaxurl = <?php echo plugins_url( 'ajax.php', __FILE__ ); ?>

$('#button').click(function(){ $.post(ajaxurl, function(data) { $('.result').html(data); }); });</script>

<?phprequire_once('../../../wp-load.php');//...

front-end:

ajax.php:WRONG!

Monday, September 12, 11

Doing Ajax

Monday, September 12, 11

Doing Ajax<script>var ajaxurl = <?php echo admin_url( 'admin-ajax.php'); ?>

$('#button').click(function(){ var data = { action: 'my_action', }; $.post(ajaxurl, function(data) { $('.result').html(data); }); });</script>

front-end:

Monday, September 12, 11

Doing Ajax

add_action('wp_ajax_my_action', 'plugin_action_callback');add_action('wp_ajax_nopriv_my_action', 'plugin_action_callback');

function plugin_action_callback() { // do whatever echo $whatever; die();}

<script>var ajaxurl = <?php echo admin_url( 'admin-ajax.php'); ?>

$('#button').click(function(){ var data = { action: 'my_action', }; $.post(ajaxurl, function(data) { $('.result').html(data); }); });</script>

front-end:

php:

Monday, September 12, 11

7. Security? What ‘bout it?

http://xkcd.com/327/

Monday, September 12, 11

Never trust user input

Monday, September 12, 11

Never trust user input

<h1>Search results for: <?php echo $_GET['s']; ?></h1>

1. XSS

http://mysite.com/?s=</h1><script>alert('This Javascript can do anything!');</script>

Monday, September 12, 11

Never trust user input

<h1>Search results for: <?php echo $_GET['s']; ?></h1>

1. XSS

http://mysite.com/?s=</h1><script>alert('This Javascript can do anything!');</script>Wrong!

Monday, September 12, 11

Never trust user input

<h1>Search results for: <?php echo $_GET['s']; ?></h1>

1. XSS

http://mysite.com/?s=</h1><script>alert('This Javascript can do anything!');</script>Wrong!

<h1>Search results for: <?php echo esc_attr( $_GET['s'] ) ; ?></h1>

get_search_query()

Escape late, escape EVERYTHING

Solution:

Monday, September 12, 11

Never trust user input

Monday, September 12, 11

Never trust user input2.SQL Injection $wpdb->query( " UPDATE $wpdb->posts SET post_title = ". $POST['title'] ." WHERE ID = 1" );

Monday, September 12, 11

Never trust user input2.SQL Injection $wpdb->query( " UPDATE $wpdb->posts SET post_title = ". $POST['title'] ." WHERE ID = 1" );

Wrong!

Monday, September 12, 11

Never trust user input2.SQL Injection $wpdb->query( " UPDATE $wpdb->posts SET post_title = ". $POST['title'] ." WHERE ID = 1" );

Wrong!

Solution:• Use APIS ( $wpdb->update, $wpdb->prepare )• Validate data ( whitelist > blacklist )

Monday, September 12, 11

Never trust user input

Monday, September 12, 11

Never trust user input

3.CSRFAuthorization VS Intention

Monday, September 12, 11

Never trust user input

3.CSRFAuthorization VS Intention

Use nonces, not just current_user_can()

wp_create_nonce(), wp_verify_nonce(), check_admin_referer()

Solution:

Monday, September 12, 11

8. Trunk?Monday, September 12, 11

$ mkdir blog

$ cd blog

$ svn co http://core.svn.wordpress.org/trunk/ .

Mac / Linux

Monday, September 12, 11

Windows / Tortoise SVN

Monday, September 12, 11

Running on Trunk

No, not for production

Monday, September 12, 11

9. Not ContributingMonday, September 12, 11

Contributing

Monday, September 12, 11

Contributing

• Core patches/testing/documentation

Monday, September 12, 11

Contributing

• Core patches/testing/documentation

• Release plugins and themes

Monday, September 12, 11

Contributing

• Core patches/testing/documentation

• Release plugins and themes

• Translations and i18n

Monday, September 12, 11

Contributing

• Core patches/testing/documentation

• Release plugins and themes

• Translations and i18n

• Forums support

Monday, September 12, 11

Contributing

• Core patches/testing/documentation

• Release plugins and themes

• Translations and i18n

• Forums support

• Tip! (or Buy)

Monday, September 12, 11

Questions ?Monday, September 12, 11