All posts by Torleif

PHP: Unset all defined variables

foreach(get_defined_vars() as $k => $v)
    unset($$k);
unset($k, $v);

Handy in for example this setting:

foreach($iterable as $item)
{
    extract($item);
    unset($item);

    // Define another variable, for only some of the items
    if($foo == 'bar')
        $x = 2;

    // Yield all defined vars
    yield get_defined_vars();

    // Cleanup, to prevent $x and other variables from
    // sticking around to the next iteration
    foreach(get_defined_vars() as $k => $v)
        unset($$k);
    unset($k, $v);
}

PHP: Pathable RecursiveIteratorIterator

Needed to recursively loop through a multi-dimensional array and print out each leaf-node with its full “path”.

For this I used an RecursiveArrayIterator for the array and a RecursiveIteratorIterator for the recursion. Thought I was home free because I had used a method called getSubpathname before, but turned out that was just something the RecursiveDirectoryIterator had…

So, had to grow my own… noting it here for others and the future:

class PathableRecursiveIteratorIterator
    extends RecursiveIteratorIterator
{
    /**
     * Gets the path to current node, i.e. each
     *   key "upwards", including self.
     *
     * @param null|string $glue Optional $glue for implode().
     *
     * @return array|string The keys, from root to self,
     *   as an array; or as a string if $glue is provided.
     */

    public function getPath($glue = null)
    {
        for($i = 0; $i < $this->getDepth(); $i++)
            $path[] = $this->getSubIterator($i)->key();

        $path[] = $this->key();

        return $glue !== null
            ? implode($glue, $path)
            : $path;
    }
}

Based upon: StackOverflow

Edit composer dependencies “inline” while developing

Have a PHP project, and want to re-use some classes in a new project. Moving them to their own repository and turning them into a Composer dependency is a clean way to do that. If hosted on GitHub/BitBucket, it’s even simply to be a bit more proper and fancy by publishing the package on Packagist with automatic updates based on git tags. However, if still heavily developing both the project and the dependency, the round trip through repo/packagist is a pain.

But today I discovered there’s an option called --prefer-source which seems to solve most of this pain. And here’s a basic note-to-self on how to get that to work…

0. Make sure dependency is a composer dependency

// Dependency composer.json
{
    "name": "my/package",
    "autoload":
    {
        "psr-4": {"": "src/"}
    }
}

1. Add dependency repo and package to root project

// Root project composer.json
{
    "repositories":
    [
        {"type": "vcs", "url": "https://github.com/username/my-project"}
    ],
    "require":
    {
        "my/project": "dev-master",
    }

2. Run update with –prefer-source

$ composer require my/package dev-master --prefer-source

We should now have the package downloaded and, more importantly, if you check ./vendor/my/package it should have the .git directory, meaning you can make immediately working changes there directly, and commit when you’re happy… Our other root project(s) depending on it should then get the update from the source repository after an easy composer update. 👍


Note: I’m a bit fuzzy on what composer does to keep track on whatever different happens through --prefer-source, and it’s an option for both composer install and composer update. For example, at first attempt, I tried to use composer update --prefer-source on a dependency that had already been downloaded, and the .git directory did not turn up, but if I just deleted the vendor directory for that package and then re-ran the command, then the .git was there.

So, feel free to comment if you have some light on that topic 😛🤓

PHP: Validating flexible/incomplete date time strings

Need to validate some datetime strings, that may or may not be incomplete. Might be for example just a year and a month, while the rest is unknown.

Noting it here in case I need it again. And in case someone else needs it, knows a more efficient/cleaner way, or sees a flaw…

function flexi_time($value): bool
{
    $valid = preg_match('/^(?<year>\d{4})(?:-(?<month>\d{2})(?:-(?<day>\d{2})(?:[ T](?<hour>\d{2}):(?<min>\d{2})(?::(?<sec>\d{2}))?)?)?)?$/', $value, $matches);
   
    if( ! $valid)
        return false;

    extract($matches);

    // Check month
    if($month ?? null AND ! between($month, 1, 12))
        return false;

    // Check date
    if($day ?? null AND ! checkdate($month, $day, $year))
        return false;

    // Check hour
    if($hour ?? null AND ! between($hour, 0, 23))
        return false;

    // Check minute
    if($min ?? null AND ! between($min, 0, 59))
        return false;

    // Check second
    if($sec ?? null AND ! between($sec, 0, 59))
        return false;

    return true;
}

function between($value, $min, $max): bool
{
    return $value >= $min && $value <= $max;
}

Test

$dates = [
    'foo', // Invalid
    '17', // Invalid
    '2017',
    '2017-01',
    '2017-13', // Invalid month
    '2017-01-17',
    '2017-02-31', // Invalid date
    '2017-01-17 20', // Invalid hour without minutes
    '2017-01-17 20:00',
    '2017-01-17T20:00', // Both space and T allowed as separator
    '2017-01-17 20:00:10',
    '2017-01-17 25:00:10', // Invalid hour
    '2017-01-17 20:70:70', // Invalid minute
    '2017-01-17 20:10:70', // Invalid second
];
print_r(array_filter($dates, 'flexi_time'));
Array
(
    [2] => 2017
    [3] => 2017-01
    [5] => 2017-01-17
    [8] => 2017-01-17 20:00
    [9] => 2017-01-17T20:00
    [10] => 2017-01-17 20:00:10
)

jQuery: How to extract a tag from an HTML response

Making a website, and using ajax for some things. Sometimes things fail and return custom error pages. I made them to be helpful, but since you can only see them in the browser developer console, they were a bit of a hassle to look at.

To see what the error was much easier, I figured I could just parse the returned HTML, extract the message I knew was there, and insert it into the page that way.

And you’d think the following would work fine:

$(document).ajaxError(function(event, jqxhr, settings, error)
{
    // Find the message in the response HTML
    var m = $(jqxhr.responseText)
        .find('#message');

    // Except .find() doesn't find anything

    // And we replace our DOM with nothing
    $('#content')
        .replaceWith(m);
});

But… No… Apparently, since the response was a complete HTML page, i.e. including html, head and body tags, jQuery was getting a bit tricked up when trying to parse it. Actually not sure if it’s jQuery or native browser parsing behind that’s causing it, but where there’s a will, there’s a way:

$(document).ajaxError(function(event, jqxhr, settings, error)
{
    // Find the inner HTML of the body tag
    var body = /<body.*>([\s\S]+)<\/body>/
        .exec(jqxhr.responseText);

    // Parse the HTML
    body = $.parseHTML(body[1])

    // Append the HTML to a non-special root tag
    body = $('<output>').append(body);

    // And *now* we can finally find our message
    var message = body.find('#message');

    // And add it to our DOM
    $('#content')
        .replaceWith(m);
});

¯\_(ツ)_/¯