Tips for Best Practices in WordPress Development
In this series, we're going to cover the most important things you should consider when developing a WordPress Plugin or a WordPress Theme.
This guide aims to provide a set of good practices that will be helpful to beginners and also to experts developers that are starting to work with WordPress
But wait! If you've been developing WordPress plugins for a while take a look before deciding this guide is not for you. I'm sure you will get something of it. After all, we all have something unique to offer.
Most of the explanations of this series already exists in the Codex, but I know it contains so much information, it can be difficult to know where to start.
Today we are covering the following topics:
- The WordPress Coding Standards
- Avoiding Function Name Collisions
- Code Comments
- Security Tips
The series aims to be as clear as possible and will include both good and bas examples in order to give a sense for how certain things should work when writing WordPress-specific code.
Note that not everything is mandatory to write a plugin; however, if you're just getting started, when why not start off on the right foot?
I will try to make these series easy to read. I will include some good and bad code examples. Not everything explained here is mandatory to write a Plugin, but if you are starting with Wordpress development, Why not start the right way? Once it becomes an habit will be hard to do it wrong.
WordPress Coding Standards
Personally speaking, this is one of my biggest flaws when I develop plugins. If you develop tools for WordPress, you should simply follow the WordPress Coding standards. Coding standards helps to improve the readability of code and helps to avoid common coding errors.
WordPress is a collaborative CMS and such a simple thing as everyone writing code the same way makes reading, writing, and maintaining the code that easier for everyone. At the beginning, it may be difficult to change the coding style with which you're used to working, but eventually you will find it will becoming second nature and your code will be cleaner, and much easier to read.
In the WordPress Handbook the standards are divided into the four main used languages
Examples
Below I will show you some simple PHP brace style examples, so you can get an idea.
Bad Examples
01
02
03
04
05
06
07
08
09
10
11
12
| if (condition) action0( $var ); if (condition) { action1(); } elseif (condition2) { action2a(); action2b(); } |
Good Examples
01
02
03
04
05
06
07
08
09
10
| if ( condition ) { action0( $var ); } if ( condition ) { action1(); } elseif ( condition2 ) { action2a(); action2b(); } |
The second example is much easier to read isn't it? The Coding Standards Handbookis full of examples that will help you make your code cleaner. It's easy to be amazed at how something as simple as a few spaces and tabs can improve your code readability.
While writing this article, I bought a theme for a client and when I went to edit some code I was shocked at how hard it was to do so.
Here's what I mean:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| <? php if (have_posts()){ $ row_count = 0 ; while (have_posts()){ the_post(); $row_count++; $ post_id = get_the_ID (); //get all post categories $ categories = get_the_category (get_the_ID()); ?> < div <?php if(!of_get_option('disable_css_animation')==1){?><? php post_class('feature-two-column medium-two-columns appear_animation'); ?><? php }else{?> <? php post_class('feature-two-column medium-two-columns'); ?> <? php }?><? php if($row_count % 2 != 0){echo ' id = "margin-left-post-'.$row_count.'" ';}?>> < div class = "image_post feature-item" > < a href="<?php the_permalink(); ?>" class="feature-link" title="<? php the_title_attribute(); ?>"> <? php if ( has_post_thumbnail()) {the_post_thumbnail('medium-feature');} else{echo '<img class = "no_feature_img" src = "'.get_template_directory_uri().'/img/feature_img/medium-feature.jpg'.'" >';} ?> </ a > <? php echo jellywp_post_type(); ?> <? php echo total_score_post_front(get_the_ID());?> <? php if(of_get_option('disable_post_category') !=1){ if ($categories) { echo '<span class = "meta-category" >'; foreach( $categories as $tag) { $tag_link = get_category_link($tag->term_id); $titleColor = categorys_title_color($tag->term_id, "category", false); echo '< a class = "post-category-color" style = "background-color:'.$titleColor.'" href = "'.$tag_link.'" >'.$tag->name.'</ a >'; } echo "</ span >"; } }?> </ div > |
A bit scary, right? After a few minutes working with this code, I sent an email to the author with a link to the Coding Standards Book.
Avoiding Function Name Collisions
Name collisions occur when a function has the same name as a function that's already been defined. For example, if in you theme you have a function called
get_the_post_terms()
and you install a plugin that have a function with the same name you will get something like:
1
| Fatal error: Cannot redeclare get_the_post_terms() (previously declared in.... |
Unfortunately, they happen far more frequently than they should. The thing is, it's easy to avoid.
To avoid this we have options:
1. Prefix Your Functions
For example, if your plugin name is "WordPress Cool Plugin", you could use a
wcc_
prefix in all your functions.
So in the example above our function name will be
wcc_get_the_post_terms()
I also recommend you to prefix your CSS, or at least try to make it more unique to avoid modifying other plugins styles
2. Wrap Your Functions Inside a Class
Maybe your plugin is so simple that it doesn't need a class, but you can still create one to keep things organized. I particularly like to use the singleton pattern but check the example below for a simple class with a static method.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| class Wcc_Mailer { static function send( $post_ID ) { $friends = 'jhondoe@example.org' ; mail( $friends , "New post!" , 'Check my new post in ' . get_permalink( $post_ID ) ); return $post_ID ; } } add_action( 'publish_post' , array ( 'Wcc_Mailer' , 'send' ) ); |
As you can see on this example I only prefixed my class name, but my function is simple called "send". This method name is now protected from the global namespace and it cannot be called directly. To call it I will need to do this:
1
| Wcc_Mailer::send( $post_id ); |
Commenting Code
Code's comments are the developer's best friend. You may find unnecessary to comment every single function or variable you create, but trust me, when your code grows - especially as it received contributions from others - it can become difficult to know exactly what to do.
Also as I said before, WordPress is a collaborative CMS. Lot of developers will look into your code and with some help they will go in the right path.
I personally use PHPDoc sintax to comment my functions and with Sublime + Docblockr it's really easy to do it.
Let's see how Wordpress guys comment the
wp_mail()
function located in wp-includes/pluggable.php
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| /** * Send mail, similar to PHP's mail * * A true return value does not automatically mean that the user received the * email successfully. It just only means that the method used was able to * process the request without any errors. * * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from * creating a from address like 'Name <email@address.com>' when both are set. If * just 'wp_mail_from' is set, then just the email address will be used with no * name. * * The default content type is 'text/plain' which does not allow using HTML. * However, you can set the content type of the email by using the * 'wp_mail_content_type' filter. * * The default charset is based on the charset used on the blog. The charset can * be set using the 'wp_mail_charset' filter. * * @since 1.2.1 * * @uses PHPMailer * * @param string|array $to Array or comma-separated list of email addresses to send message. * @param string $subject Email subject * @param string $message Message contents * @param string|array $headers Optional. Additional headers. * @param string|array $attachments Optional. Files to attach. * @return bool Whether the email contents were sent successfully. */ function wp_mail( $to , $subject , $message , $headers = '' , $attachments = array () ) { [....] // Send! try { return $phpmailer ->Send(); } catch ( phpmailerException $e ) { return false; } } |
As you can see they describe what the function does, what parameters are needed and what it's going to return.
Pretty self-explanatory, right?
Comments are not meant to be used only with PHP. In HTML, for example, I like to use
<!--#id-of-div-->
at the end of large blocks of code so I don't get lost so easily.
For CSS, I use comments to split my code in different sections.
For example :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| /********************* GENERAL STYLES *********************/ body { font-family : Arial ; color : #333 ; } /****************************************************************** H1, H2, H3, H4, H5 STYLES ******************************************************************/ h 1 , .h 1 { font-size : 2.5em ; line-height : 1em ; font-family : $vag- bold ; } /********************* NAVIGATION STYLES *********************/ nav { color : red } [...] |
Share your commenting tricks with us!
Security Tips
Security needs to be taken very seriously! If you plugin or theme gets popular, trust me, you don't want to be the culprit of thousands of sites hacked. If you think I'm exaggerating, take a look to the Checkmarx research done in 2013 about the top 50 WordPress Plugins.
Now let's see some WordPress development security tips:
XSS Vulnerabilities
To prevent XSS, we have to do two things. Sanitize data input and sanitize output data. We have several methods to sanitize depending on the data and the context it's used. By general rule you don't have to trust any input data, and don't trust any data that is going to be outputted.
For input data you can use for example
sanitize_text_field()
that checks for invalid UTF-8, Convert single < characters to entity, strip all tags, remove line breaks, tabs and extra white space and strip octets. Depending on the context you are, there are different functions that will help you out sanitizing your data.
The same happens when you output your data. Check the following example on how to output a link:
1
| < a href="<?php echo esc_url( $url ); ?>"><? php echo esc_html( $text ); ?></ a > |
esc_url
rejects invalid urls, eliminates invalid characters, and removes dangerous charactersesc_html
encodes < > & " ' when outputting HTML.
Again, depending on the data you have, there are different functions to help you out. For JavaScript your could use
esc_js
.
In addition to sanitization, remember to validate your date, as well.
Prevent Direct Access to Your Files
Most hosts allow direct access to files. In your plugin, this means that most probably some PHP errors will arise and those errors are valuable information for attackers.
A very basic code to prevent this, that you can place on top of your script is:
1
2
| // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit ; |
This basically prevents to execute the script if we are not accessing it through WordPress.
Remove All Warning and Notices
Not only PHP errors help attackers - notices and warnings also include a lot of valuable information. Every plugin should be coded using DEBUG mode. This will also help to catch deprecated functions on your plugin. To enable
DEBUG
mode simple search this line on your wp-config.php
and change it to TRUE
.
1
| define( WP_DEBUG, true ); |
Along with this, your should try the great Debug Bar plugin. By adding this other simple line, you will also be able to analyze all the database queries.
1
| define( 'SAVEQUERIES' , true ); |
Use Nonce Values
Nonce values are short for numbers used once and are used to protect against cross-site request forgeries, or CSRF, that, in other words, are unexpected or duplicate requests that could cause undesired permanent or irreversible changes to the web site and particularly to its database. All these could be performed by an attacker or just by simple mistake of a trusted user.
Depending on where you need the nonce you can create it in different ways:
To use it on a link, use
wp_nonce_url()
1
| $complete_url = wp_nonce_url( $bare_url , 'trash-post' , 'my_nonce' ); |
To use it on a form, use
wp_nonce_field()
1
| wp_nonce_field( 'trash-post', 'my_nonce' ); |
To use it on any other place, use
wp_create_nonce()
1
| wp_localize_script( 'my-script' , 'my-var-name' , array ( 'nonce' => wp_create_nonce( 'trash-post' , 'my_nonce' ) ); |
If you check my example above, you will see how I use
wp_localize_script
( which I will talk about it on the next article) to include my nonce in a JavaScript code block. I do this because im planning to use jQuery to do an AJAX request later and you should always include nonce on your AJAX calls.
Then, in your script to simply verify the nonce, you enter the following code
1
2
3
| if( ! wp_verify_nonce( 'trash_post' , 'my_nonce') ) { die( 'Busted!'); } |
Use WordPress Functions and Libraries
Always check if whatever you're trying to do is possible to do it with WordPress core functions and libraries. That way, your scripts will be less prone to vulnerabilities and if some appears, they will be fixed by the WordPress core contributors and you won't have to worry to contact all of your clients.
The most famous example on this is with TimThumb library that, up until a few years ago, was used by thousands of plugins and themes. One day, in 2011 the vulnerability was disclosed. Since then, we can now use the built-in
add_image_size()
function for this purpose.
Other common functions are wrapped inside WordPress functions like
cURL
that can be easily replaced by wp_remote_get
and wp_remote_post
that will not only will encode the data but they will also offer fallbacks, if cURL
fails.
Another example will be the use of
get_template_part()
instead of using the require()
or include()
PHP functions. While they are basically the same, the former one already knows where your theme is located and it will look for the requested file in that theme’s directory, It won't issue a warning or fatal error if the requested file does not exist, It can search for other suitable files, if the requested one is not found and it knows about child themes and parent themes.
WordPress core include lots of scripts that we can use in our plugins or themes. So always take a look before adding new libraries.
What's Next?
I hope you got a lot out of this particular article. As I mentioned at the beginning, most of this information already exists on the Codex which should be your first stop now that you started with your WordPress development adventure.
When I started developing with WordPress a few years ago, I wished I had a guide like this with all the information together, so that's the main reason I decided to write these series.
On the next article I will explain the following topics:
- The correct way to add JavaScript and stylesheets in your plugin
- How to use Ajax in WordPress the correct way
- Let your users do changes with hooks
Please don't hesitate to leave your comments or suggestions for the two remaining articles on these series.
This is a community driven project, so you are encouraged to contribute as well, and we are counting on your feedback. login exam for wordpress
ReplyDeleteGreat blog you have got here.. It’s hard to find excellent writing like yours these days.
ReplyDeletebest web design services | top android app development companies
Thank you for this great script...
ReplyDeletewordpress plugin development | website development company sydney