2

I would like to use a Timer in php, sending messages after a certain period of time (no player activity)? This is for a multi-player game through sockets.

I have googled and stackoverflew and found the following

  • could use a cron (will need to synronize my php classes (socket based) with the cron... ), No I guess.
  • could use sleep ... if it could wait for less than one second, I will have given a try, No again, I guess
  • could use register_tick_function ... not applicable I think

Any ideas? Thanks!

2
  • If the granularity of sleep is too large, then there's always the usleep function. Irrespective, it's hard to recommend an approach without a bit more information. (Are the socket connections from the clients always connected, for example.) Commented Feb 17, 2011 at 15:51
  • Yes connections from the clients are always connected. Commented Feb 17, 2011 at 15:55

4 Answers 4

5

This is what I wrote to deal with my timing needs. It doesn't magically interrupt your code, but it provides a very flexible way to schedule timers. All you have to do, is make sure you run $events->CheckTimers() every now and then.

The libevent module does a lot more, but that's a bit more effort.

$events = new Events();

function callback($text) {
        printf("I'm a timer callback %s\n", $text);
}

$events->AddTimer("5s", "callback", "every 5 seconds", EVENTS::EVENT_REPEAT);
$events->AddTimer("2s..15s", "callback", "random time 2 to 15 seconds", EVENTS::EVENT_REPEAT);
$events->AddTimer("1m", create_function('', "echo 'Im a LAMBDA function that runs every minute\n';"), false, EVENTS::EVENT_REPEAT);
$events->AddTimer("1h..2h", "callback", "I will run once, in between 1 and 2 hours");

# This is just an example, in reality, you would make sure to call CheckTimer() regulary (ideally not more than once a second)

while (true) {
        $events->CheckTimers();
        printf("Next timer in %s seconds...\n", $ne = $events->GetNextTimer(), gettype($ne));
        sleep(1);
}


class Events {
    const EVENT_REPEAT      = 0x0001;
    const EVENT_SEQUENCE    = 0x0002;
    var $events;
    var $timers;

    function __construct() {
            $this->events = array();
            $this->timers = array();
    }

    function AddTimer($when, $action, $args = false, $flags = 0) {
            if (preg_match('#([0-9a-zA-Z]+)..([0-9a-zA-Z]+)#', $when, $a)) {
                    $time = time(NULL) + rand($this->time2seconds($a[1]), $this->time2seconds($a[2]));
            } else {
                    $time = time(NULL) + $this->time2seconds($when);
            }
            if ($flags & self::EVENT_SEQUENCE) {
                    while ($this->IsArrayCount($this->timers[$time])) {
                            $time ++;
                    }
            }
            $this->timers[$time][] = array("when" => $when, "action" => $action, "args" => $args, "flags" => $flags);
            ksort($this->timers);
    }

    function GetNextTimer() {
            if (!$this->IsArrayCount($this->timers)) {
                    return false;
            }
            reset($this->timers);
            $firstevent = each($this->timers);
            if ($firstevent === false) {
                    return false;
            }
            $time = $firstevent["key"];
            $nextEvent = $time - time(NULL);
            if ($nextEvent < 1) {
                    return 1;
            }

            return $nextEvent;
    }

    function CheckTimers() {
            $rv = false;
            $now = time(NULL);
            foreach ($this->timers as $time => $events) {
                    if ($time > $now) {
                            break;
                    }
                    foreach ($events as $key => $event) {
                            # debug("Event::CheckTimer: {$event["action"]}");
                            # ircPubMsg("Event::CheckTimer: {$event["action"]}", "#bots");
                            if (!$event["args"]) {
                                    call_user_func($event["action"]);
                            } else {
                                    $rv = call_user_func_array($event["action"], is_array($event["args"]) ? $event["args"] : array($event["args"]));
                            }
                            unset($this->timers[$time][$key]);
                            if ($event["flags"] & self::EVENT_REPEAT) {
                                    $this->AddTimer($event["when"], $event["action"], $event["args"], $event["flags"]);
                            }
                            if ($rv) {
                                    # break;
                            }
                    }
                    if (!$this->IsArrayCount($this->timers[$time])) {
                            unset($this->timers[$time]);
                    }

                    if (0 && $rv) {
                            break;
                    }
            }

            if ($rv) {
                    return $rv;
            }
    }

    function time2seconds($timeString) {
            $end = substr($timeString, strlen($timeString) - 1);
            $seconds = intval($timeString); //  = preg_replace("#[^0-9]#", "", $a);

            if (is_numeric($end)) {
                    return $seconds;
            }

            $unim = array("s","m","h","d", "w", "m", "y");
            $divs = array(1, 60, 60, 24, 7, 28, 12, false);
            $found = false;
            while (!$found) {
                    $u = array_shift($unim);
                    $d = array_shift($divs);
                    $seconds *= $d;
                    if ($end === $u) {
                            return $seconds;
                    }
            }

            return intval($timeString);
    }

    function IsArrayCount($possibleArray) {
            return (isset($possibleArray) && is_array($possibleArray)) ? count($possibleArray) : false;
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

Does PHP's time_nanosleep() work for you? Or usleep()?

2 Comments

whats the practical difference between the two?
Thanks. But even with usleep, I have not idea how to implement it... The best will be to implement an infinite loop in a thread ...
0

Maybe a daemon? http://kevin.vanzonneveld.net/techblog/article/create_daemons_in_php/

3 Comments

This is the most common answer in Google (and fine in many cases). However here, I will need to inject the daemon event into my game classes which I think can be tricky.
sorry, I just read the article. Apparently this may fit (dameon not cronjob) but it seems not so easy at first glance... Any other simplier solutions in stackoverflow brains? ...
I never did socket stuff before, so maybe a silly thing, but can't you set the timeout to a bigger value? I found SO_KEEPALIVE thing here php.net/manual/en/function.socket-get-option.php
0

here are a few concepts how to do it : 1. common concept is to create script for sending messages, then add it to server Cron application and execute the script with specified time. 2. write python script that will control your php application file or section, in pythonic terms you can fork process on server start that will do your tasks every amount of time (with infinity loop I guess ... while 1: ..) 3. write shell .sh file that will invoke php or python script and add controls of this script it to /etc/init.d to be more controllable.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.