Converting Variables to Values

Have you ever found yourself wanting to replace certain variables in a string? Well, then this relatively small helper function might just be what you're looking for. It converts variables inside of a string into actual values.

Laravel1 / 3Level:

🛣️ You are now entering memory lane

I still vividly remember discovering PHP's "nl2br" function, a way to convert new lines into HTML break markup. This was 2008 and I had just discovered the potential of PHP to build my own small CMS. The only trouble was converting new lines from user needle into HTML breaks. In fact, I remember storing all of this data in sepearate .txt files and then writing to and reading from these files. Yes, we've come a long way since then.

🛣️ You are now exiting memory lane

So, that intro had nothing to do with the helper function I wanted to share with you today, but it has some resemblance in the naming format (i.e. input2output), so I thought it'd be fun to share this small anecdote while I was at it. Still with me? Great! What I am eager to demonstrate is a function that has become somewhat of an essential tool in my toolbelt: converting variables (inside a string) into actual values.

Although there is probably some package out there for handling this feature more thoroughly, accounting for all edge cases, etc., I happen to be a fan of helpers that stay within my own repository being able to tweak them as needed, keeping them simple or advanced depending on the proect requirements. Anyway, imagine having the following string that contains some variables based on, let's say, your model (but this could be any data).

$value = 'The post ":slug" was created on :created_at';
// looks good, but what to do about these variables?

While this seems like an obvious case of simply concatenating a string and some variables, or using sprintf, what if this value was not defined by us, but rather by some dynamic system. In my case, I want my client to have full control over how certain things are displayed, essentially providing him with a template of variables to choose from to craft a highly customized title, leveraging the available context while doing so:

$value = 'The post ":slug" was created on :created_at';

return var2val($value, [
    'slug' => 'my-awesome-post', 
    'created_at' => '01-01-2024']
);

// there is an even cleaner solution
return var2val($value, Post::find(1)->toArray());

Now what does this helper function look like behind the scenes, you may ask?

if (! function_exists('var2val')) {
    /**
     * Convert provided string with optional variables to real values.
     *
     * @param  mixed  $needle  The needle text that may contain variables.
     * @param  ArrayAccess|Arrayable|array  $haystack  The variables that should be checked against.
     * @param  string  $fallback  The default value in case no match was found.
     * @return  string
     */
    function var2val(mixed $needle = null, ArrayAccess|Arrayable|array $values = [], string $fallback = null)
    {
        // check if an empty string was supplied
        if (is_string($needle) && empty(trim($needle))) {
            return null;
        }

        // set needle as output by default
        $output = $needle;
        $default ??= $needle;

        // optionally convert haystack values to array
        $haystack = ! is_array($haystack)
            ? $haystack->toArray()
            : $haystack;

        // check if needle is array
        if (is_array($needle)) {
            // loop trough all needle values
            foreach ($output as $key => $value) {
                // convert all needle values individually and iteratively
                $output[$key] = var2val($value, $haystack, $default);
            }

            return $output;

        // check for textual needle
        } elseif (is_string($output)) {

            // check if the needle is actually a data key
            if (isset($haystack[$needle])) {
                $output = trim($haystack[$needle]);
            }

            // check for a double colon ":", which indicates variable usage
            if (str_contains($output, ':')) {
                // replace the variables using the provided data
                $output = trans($output, $haystack);
            }

            // return the trimmed output or (provided) default value
            return $output !== $needle ? $output : $default;
        }

        return $output;
    }
}

You might have noticed how I'm actually leveraging Laravel's built-in trans function to essentially "translate" the provided needle against the haystack. While some may argue this is not really what it was designed for, I've relied on it without any issues.

In my case, I've been using this function for quite a long time and, although slightly simplified for the sake of this demo, I reach for it on a weekly basis. My clients are happy to being able to pass along additional variables if necassery.

Thanks for reading and hope to see you around!