Simple Javascript like timer implementation for PHP.

  • git clone https://klva.cz/src/php/timer.git
  • README.md: Refined about timing. e84c103, 4 Aug 2018
    src/
    composer.json
    README.md
    test.php

    PHP Timer

    This is simple Javascript like timer implementation for PHP. It's not meant to be a production library so don't use it.

    Functions:

    Original solution by Jakub Vrána found years ago here; this one is different, since I don't think the proposed code works the same way the Javascript does.

    There is an important thing you might not notice in Javascript. Whenever you set a timer, remaining time is not being updated while a synchronous, blocking code is running. Since almost all the API Javascript has is asynchronous, it's not an issue.

    Take a look at this code:

    setTimeout(function() {
        print 'Hello ';
    }, 0);
    
    sleep(1);
    print 'World!';
    

    At first look, it seems like Hello should print first (time set to 0 ms) and World! should follow a second later. Instead, this is what actually happens:

    1. our Hello callback gets queued with time set to 0 ms,
    2. sleep(1) hangs the execution for a second - that's because it's synchronous and blocking,
    3. World! gets printed, because everything behind the initial setTimeout is still a synchronous code,
    4. now, our internal event loop takes the timer with least remaining time (waits eventually) and executes it.

    This is in fact exactly the same thing Javascript does. It doesn't have a sleep() function, but here is how you could emulate it:

    setTimeout(() => console.log('Hello '), 0);
    
    alert('Read this for a second and then close it.');
    console.log('World!');
    

    You can run this code in your browser and see what happens - just like in PHP, World! will be printed first and Hello will follow right after closing the alert window.

    Solution

    The thing we can realize here is that Javascript event loop doesn't have any sort of interruption nor threading and it doesn't measure remaining time by abusing OS current time. Instead, it sleeps till a timer is ready when there's no synchronous code being executed. Synchronous code execution simply delays all pending timers.

    Therefore, this is why I think my implementation is as close to Javascript behavior as it can be.

    As shown above, the correct (well, Javascript) solution doesn't use microtime(), ticks nor threads. Whenever a timer is set, we'll store the user defined time (*1000 - we'll need microseconds instead of milliseconds for usleep() later) and set its remaining time the same.

    Now, we'll run our event loop on shutdown using register_shutdown_function() and till a timer exists, we will repeat this:

    1. obtain a timer with least remaining time,
    2. usleep() on remaining time,
    3. decrease other timers remaining time by the remaining time of currently executed timer,
    4. execute the current timer (reset its remaining time if it's periodic, drop otherwise).

    Note that since we're calling usleep() here, timing accuracy suffers. That's actually another con for microtime() - if we would decrease remaining time by something measured using this function, timers with low fractions of seconds might be executed in an unexpected order, which is a serious issue we're trying to mitigate by maintaining the remaining time manually, using exactly calculated microseconds. This applies for Javascript as well - timers are guaranteed to be executed in an expected order, not in an accurate expected time.