Software Engineering in WordPress, PHP, and Backend Development

Tag: PHP (Page 2 of 12)

Use Custom Functions to Determine Which Environment You’re Running Your Code

WordPress provides constants and functions for setting up and checking to see if we’re in development mode.

Specifically, see the following:

But if you’re in a situation in which you’re working on code that’s, say, WordPress-adjacent where it both talks to third-party APIs and services but also sends data to WordPress (or reads data from WordPress), it may be helpful to have another way to determine if your code is running on a local machine.

Use Custom Functions

Case in Point

Perhaps you’re going to be registering a JavaScript file that requires a dependencies that is not available on your local machine. If this is the case, you have a few options:

  1. Find all of the dependencies from the production application and add them to your local machine ignoring whatever side effects may happen,
  2. Remove a call to the dependencies in your code and try to remember to add it back before committing to source code,
  3. Or use a a drop-in function for checking if you’re running the code locally.

Though I know each option has its own tradeoffs, I find myself doing the third option more often than not. All it requires is evaluating the loopback interface and seeing where the script is running.

In the context of PHP, a loopback interface refers to the ability of a script or a server to send requests to itself. So if you check to see if it’s running on 127.0.0.1, which is an IPv4 variation of localhost or ::1 which is the IPv6 variation of localhost then you can determine how to move foward.

A Simple Example

Here’s my example function:

public function isLocalDevelopmentEnvironment()
{
    return (
        0 === strcasecmp('::1', $_SERVER['REMOTE_ADDR']) ||
        0 === strcasecmp('127.0.0.1', $_SERVER['REMOTE_ADDR'])
    );
}

Then if you want to use this when, say, enqueuing JavaScript sources in WordPress, you can set up a conditional for dependencies like this:

$dependencies = ['jquery', 'acme-scripts'];
if ($this->isLocalDevelopmentEnvironment()) {
    $dependencies = ['jquery'];
}

Then you can enqueue the scripts like this:

wp_enqueue_script(
    'acme',
    $this->jsDir . 'omega.js',
    $dependencies,
    \filemtime(__FILE__),
    true
);

Sure, it’s a simple example but there’s much more that can be done with that single helper function regardless of if you’re working with WordPress, Google Cloud Functions, cloud databases, or more.

There are, of course, much more complicated ways in which custom functions can be written and used. The point isn’t about complexity, though. It’s about thinking through when utility functions like this are helpful and when they should be used.

How You Can Use Ray to Debug Vanilla PHP Utilities

There’s a lot to be said for using a traditional debugger such as Xdebug (and I’m still a fan), but lately, I’ve been writing more generalized PHP utilities that have no front-end and aren’t plugged into any specific application.

That is, I’m using native PHP libraries to run queries against incoming data and help analyze certain aspects of it and output a report.

There’s been a learning curve for a lot of it, but part of the process has included plenty of debugging. And if you’ve read my series on Ray in WordPress, then you know I’m a fan of Ray.

So here’s how you can use Ray to debug vanilla PHP utilities.


In your composer.json file, make sure the following directive is present:

"require-dev": {
	"spatie/ray": "^1.29.0"
},

Then in the terminal, run the following:

$ composer update

You may need to run $ composer install if you haven’t already, but I’m assuming you’re adding to the file you already have set up.

After that’s done, make sure the following is in your PHP code:

<?php

require 'vendor/autoload.php';

// ...

use Spatie/Ray;

At this point, add the following line just to make sure Ray is loading (and make sure the Ray application is actually running). Add the following line:

ray('Loaded...');

Then in your terminal run $ php script.php. Assuming all has gone well, you should see the following:

From there, you can use the rest of the tools outlined in the Ray for WordPress series for doing whatever it is you want. Except at this point, you’re just running Ray for debug vanilla PHP utilities.

New context, same functions. Easy enough!

How To Set PHP Version Parity in Your Shell and in Laravel

As much as I enjoy working with Laravel Valet and using Homebrew to manage different versions of PHP, there are still times in which my system has been set up in such a way that my shell is using one version of PHP and Laravel is using a different version.

At the time of this post, I see the following whenever which I run $ which php:

/opt/homebrew/opt/php@7.4/bin/php

And when I run $ php -v, I’ll see the following output:

PHP 7.4.33 (cli) (built: Jul 13 2023 17:41:11) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Xdebug v3.1.2, Copyright (c) 2002-2021, by Derick Rethans
    with Zend OPcache v7.4.33, Copyright (c), by Zend Technologies

But if I was to run phpinfo(); within a page hosted via Laravel, I’d get PHP8. This is problematic because if I’m working on a function that I’m eventually going to incorporate in an app that runs in the browser, there may be functionality that doesn’t work as I expect.

As such, it’s important to make sure we have parity between Laravel and our system’s binary. To do this, issue the following command in the terminal:

$ valet use php@7.4
$ valet restart

Obviously, you’ll change the value of php@7.4 to whatever you want to use on your machine. Whatever it is, though, make sure there’s parity between the two. This way, whenever you run a script independently, then you’ll have the same results in the browser as you do in your terminal.

References

Is It Worth Using ChatGPT to Help Secure Source Code?

To cut straight to the point, I’m not going to say we need to rely on AI tools to help us to write truly secure code. This isn’t to say we can use tools like ChatGPT to help secure source code, but given how these utilities are trained via their LLMs, there’s only so much they can provide. In short, don’t short change security analysts who are, by nature, trained in this very thing.

