Такого рода вещи следует безусловно считать на сервере для гарантии от жульничества, обрывов связи и т.п.
В сущности вам нужен неблокируемый таймер. Есть несколько способов реализации этого, но почти все костыльные в силу синхронности парадигмы программирования под php.
Однако, есть один способ, который позволит сделать полностью асинхронную обработку, только это потребует кардинального пересмотра всего серверного приложения.Есть такая библиотека libevent и обертка под названием reactPHP.
Я недавно решал проблему в своем проекте с отложенным отключением пользователей при обрыве websocket-соединения и мне данное решение помогло.
Суть в том, что приложение строится как цепочка callback-ов типа onOpen, OnMessage, onClose, которые вешаются на event loop. Сооветственно, на него же можно повесить и таймеры при обработке этих глобальных callback-ов.
Например:
...
public function onClose(Connection $connection)
{
...
$loop = MightyLoop::get()->fetch();
$detacher = function() use ($user) {
$clients = UserCollection::get();
$clients->detach($user);
$this->notifyOnClose($user, $clients);
};
if ($user->getAsyncDetach()) {
$timer = $loop->addTimer(30, $detacher);
$user->setTimer($timer);
} else {
$detacher($user);
$user->getConnection()->close();
}
...
}