Category: PHP

  • Do the slow work after the page is sent: WordPress’s shutdown hook

    Last month I added a search log to my WordPress plugin: every time the AI layer surfaced a product that plain keyword matching had missed, I wanted a row in a table saying so. Useful feature. Also a great way to make every single search on someone’s store slower, if you write it the obvious way.

    The fix is a WordPress hook that does not get talked about enough: shutdown.

    The problem with the obvious version

    You are already inside the search filter, you have the data, so you write the row there:

    It works. It is also bookkeeping that the shopper is paying for with their time. One INSERT is a millisecond or two, which sounds like nothing until you remember that this runs on every search, that the database might be on another host, and that your “one small write” has a way of becoming three writes and a lookup six months later.

    The visitor needs the search results. They do not need your analytics.

    What the shutdown hook actually is

    shutdown is the last action WordPress fires. Core registers it as a PHP shutdown function, so it runs at the very end of the request – after the template has rendered, after the response has been handed off. It fires on normal page loads, on AJAX, on REST requests, and even when something calls wp_die().

    That makes it the natural home for work that must happen during a request but that nobody is waiting for.

    Why it feels instant: fastcgi_finish_request()

    On PHP-FPM – which is what most decent hosting runs – PHP can tell the web server “I am done, send it” and keep executing. WordPress core and various plugins call fastcgi_finish_request() at the end of a request, so by the time your shutdown callback runs, the browser has already received the page and started rendering it.

    Be honest with yourself about what that means, though. The visitor is not waiting, but the PHP worker still is. You have moved the cost off the perceived page load and onto your pool of workers. That is a very good trade for a single INSERT. It is a terrible trade for something that takes two seconds, because under traffic you will run out of workers and the queue will bite you in a much less obvious way.

    On old mod_php or CGI setups the connection may not be closed early at all, so the technique degrades to “same total time, tidier code”. Nothing breaks – it just stops being a performance win.

    The pattern, in three moves

    This is the actual code from AI Search 1.31.0, trimmed for reading. First, register the hook where you register the others:

    Second, during the request you only collect. No queries, no writes – just put what you know on the object and carry on:

    Third, the callback that actually writes. Note the early return and the null: this thing must be safe to call when there is nothing to do, and safe to call twice.

    That is the whole trick. The search path does zero database work for logging; the row still lands on every relevant request; and the shopper never waits for it.

    When you should not reach for it

    • Anything the response depends on. Obvious when written down, easy to forget at 11pm. If the page needs the result, it cannot happen after the page.
    • Anything slow. Calling an external API, resizing images, sending mail through a slow SMTP server – all of these hold a worker hostage. That is what WP-Cron, or a real queue, is for.
    • Anything that needs to output. Headers are long gone. No redirects, no cookies, no printing. If you echo something here you will either see it glued to the end of your page or nowhere at all.
    • Anything that must not be lost. A fatal error, a killed worker or an aggressive timeout can end the request before your callback runs. For a search log that is fine. For an order, it is not.

    Small gotchas worth knowing

    $wpdb is still perfectly alive at this point, so queries work normally. Object cache writes, on the other hand, are mostly pointless: nothing else in this request will read them. And if you use a persistent object cache, remember that other requests may already be serving cached output that does not know about your write – so do not use shutdown to fix up something the current page displayed incorrectly.

    Also, keep the callback idempotent. In unusual flows (some AJAX handlers, some error paths) you can end up with the shutdown sequence running more than once. Nulling the pending payload after reading it, as above, is the cheapest possible insurance.

    The result in practice

    In AI Search this powers the Insights tab: store owners can see which searches the AI actually rescued – the queries where semantic matching surfaced products that keyword matching had missed – without the logging costing their shoppers anything. Same feature, same data, moved a few milliseconds to the right in the request lifecycle.

    It is a small technique. But “does this actually need to block the response?” is a question worth asking about half the code in a typical plugin, and shutdown is the tool for the times when the answer is no.

  • Everyday Efficiency: PHP 8 Techniques for WordPress Engineers

    As a software engineer navigating the daily challenges of WordPress development, embracing the latest tools and tricks is essential to staying efficient and effective. In this post, I want to share three PHP 8 tricks I incorporate into my everyday tasks as a software engineer working on WordPress projects.

    When PHP 8.0 was released, I wrote about my favourite improvements here, and you’ll see some of them in this post – however, this time those techniques are part of my day as an engineer.

    These tricks aren’t just about staying up-to-date; they’re practical enhancements that make my coding life easier and my WordPress development more streamlined. Let’s dive in!

    Easier Null Handling with “?->” Operator


    In PHP 8, there’s a cool trick called the Nullsafe Operator (?->). It helps when you’re dealing with nested objects and saves you from writing lots of null-checks. Check out this example:

    // classic one
    if ($user !== null && $user->getProfile() !== null) {
       $avatar = $user->getProfile()->getAvatar();
    } else {
       $avatar = 'default.jpg';
    }
    // With PHP 8 Nullsafe Operator
    $avatar = $user?->getProfile()?->getAvatar() ?? 'default.jpg';

    This makes your code shorter and easier to read, especially when working with WordPress objects.

    Cleaner Conditionals with “match” Instead of “switch”


    PHP 8 has a new way to handle conditions using the match expression. It’s like the old switch statement but more concise. Look at this:

    
    // Traditional switch
    switch ($status) {
    case 'published':
       $message = 'Post is live!';
       break;
    case 'draft':
       $message = 'Post is still in draft.';
       break;
    default:
       $message = 'Post status unknown.';
    }
    // PHP 8 match expression
    $message = match( $status ) {
       'published' => 'Post is live!',
       'draft'     => 'Post is still in draft.',
       'default'   => 'Post status unknown.',
    };

    Personally, I love this one! When you avoid overusing If/else statements in your code, matching labels with indexes is always a great solution – even better when you can match the index with the actual status.

    Constructor Property Promotion for Cleaner Class Definitions:

    PHP 8 introduces a streamlined way to define classes with the Constructor Property Promotion feature. This feature is a game-changer for WordPress developers, allowing for more concise and readable class definitions. It’s particularly useful when creating custom components like widgets or API responses. Instead of declaring class properties and then assigning them in the constructor, PHP 8 allows you to do both simultaneously, thereby reducing boilerplate code.

    For example, consider a custom widget in a WordPress theme:

    // PHP 7 style
    class CustomWidget {
        private $title;
        private $content;
    
        public function __construct($title, $content) {
            $this->title = $title;
            $this->content = $content;
        }
    }
    
    // PHP 8 Constructor Property Promotion
    class CustomWidget {
        public function __construct(
            private $title, 
            private $content
        ) {}
    }

    This approach makes the code significantly more concise and enhances readability, which is essential in complex WordPress projects.

    Named Arguments for More Readable Function Calls

    PHP 8’s Named Arguments feature significantly enhances the readability of function calls, especially in WordPress where functions often come with multiple parameters. This feature allows developers to pass arguments to a function based on their names, rather than their position, making the code more understandable and less prone to errors related to the ordering of arguments.

    Again, considering a WordPress function call scenario:

    // PHP 7 positional arguments
    add_theme_support('custom-background', array(
        'default-color' => '000000',
        'default-image' => '',
    ));
    
    // PHP 8 named arguments
    add_theme_support('custom-background', default-color: '000000', default-image: '');
    
    // Another example with WP_Query
    $query = new WP_Query(array(
        'post_type' => 'post',
        'posts_per_page' => 10,
        'orderby' => 'title',
        'order' => 'ASC'
    ));
    
    // With PHP 8 named arguments
    $query = new WP_Query(
        post_type: 'post',
        posts_per_page: 10,
        orderby: 'title',
        order: 'ASC'
    );

    Using named arguments not only clarifies the purpose of each parameter but also makes the code more flexible for future changes, an invaluable asset in the evolving landscape of WordPress development.

    More Control with Union Types

    PHP 8 introduces something called union types. It’s a way to say a variable can be one of several types. This is handy when working with functions that can take different kinds of data:


    // Without union types
    function processUserData($data) {
    // Function logic here
    }
    // With union types
    function processUserData( array|string $data ) {
    // Function logic here
    }


    By using union types, you can be clearer about what kind of data your functions expect, making your WordPress code more robust.

    Is WordPress core already prepared for PHP 8?

    It’s very important that you run your local PHP environment as similar as possible to your project’s final server, because although WP core is compatible with PHP, you’ll see that officially the stable version continues to be 7.4. But my advice: run at least version 8.1 🙂

    Conclusion

    These PHP 8 tricks might seem small, but they can make a big difference in your WordPress development. The Nullsafe Operator, Match Expression, and Union Types are easy to understand and implement. By using these tricks, you’ll not only stay current but also write code that’s simpler, cleaner, and more effective in your WordPress projects.

    All these details will reveal that you’re proud of your work. In a world where >40% of the Internet potentially can run your code, keep in mind you have the mission to create a web that is being designed to last.

  • PHP 8.0 – 4 new features in this latest version

    PHP 8.0 – 4 new features in this latest version

    New PHP major versions are always very expected by PHP developers, we already know good things coming when a new release is announced. This time we have a lot of performance improvements and new features that improve our code, using modern programming methods in a badass old-school language.

    Besides those new features and recommendations, I strongly recommend we use them only for environments that we can expect and have sure that 8.0 is running. In some cases, we can use polyfills instead. If you’re a plugin developer, it’s too soon to start using the new features of this version.

    Nullsafe operator

    I really love clean and efficient code, so I’m not a fan of the classic if/else statements (why we shouldn’t use them? Read more here). However, there is always a time when we need to use a simple if/then, and the best way to do that is using an inline if statement Like this:

     $today_exists = time() ? true : false;

    Clean, useful and works like a charm. I can’t know how many times I’ve tried to check a null value of a class method (for example), giving a “then” statement in the same line. Simple – it wasn’t possible, and the solution was based into split the statement into two or more lines.

    The good news is called Nullsafe operator. PHP 8 now provides a null validation without any errors, so the following example now is pretty possible:

    $today_exists = $this->get_time()?->$this->is_true();

    str_contains() function

    With this new function, we no longer need to use the strpos() function. This last one is more recognized by developers to be used to verify if a string contains a word, like this:

    $today_exists = ( false !== strpos( 'Today is the day', 'Today' ) );

    Yes, that’s what we had to do to check if a string contains X word 🙂

    Checking the documentation, it’s really strange to see str_contains() only is available from 8.0. In this link, you can also find a pollyfill alternative.

    New match() expression

    That’s a good one for clean code. Imagine we have a switch case expression, but optimized to be used without breaks, and organized as a class sintax. I’m particullary interested into starting to use this new approach, check some examples in the documentation.

    $food = 'cake';
    
    $return_value = match ($food) {
        'apple' => 'This food is an apple',
        'bar' => 'This food is a bar',
        'cake' => 'This food is a cake',
    };
    
    var_dump($return_value);

    Enums

    Exactly like other languages (e.g. javascript), enums provides us to call a function and have a definite number of cases on the args. The result is a more stable code. This one will be available in the 8.1 release only, but it’s a great example of what it’s happening with PHP – giving to the developers great native features we can use like a modern programming language.

    I’m looking forward to use these features on my code, and I’m confident that will be possible soon – PHP 8.0 has been launched in November 2020, so please read more about WordPress and 8.0 compatibility before starting to code.