On WordPress

2024-07-25

You know it. WordPress. WordPress is everywhere. Around a half of all the websites are running on WordPress. Impressive, isn't it?

WordPress is everywhere

Why is WordPress so popular?

It's simple. All you need to start with WordPress is a classic LAMP server. Definitely, Linux. Apache, or, in a modern environment, Nginx. Actually, any web server which is able to run PHP. MySQL, or MariaDB. WordPress still works only with MySQL/MariaDB, other databases are not supported. And PHP.

PHP is the core, the base, the bones of the WordPress. The modern WP version, 6.5, released in 2024, supports relatively old PHP version 7.4, released in 2019.

It's absolutely not a problem to get your own WordPress server. Nowadays, you can do it just with some clicks on any hosting platform. And all the details and nuances how to run PHP, PHP-FPM, or FastCGI, or Apache mod_php module, are hidden.

PHP is a very simple but powerful language. It's a language created for the Web. So, its main execution flow is still to process HTTP requests. Start the interpreter. Parse the request headers and parameters. Load the script from a disk, and all its includes and requires. Execute the script, including interactions with the database. Generate, typically HTML, output. All these steps are performed per each HTTP request. Nowadays, optimised and cached, so the performance is not so low as you may expect.

PHP is a functional language. From its childhood you may pass functions or methods to other functions and methods and call them there. This is widely used in WordPress as hooks. A reference to a function is just a string with the function name.

An example from the tutorial:

<?php
function exclaim($str) {
  return $str . "! ";       // the dot (.) is the string concatenation operator
}

function ask($str) {
  return $str . "? ";
}

function printFormatted($str, $format) {
  // Calling the $format callback function
  echo $format($str);
}

// Pass "exclaim" and "ask" as callback functions to printFormatted()
printFormatted("Hello world", "exclaim");   // Hello world!
printFormatted("Hello world", "ask");       // Hello world?
?>

And you still need to write the <?php markers as .php files are still considered as templates where the content is printed to output as is by default, except <?php...?> parts which are the PHP code snippets.

PHP is an OOP language. You can define classes and create new instances with the new operator.

<?php
class Fruit {
  public $name;
  function set_name($name) {
    $this->name = $name;
  }
}
$apple = new Fruit();
$apple->set_name("Apple");

echo $apple->name;
?>

WordPress initially was not any content management system but a blogging engine. So, out-of-the-box you can create only blog posts there.

But the power of WordPress comes from its plugins. Probably, the most known plugin is WooCommerce. It converts the WordPress blogging platform into a fully functional e-commerce site. With post-products, post-orders, etc... There are still posts in the database, but with additional metadata they can represent your e-shop products and users' orders. There are even plugins for WooCommerce to extend already its functionality.

WordPress' plugins are just some additional PHP code, classes and functions, which is included into HTTP requests processing flow. You can do mostly everything, what you can do with PHP, there. But plugins can and should go upper and utilise WordPress API, which most powerful part is hooks.

A Filter hook is a callback which can modify the passed value. Somewhere in the WordPress core code or in a plugin a developer wants a value to be defined or changed externally. So, they define a place where the filter is called. And the filter itself can be defined elsewhere, in another plugin. Even multiple filter callbacks can be defined, they are applied in a chain in this case.

For example, somewhere in wp_mail() function which is used to send any emails from WordPress there are definitions of these filters:

<?php
$from_email = apply_filters( 'wp_mail_from', $from_email );
$from_name = apply_filters( 'wp_mail_from_name', $from_name );

The developers of wp_mail() rightly decided the email address and the name of the sender of all emails can be changed depending on the context. They added the extension points represented by the WordPress filters. There's a default sender address like wordpress@yourdomain.net. But there is no UI to change it.

But you can create a plugin to change the sender address and name. You must add filters in the plugin to change the behaviour of wp_mail():

<?php
add_filter('wp_mail_from', 'wpces_mail_sender_from_email');
function wpces_mail_sender_from_email($old) {
    $wpces_sender_email_address_value = get_option('wpces_sender_email_address');
    return $wpces_sender_email_address_value;
}

add_filter('wp_mail_from_name', 'wpces_mail_sender_from_email_name');
function wpces_mail_sender_from_email_name($old) {
    $wpces_email_sender_name_value = get_option('wpces_email_sender_name');
    return $wpces_email_sender_name_value;
}

Here the WordPress' Options API is utilised. get_option() function is used to retrieve the previously stored option value.

There's still another problem with the email sender address. These filters define the sender in the email headers. But also there is the MAIL FROM address in so-called SMTP envelope. And it's better to change this address also, for anti-spam filters to handle our emails better.

We need to interact with WordPress' underlying emailing library called PHPMailer. And the WordPress developers added another hook into wp_mail() function for us:

<?php
do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );

Here the Action hook is called. The action is just an action. Its return value is ignored. But here the action callback receives a reference to the PHPMailer instance, and it can change its state.

For example, in our imaginary plugin:

<?php
add_action( 'phpmailer_init', 'wpces_phpmailer_init' );
function wpces_phpmailer_init( $phpmailer ) {
    $address = get_option( 'wpces_sender_email_address' );
    if ( $address ) {
        $phpmailer->Sender = esc_html( $address );
    }
}

The WordPress plugins and hooks are very powerful. Every useful piece of functionality in WordPress world exists as a plugin. There are plugins for everything. There are actions and filters that you may extend everywhere. You see, even changing of the email sender is done as a plugin, it's not a core feature.

But this flexibility has a cost. There's no validation for the hooks. There's even no validation for plugin dependencies. Yes, sometimes one plugin may require a presence of other plugins, and it can be validated only by the plugin itself.

This makes WordPress very fragile. Any new plugin added to the installation may break everything. Any new plugin may break the performance of the whole site. The database schema is not optimal, everything is just posts and metadata. The database queries are not optimal.

The hooks of the WordPress core are well-documented. But the hooks provided by the plugins, in many cases you may discover them from the source code only. And you may take the source code only after you pay for the plugin. Thanks to PHP, the interpreting language, the source code is always available.

WordPress is good as a fast start. But if you have to use, say, more than five plugins... Maybe you need to develop something more specific for your case.