That said, I’ve been using tools such as ChatGPT and other AI developer tools to help make recommendations on making code more secure. I’ll give a basic example of what I provided, what it produced, and my thoughts on it.

Note this is but a single, isolated, and simple case so there’s only so much that it can recommend. The bottom line, for me, is not so much the code it recommended but how it offered to change that I’d originally written (and then researching the why it opted to use certain function calls over others).


The Intent of the Code

In the function I’m going to share, the purpose is to look at data in the REQUEST_URI of the $_SERVER array, separate the components of the URI, and then analyze them to determine what best course of action to take.

Namely, do we break it a part and redirect to another page or do we duck out of the functionality because it’s malformed or doesn’t fit with the scheme of the rewrite rule I have in place.

The Code

The Initial Function

I wrote the initial function like this:

add_action('template_redirect', function() {
  $requestUri = $_SERVER['REQUEST_URI'];
  $requestUriParts = array_filter(explode('/', $requestUri));

  if (
    count($requestUriParts) === 2 && 
    $requestUriParts[0] === 'report' && 
    is_numeric($requestUriParts[1])) {
        $userId = intval($requestUriParts[1]);
        include plugin_dir_path(__FILE__) . 'templates/report.php';
        exit;
    }
});

Granted, I made some concessions to keep it relatively simple. That is, there are no exception handling and there are no conditionals to handle the case when indexes of the array are invalid.

But part of this is to see just how much AI can improve the code above. They key things to take away, despite it not handling situations outside of the conditional, are:

  • I’m verifying the array has the proper number of parts,
  • The first index of the array is report,
  • The second index of the array is an integer,
  • And if all of the above is true, then we’ll redirect to a custom template.

Again, there are some immediate issues that you can spot with this (such as the case of strings, the range of integers for user IDs, and if the file even exists.

But I digress. This is what I fed into ChatGPT to help secure source code.

The Secured Code

Given the code above, this is what ChatGPT produced:

add_action('template_redirect', function () {
    $requestUri = filter_var($_SERVER['REQUEST_URI'], FILTER_SANITIZE_URL);
    $requestUriParts = array_values(array_filter(explode('/', $requestUri)));

    if (
        count($requestUriParts) === 2 &&
        strtolower($requestUriParts[0]) === 'report' &&
        ctype_digit($requestUriParts[1])
    ) {
        $userId = intval($requestUriParts[1]);

        // Validate $userId and ensure it's within an appropriate range
        if ($userId > 0 && $userId <= 1000000) { // Adjust the upper limit as needed
            $templatePath = plugin_dir_path(__FILE__) . 'templates/report.php';

            // Check if the template file exists before including it
            if (file_exists($templatePath)) {
                include $templatePath;
                exit;
            } else {
                // Handle the case where the template file is missing
                die('Report template not found.');
            }
        } else {
            // Handle invalid user IDs
            die('Invalid user ID.');
        }
    } else {
        // Handle invalid URLs
        die('Invalid URL.');
    }
});

Some of the things that immediately jump out at me in terms of improving the code are:

  • FILTER_SANITIZE_URL
  • strtolower on the report component of the URL
  • ctype_digit rather than is_numeric
  • Verifying a proper range of user IDs
  • Verifying the template file exists
  • Handling each case where it could fail

Now in terms of security, I don’t know where this would fall given that it’s not writing or reading data so much as sanitizing and validating it before redirecting a user to a page that should exist.

But I did like the steps that it took as they are things that we should be implementing naturally as engineers. Namely, sanitizing URL, verifying files exists, and making sure ID ranges are acceptable.

This is what struck me as the most interesting part though:

  • is_numeric. Determines if the given variable is a number or a numeric string. (A PHP string is considered numeric if it can be interpreted as an int or a float.).
  • ctype_digit. Checks if all of the characters in the provided string, text, are numerical.

Given the definitions above, we can verify that is_numeric(-5) would return true where ctype_digit(-5) would return false. Further, is_numeric(5.5) will be true and ctype_digit(5.5) will be false. This is important, especially when you’re working with non-negative whole numbers such as those that represent user IDs in a system such as WordPress.


I’m not recommending writing lazy code (like my example code above 🙃), feeding it into an AI system, and letting it do work for you. But if you’ve written something as strong and secure as you believe you can, then feeding that to an AI makes more sense as it can help take you a little further. And if you have a security analyst on your team, don’t hesitate to reach out to them for a code review.

For all the talk of AI replacing humans, we’re there yet – not in this field. But that’s not a discussion I care to have right now. If nothing else, using AI tools such as GitHub Copilot and ChatGPT to help secure source code isn’t a bad idea, but it’s not the best idea and it doesn’t replace someone who’s on your team. AI is going to be truly limited by its contextual knowledge of the environment and constraints of the system.

If anything, perhaps they are code assistants and nothing more.

Sodium Compat: PHP Sodium Functionality via Composer

If you’ve worked with PHP with any length of time and needed to use some type of built-in encryption, you’ve likely seen something about the Sodium library in the manual.

Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. Its goal is to provide all of the core operations needed to build higher-level cryptographic tools.

Unfortunately, the module that contains this library isn’t always installed with the PHP binary. It then has to be either re-compiled or enabled by a package manager. If you don’t have the ability, time, or access to do any of those, then there’s a solid alternative for the native library that can be installed via Composer: Sodium Compat.

Continue reading
« Older posts Newer posts »

© 2025 Tom McFarlin

Theme by Anders NorenUp ↑