'task name', * 'output' => '... task output ...' * ) * ``` */ static public function runTasks() { // get the array where rescheduled timetables are stored $timetable = self::getTimetableFromOptionTable(); // collect tasks $tasks = array(); /** * Triggered when the TaskScheduler runs scheduled tasks. Collects all the tasks that * will be run. * * Subscribe to this event to schedule code execution on an hourly, daily, weekly or monthly * basis. * * **Example** * * ``` * public function getScheduledTasks(&$tasks) * { * $tasks[] = new ScheduledTask( * 'Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient', * 'clearAllCacheEntries', * null, * new Daily(), * ScheduledTask::LOWEST_PRIORITY * ); * } * ``` * * @param ScheduledTask[] &$tasks List of tasks to run periodically. */ Piwik::postEvent(self::GET_TASKS_EVENT, array(&$tasks)); /** @var ScheduledTask[] $tasks */ // remove from timetable tasks that are not active anymore $activeTaskNames = array(); foreach ($tasks as $task) { $activeTaskNames[] = $task->getName(); } foreach (array_keys($timetable) as $taskName) { if (!in_array($taskName, $activeTaskNames)) { unset($timetable[$taskName]); } } // for every priority level, starting with the highest and concluding with the lowest $executionResults = array(); for ($priority = ScheduledTask::HIGHEST_PRIORITY; $priority <= ScheduledTask::LOWEST_PRIORITY; ++$priority) { // loop through each task foreach ($tasks as $task) { // if the task does not have the current priority level, don't execute it yet if ($task->getPriority() != $priority) { continue; } $taskName = $task->getName(); if (self::taskShouldBeExecuted($taskName, $timetable)) { self::$running = true; $message = self::executeTask($task); self::$running = false; $executionResults[] = array('task' => $taskName, 'output' => $message); } if (self::taskShouldBeRescheduled($taskName, $timetable)) { // update the scheduled time $timetable[$taskName] = $task->getRescheduledTime(); Option::set(self::TIMETABLE_OPTION_STRING, serialize($timetable)); } } } return $executionResults; } /** * Returns true if the TaskScheduler is currently running a scheduled task. * * @return bool */ static public function isTaskBeingExecuted() { return self::$running; } /** * Return the next scheduled time given the class and method names of a scheduled task. * * @param string $className The name of the class that contains the scheduled task method. * @param string $methodName The name of the scheduled task method. * @param string|null $methodParameter Optional method parameter. * @return mixed int|bool The time in miliseconds when the scheduled task will be executed * next or false if it is not scheduled to run. */ static public function getScheduledTimeForMethod($className, $methodName, $methodParameter = null) { // get the array where rescheduled timetables are stored $timetable = self::getTimetableFromOptionTable(); $taskName = ScheduledTask::getTaskName($className, $methodName, $methodParameter); return self::taskHasBeenScheduledOnce($taskName, $timetable) ? $timetable[$taskName] : false; } /** * Checks if the task should be executed * * Task has to be executed if : * - the task has already been scheduled once and the current system time is greater than the scheduled time. * - execution is forced, see $forceTaskExecution * * @param string $taskName * @param array $timetable * * @return boolean */ static private function taskShouldBeExecuted($taskName, $timetable) { $forceTaskExecution = (isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) || DEBUG_FORCE_SCHEDULED_TASKS; return $forceTaskExecution || (self::taskHasBeenScheduledOnce($taskName, $timetable) && time() >= $timetable[$taskName]); } /** * Checks if a task should be rescheduled * * Task has to be rescheduled if : * - the task has to be executed * - the task has never been scheduled before * * @param string $taskName * @param array $timetable * * @return boolean */ static private function taskShouldBeRescheduled($taskName, $timetable) { return !self::taskHasBeenScheduledOnce($taskName, $timetable) || self::taskShouldBeExecuted($taskName, $timetable); } static private function taskHasBeenScheduledOnce($taskName, $timetable) { return isset($timetable[$taskName]); } static private function getTimetableFromOptionValue($option) { $unserializedTimetable = @unserialize($option); return $unserializedTimetable === false ? array() : $unserializedTimetable; } static private function getTimetableFromOptionTable() { return self::getTimetableFromOptionValue(Option::get(self::TIMETABLE_OPTION_STRING)); } /** * Executes the given taks * * @param ScheduledTask $task * @return string */ static private function executeTask($task) { try { $timer = new Timer(); call_user_func(array($task->getObjectInstance(), $task->getMethodName()), $task->getMethodParameter()); $message = $timer->__toString(); } catch (Exception $e) { $message = 'ERROR: ' . $e->getMessage(); } return $message; } }