WordPress is an open source Content Management System (CMS) which is used to build and manage websites. WordPress is the most popular CMS on the internet by a country mile, powering about half of all CMS websites at time of writing and about a quarter of all websites on the internet.
WordPress started life as a platform for blogging but has evolved over the years to be suitable for most types of websites. The interface can be used without coding knowledge making it popular for beginners and developers who want to empower their clients to manage their own website.
Another large factor in the popularity of WordPress is it's flexibility, mostly due to the core's plugin and theming systems. The plugin system makes it easy to extend the core functionality without modifying the core code. In a similar manner, the theming system makes it easy to change the website's layout and aesthetics. There are now thousands of free and premium WordPress plugins and themes available. Many of these are located at the wordpress.org plugin repository and theme repository respectively.
WordPress is developed by it's own community, but is strongly associated with the company Automattic, which employs many of WordPress' core developers.
Code
WordPress is built upon the PHP server scripting language and the MySQL querying language. WordPress uses MySQL as a datastore for user content and configuration. The PHP wrangles the content data into a HTML webpage with all the necessary assets.
wordpress.com vs wordpress.org
You can use WordPress by signing up for Automattic's wordpress.com service and hosting your website on their servers, or you can download the WordPress software from wordpress.org and host your website on a server under your control. The first option is easy but you cannot edit any site code. You can only make changes through the WordPress interface. The second option requires more work but gives you flexibility to do whatever you like with your website code. If you are a StackOverflow user, you probably will be going with the second option.
Open Source
WordPress is open source software meaning it is free to use and anyone can view the source code and contribute to it. Potential contributors can get started by reading the Contribution page of the WordPress codex..
Bugs can be reported by submitting a bug on the WordPress ticket tracker.
Documentation
WordPress is officially documented in the WordPress Codex at WordPress.org. Developers working with WordPress will be particularly interested in the Developer Codex section and Developer Reference section of wordpress.org.
$show
Values | Description | Example |
---|---|---|
'name' (Default) | Site title | 'Matt Mullenweg' |
'description' | Site tagline | 'Just another WordPress site' |
'wpurl' | URL of the WordPress installation. Same as the site_url() function | 'http://example.com' , 'http://localhost/wordpress' |
'url' | URL of the site. Same as the home_url() function | 'http://example.com' , 'http://localhost/wordpress' |
'admin_email' | Email address of the main Administrator | '[email protected]' |
'charset' | Character encoding of the pages and feeds | 'UTF-8' |
'version' | Current version of the WordPress installation | '4.5' |
'html_type' | content-type value of the HTML | 'text/html' |
'text_direction' | Text direction determined by the site’s language | 'ltr' |
'language' | ISO 639-1 based language code | 'en-US' |
'stylesheet_url' | URL of the stylesheet of the activated theme. Value priority: Child theme » Parent theme. | 'http://example.com/wp-content/themes/twentysixteen/style.css' |
'stylesheet_directory' | Resource location of the activated theme. Value priority: Child theme » Parent theme. | 'http://example.com/wp-content/themes/twentysixteen' |
'template_url' | URL directory of the activated theme. Value priority: Parent theme » Child theme. | 'http://example.com/wp-content/themes/twentysixteen' |
'template_directory' | Same as 'template_url' | |
'pingback_url' | Pingback XML-RPC file | 'http://example/xmlrpc.php' |
'atom_url' | Atom feed URL | 'http://example/feed/atom/' |
'rdf_url' | RDF/RSS 1.0 feed URL | 'http://example/feed/rdf/' |
'rss_url' | RSS 0.92 feed URL | 'http://example/feed/rss/' |
'rss2_url' | RSS 2.0 feed URL | 'http://example/feed/' |
'comments_atom_url' | Comments Atom feed URL | 'http://example/comments/feed/atom/' |
'siteurl' | (deprecated) Use ‘url’ instead | |
'home' | (deprecated) Use ‘url’ instead |
$filter
Values | Description | Example |
---|---|---|
'raw' (Default) | No filters will be applied | raw data |
'display' | Filters will be applied to the return value if $show is neither 'url' , 'directory' , 'home' | filtered data |
You must return $template
even if not modifying. If this confuses you, look at examples where apply_filter()
has been used in code
You should not set up variables here for use later, there are better hooks for this.
A useful program flow for this filter is:
$template
includes our custom post type name --> template hierarchy!!There are two ways to reference the $wpdb
object. The first is to use the PHP keyword global
in order to act on the global instance of the object.
global $wpdb;
echo $wpdb->prefix;
// Outputs the prefix for the database
The second way to use the $wpdb
object is to reference PHP's $GLOBALS
super global variable.
echo $GLOBALS['wpdb']->prefix;
// This will also output the prefix for the database
The second way is discouraged as it may not be considered the best practice.
Wordpress Hooks
Something that often confuses developers when starting to work with WordPress are the use of apply_filters()
and add_action()
. You will often see plugins/themes making use of these in code and if you don't understand the concept, you will find it hard to work with them.
In brief (very brief, look up WordPress load flowchart for process in detail), WordPress loads in the following way:
If you are a developer and working with a functions file, you can see both are loaded earlier in the process than the files you are working with. Meaning you can't modify processes (note you cannot overwrite functions) or variables that run later or have not been defined yet. Also theme developers may place hooks in their code to allow plugins to hook to or plugins might allow for other plugins to overwrite their variables. Now this may be confusing thus far, but hang in there.
To understand add_filter()
and add_action()
we need to look at how the hooks are created in the first place.
$arga= 'hello';
do_action('im_a_hook', $arga );
When you encounter the above in WordPress, it will call any functions attached to the hook im_a_hook
(look up $wp_filter
for information on the process). In your attached function $arga
will be available for the attached function to work with.
add_action('im_a_hook', 'attached_function');
function attached_function($arga){
echo $arga;
}
This opens up powerful new opportunities to modify variables at certain points of the load process. Remember we said earlier that templates are loaded after plugins/ themes? One common plugin is WooCommerce which creates screens later in the process, I'm not going to document how but an example of do_action
can be found in the plugin.
do_action( 'woocommerce_after_add_to_cart_button' );
Here we have a hook created that passes no variables back, but we can still have fun with it:
add_action( 'woocommerce_after_add_to_cart_button', 'special_offer');
function special_offer(){
echo '<h1>Special Offer!</h1>;
}
The above add_action
will echo
a heading of special offer where do_action('woocommerce_after_add_to_cart_button')
is located which is when creating a WooCommerce screen. So we can use this hook to insert html. Other uses might include redirecting to a different screen altogether, etc.
Also multiple variables may be passed to the function. Try this in your themes functions. Note the last parameter we are setting to 3, because we want to work with the 3 available parameters. If we changed this to 2, only 2 would be returned and we would get a undefined error.
add_action('custom_hook', 'attached_function', 10, 3);
function attached_function($a,$b,$c){
var_dump($a);
var_dump($b);
var_dump($c);
}
$arga = 1;
$argb = 2;
$argc = 3;
do_action('custom_hook', $arga, $argb, $argc);
exit;
There is another WP hook type called a filter. A filter is different from an action in its usage, an action can only receive variables, obviously these variables are within the functions scope (you should know what php scope is, if not google). Filters pass back the returned data, so you can use to modify variables.
$filter_me= apply_filters('im_a_filter', $variable_to_filter);
Where you see the above, you can modify the value of $filter_me
as any data you return will be the value stored in the variable. So for example (note we are changing $variable_to_filter
to $filter_me
in the example):
add_filter('im_a_filter', 'attached_function', 100);
function attached_function($filter_me){
$filter_me= 'ray';
return $filter_me;
}
$filter_me = 'bob';
$filter_me= apply_filters('im_a_filter', $filter_me);
The $filter_me
variable will now contain 'ray' rather than 'bob', we have set a priority of 100 so we are reasonably confident no one is changing the value after use (there can be multiple filters running on the same hook) So we can now change variables used later in the process if apply_filters()
is present.
You can also pass multiple parameters, but you can only change the value of one. You must also return a value, or else your variable will contain nothing. If you understand how you use php to assign values/arrays/objects to variables this will be obvious to you, e.g.:
add_filter('im_a_filter', 'attached_function', 100, 3);
function attached_function($filter_me, $arga, $argb){
$filter_me= 'ray'.$arga.$argb;
$arga= 'you fool';
return $filter_me;
}
$filter_me = 'bob';
$arga = ' middlename';
$argb = ' surname';
$filter_me= apply_filters('im_a_filter', $filter_me, $arga, $argb);
The $filter_me
variable now contains 'ray middlename surname'. But what about $arga
? This still contains 'middlename', changing an $arga
to 'you fool' within our function has no effect on the defined value outside of its scope (there are ways, google globals etc.)
add_action($hook_name, $function, $priority, $parameters)
add_filter($hook_name, $function, $priority, $parameters);
The WordPress Admin Toolbar was added in version 3.1 and contains links to common administrative tasks as well as links to the user's profile and other WordPress information. However, many site owners dislike showing the toolbar by default to all logged-in users and/or want to add their own options to it.
Query arguments are numerous. WP_Query() codex page has a list of parameters. Some of them are
One of the most important thing to have in mind is:
query_posts()
overrides the main query, and can cause problems in the rest of your theme. Any time you need to modify the main query (or any query for that matter) is to use pre_get_posts filter. This will allow you to modify the query before it ran.
Also when you are querying posts, you should always reset it using wp_reset_postdata(). This will restore the global $post
variable of the main query loop, and you won't have any issues later on (such as categories being excluded, because in your secondary loop you've excluded them and forgot to reset the query).
If you are using PHP 5.3.0 or above, you can use closures (anonymous functions)
add_action( 'pre_get_posts', function( $query ) {
if( !$query->is_main_query() || is_admin() ) return;
// this code will run only if
// - this query is main query
// - and this is not admin screen
});
The next table shows you a list of elements that you can use inside of the first parameter (Array).
Parameter | Description |
---|---|
ID | (Int) The post ID. If equal to something other than 0, the post with that ID will be updated. Default 0. |
post_author | (Int) The ID of the user who added the post. Default is the current user ID. |
post_date | (String) The date of the post. Default is the current time. |
post_date_gmt | (String) The date of the post in the GMT timezone. Default is the value of $post_date. |
post_content | (Mixed) The post content. Default empty. |
post_content_filtered | (String) The filtered post content. Default empty. |
post_title | (String) The post title. Default empty. |
post_category | (Array) Array of post category values. |
post_excerpt (String) The post excerpt. Default empty. | |
post_status | (String) The post status. Default draft. |
post_type | (String) The post type. Default post. |
comment_status | (String) Whether the post can accept comments. Accepts open or closed. Default is the value of default_comment_status option. |
ping_status | (String) Whether the post can accept pings. Accepts open or closed. Default is the value of default_ping_status option. |
post_password | (String) The password to access the post. Default empty. |
post_name | (String) The post name or slug. Default is the sanitized post title when creating a new post. |
to_ping | (String) Space or carriage return-separated list of URLs to ping. Default empty. |
pinged | (String) Space or carriage return-separated list of URLs that have been pinged. Default empty. |
post_modified | (String) The date when the post was last modified. Default is the current time. |
post_modified_gmt | (String) The date when the post was last modified in the GMT timezone. Default is the current time. |
post_parent | (Int) Set this for the post it belongs to, if any. Default 0. |
menu_order | (Int) The order the post should be displayed in. Default 0. |
post_mime_type | (String) The mime type of the post. Default empty. |
guid | (String) Global Unique ID for referencing the post. Default empty. |
tax_input | (Array) Array of taxonomy terms keyed by their taxonomy name. Default empty. |
meta_input | (Array) Array of post meta values keyed by their post meta key. Default empty. |
When you execute this function, you could probably get a duplicated post, at least that happened to me. (You can check it into the Post WordPress Section)
I found a solution:
if( !get_page_by_title( $title, 'OBJECT', 'post' ) ){
$my_post = array('post_title' => $title,
'post_content' => 'Content',
'tags_input' => $tags,
'post_category' => array(2),
'post_status' => 'publish'
);
$result = wp_insert_post( $my_post );
}
Before you save a new post, validate if the new post already exists using the post title as a parameter, if there's not a post title, you can save your new post.
Check get_page_by_title's documentation here.
The following Post Formats are available for users to choose from, if the theme enables support for them.
Note that while the actual post content entry won't change, the theme can use this user choice to display the post differently based on the format chosen. For example, a theme could leave off the display of the title for a "Status" post. How things are displayed is entirely up to the theme, but here are some general guidelines.
The way Plugin hooks work is that at various times while WordPress is running, WordPress checks to see if any Plugins have registered functions to run at that time, and if so, the functions are run. These functions modify the default behavior of WordPress.
There are two kinds of hooks:
Filters give you the ability to change the value of a piece of data during the execution of WordPress. Callback functions for filters will be passed through a variable, modified, and then returned. They are meant to work in an isolated manner, and should never affect global variables or anything else outside of the function.
Actions, in contrast, allow you to add to or change how WordPress operates. Your callback function will run at a specific point in in the execution of WordPress, and can perform some kind of task, like echoing output to the user or inserting something into the database.
Security should be always in mind when developing. Without security an app is open to various attacks such as SQL Injections, XSS, CSRF, RFI etc that can lead to serious problems.
Untrusted data comes from many sources (users, third party sites, your own database!, ...) and all of it needs to be validated both on input and output. (Source: WordPress Codex)
The data should be validated, sanitized or escaped depending the use and the purpose.
To validate is to ensure the data you've requested of the user matches what they've submitted. (Source: WordPress Codex)
Sanitization is a bit more liberal of an approach to accepting user data. We can fall back to using these methods when there's a range of acceptable input. (Source: WordPress Codex)
To escape is to take the data you may already have and help secure it prior to rendering it for the end user. (Source: WordPress Codex)
Intended to improve site speed and safety.
I've been advertising that the use of a child theme is always a good thing but there is always a But ...
In our Template overwriting example let's imagine that the author of a theme is adding his own improvements to the sidebar template and there will be a new one at
/themes/template/sidebar.php
<?php
/**
* The template for the sidebar containing the main widget area
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*/
if ( is_active_sidebar( 'sidebar-1' ) ) : ?>
<aside id="secondary" class="sidebar widget-area" role="complementary">
<?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside><!-- .sidebar .widget-area -->
<?php endif; ?>
Now our website won't benefit from the new role="complementary"
spec because our child theme is still overwriting the template with its own file at
/themes/child-theme/sidebar.php
It is our duty as website maintainers to keep a track about what templates do we overwrite and, in the imminent case of an update, look carefully at the changelog so you update the child theme files if necessary.
IMPORTANT – Don’t use camelCase or UPPER-CASE for your attributes
You can generate a shortcode with attribute Here
Argument options are:
E_USER_NOTICE
messages in debug mode, starting with version 4.2.sidebar
will be prepended to the class value. For example, a class of tal
will result in a class value of sidebar-tal
. (default: empty).<li id="%1$s" class="widget %2$s">
) Note: uses sprintf
for variable substitution</li>\n
).<h2 class="widgettitle">
).</h2>\n
).Security should be always in mind when developing. Without security an app is open to various attacks such as SQL Injections, XSS, CSRF, RFI etc that can lead to serious problems.
Untrusted data comes from many sources (users, third party sites, your own database!, ...) and all of it needs to be validated both on input and output. (Sourse: WordPress Codex)
The data should be validated, sanitized or escaped depending the use and the purpose.
To validate is to ensure the data you've requested of the user matches what they've submitted. (Sourse: WordPress Codex)
Sanitization is a bit more liberal of an approach to accepting user data. We can fall back to using these methods when there's a range of acceptable input. (Sourse: Wordpress Codex)
To escape is to take the data you may already have and help secure it prior to rendering it for the end user. (Sourse: WordPress Codex)
init
is an action hook that gets fired after WordPress has finished loading but before any HTTP headers are sent.
The add_action()
function creates an Action Hook, associating a PHP function with a particular action "tag" or name. When the action is "triggered" by a call to do_action()
(or do_action_ref_array()
) with a specific tag, all functions "hooked" to that tag will be executed.
In most cases, this function should be used in a theme's functions.php
file or a plugin file - or another source file loaded by either.
This function is a part of the Plugin API
WordPress websites are frequently hacked. This topic is for techniques and practices that increase the security of your WordPress installation beyond what is achieved in a base install.
Apart from this topic, another good place to read about securing a WordPress installation is the Hardening WordPress Codex page.
The Options API is a simple and standardized way of working with data staored in the options table of MySQL database. The API makes it easy to create, read, update, and delete options.
Here is a list of the default positions (for $position)
Here are a list of slugs for $parent_slug
List of arguments for $option
For the parameter $leavename, it is false by default.
Please note that get_the_category() returns an array, which means that you can't directly echo the value returned.
Here is a list of objects of each category that you can print:
If you plan to get the title of a post or page using a post loop, it is suggested to use the_title() instead.
List of features to be used in $feature:
The content inside the render meta box can be anything. Instead of the values being directly integrated, you can also use an include
with a PHP template and use set_query_var
method to pass data to it. The save would work the same way.
These must be executed directly in an include file. Whether it is in functions.php or in another include file, these cannot be wrapped in a hook. They won't work on init or any other I have found so far.
They can also be included directly in a template like page.php to execute only for that template.
NOTE: DO NOT INCLUDE THIS IN A DISTRIBUTED THEME OR PLUGIN (unless it is disabled by default, like not including the include file it's in unless the user specifies).
This is bad practice to include in a site you don't control because it can and will break the output of any other themes or plugins.
get_home_path()
and ABSTPATH
Please keep in mind the difference between ABSPATH
and get_home_path()
if you have WordPress installed in a subfolder.
The get_home_path()
function will always return a path without the subfolder:
This is how it differs from ABSPATH
, which will return different values:
ABSPATH
is first defined in wp-load.php
which will be located at /var/www/htdocs/example/wp/wp-load.php
hence this is where ABSPATH
will take its definition from.
get_home_path()
checks if the site_url
and home_url
differ, and removes the substring from the path. Otherwise it returns ABSPATH
value:
function get_home_path() {
$home = set_url_scheme( get_option( 'home' ), 'http' );
$siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' );
if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) {
$wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */
$pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) );
$home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos );
$home_path = trailingslashit( $home_path );
} else {
$home_path = ABSPATH;
}
return str_replace( '\\', '/', $home_path );
}
Calling get_home_path()
must be done in a context where wp-admin/includes/file.php
has already been included.
For example using get_home_path()
within the admin_init
hook is fine, but using it within the init
is not and will result in a PHP fatal error:
Call to undefined function get_home_path()
This file only gets included from within the admin (dashboard) context, if you absolutely need it outside of this context you will need to include the file yourself before calling the function:
require_once(ABSPATH . 'wp-admin/includes/file.php');
To get this WordPress REST API simple example to work for you, you need to learn how it works in more detail. Official documentation recommends learning about:
Routes/Endpoints - which are mappings of individual HTTP methods to routes known as "endpoints" - you do it using register_rest_route() function, and here you can find more about Routes and Endpoints.
Requests - WordPress REST API defines WP_REST_Request
class which is used to store and retrieve information for the current request. WP_REST_Request
objects are automatically generated for you whenever you make an HTTP request to a registered route. The data specified in the request will determine what response you get back out of the API. Here can learn more about the WP_REST_Request class.
Responses - are the data you get back from the API. The WP_REST_Response
provides a way to interact with the response data returned by endpoints. In your endpoint definition you name the callback (response) function to serve your interaction.
Here can learn more about the WP_REST_Response class.
Schema - Each endpoint requires and provides slightly different data structures, and those structures are defined in the API Schema. If you want maintainable, discoverable, and easily extensible endpoints it is recommended to use the schema. Here you can learn more about the Schema.
Controller Classes - they bring all elements together in a single place. With a controller class you can manage the registration of routes & endpoints, handle requests, utilize schema, and generate API responses. You have already learned about two controller classes: WP_REST_Request
and WP_REST_Response
. Here you can learn more about the Controller Classes
Note: Some of this information is taken from the official Wordpress REST APi Handbook