Вход Регистрация
Файл: library/XenForo/Model/Deferred.php
Строк: 393
<?php

class XenForo_Model_Deferred extends XenForo_Model
{
    public function 
getDeferredById($id)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT *
            FROM xf_deferred
            WHERE deferred_id = ?
        '
$id);
    }

    public function 
getDeferredByKey($key)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT *
            FROM xf_deferred
            WHERE unique_key = ?
        '
$this->_getSafeUniqueKey($key));
    }

    protected function 
_getSafeUniqueKey($key)
    {
        if (
is_string($key) && strlen($key) > 50)
        {
            return 
md5($key);
        }

        return 
$key;
    }

    public function 
getRunnableDeferreds($manual false)
    {
        return 
$this->fetchAllKeyed('
            SELECT *
            FROM xf_deferred
            WHERE trigger_date <= ?
                AND manual_execute = ' 
. ($manual 0) . '
            ORDER BY trigger_date
        '
'deferred_id'XenForo_Application::$time);
    }

    public function 
countRunnableDeferreds($manual false)
    {
        return 
$this->_getDb()->fetchOne('
            SELECT COUNT(*)
            FROM xf_deferred
            WHERE trigger_date <= ?
                AND manual_execute = ' 
. ($manual 0) . '
        '
XenForo_Application::$time);
    }

    public function 
getStoppedManualDefers()
    {
        return 
$this->fetchAllKeyed('
            SELECT *
            FROM xf_deferred
            WHERE trigger_date <= ?
                AND manual_execute = 1
            ORDER BY trigger_date
        '
'deferred_id'XenForo_Application::$time 60);
    }

    protected static 
$_uniqueDefers = array();

    public function 
defer($class, array $data$uniqueKey null$manual false$triggerDate null)
    {
        
$runner XenForo_Deferred_Abstract::create($class);
        if (!
$runner)
        {
            return 
false;
        }

        if (!
$triggerDate)
        {
            
$triggerDate XenForo_Application::$time;
        }
        if (!
$uniqueKey)
        {
            
$uniqueKey null;
        }

        
$manual = ($manual 0);

        
$db $this->_getDb();

        if (
$uniqueKey)
        {
            
$uniqueHash "$uniqueKey-$triggerDate. ($data '-' md5(serialize($data)) : '');
            if (isset(
self::$_uniqueDefers[$uniqueHash]))
            {
                return 
self::$_uniqueDefers[$uniqueHash];
            }
        }
        else
        {
            
$uniqueHash false;
        }

        
$db->query('
            INSERT INTO xf_deferred
                (execute_class, execute_data, unique_key, manual_execute, trigger_date)
            VALUES
                (?, ?, ?, ?, ?)
            ON DUPLICATE KEY UPDATE
                execute_class = VALUES(execute_class),
                execute_data = VALUES(execute_data),
                manual_execute = VALUES(manual_execute),
                trigger_date = VALUES(trigger_date)
        '
, array($classserialize($data),  $this->_getSafeUniqueKey($uniqueKey), $manual$triggerDate));

        
$id $db->lastInsertId();

        if (!
$manual)
        {
            
$this->updateNextDeferredTime();
            
XenForo_Application::$autoDeferredIds[] = $id;
        }
        else
        {
            
XenForo_Application::$manualDeferredIds[] = $id;
        }

        if (
$uniqueHash)
        {
            
self::$_uniqueDefers[$uniqueHash] = $id;
        }

        return 
$id;
    }

    public function 
resetUniqueDeferInserts()
    {
        
self::$_uniqueDefers = array();
    }

    public function 
updateNextDeferredTime()
    {
        
$date intval($this->_getDb()->fetchOne('
            SELECT MIN(trigger_date)
            FROM xf_deferred
            WHERE manual_execute = 0
        '
));

        
$this->_getDataRegistryModel()->set('deferredRun'$date);

        return 
$date;
    }

    public function 
setNextDeferredTime($time)
    {
        
$time intval($time);
        if (
$time <= 0)
        {
            return 
false;
        }

        
$this->_getDataRegistryModel()->set('deferredRun'$time);

        return 
true;
    }

    public function 
deleteDeferredById($id)
    {
        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);
        
$value $db->delete('xf_deferred''deferred_id = ' $db->quote($id));
        
XenForo_Db::commit($db);

        return 
$value;
    }

    public function 
cancelFirstRunnableDeferred($manual true)
    {
        
$defers $this->getRunnableDeferreds($manual);
        if (!
$defers)
        {
            return 
false;
        }

        
$deferred reset($defers);
        
$runner XenForo_Deferred_Abstract::create($deferred['execute_class']);
        if (!
$runner)
        {
            return 
false;
        }

        if (!
$runner->canCancel())
        {
            return 
false;
        }

        
$this->deleteDeferredById($deferred['deferred_id']);

        return 
$deferred;
    }

    protected static 
$_shutdownRegistered false;
    protected static 
$_runningDeferred false;

    public static function 
shutdownHandleFatalDeferred()
    {
        if (!
self::$_runningDeferred)
        {
            return;
        }

        
// if we get a fatal error from a manual deferred, reinsert it so a refresh can catch it

        
$deferred self::$_runningDeferred;
        try
        {
            
XenForo_Db::rollbackAll();

            if (
$deferred['manual_execute'])
            {
                
XenForo_Application::defer(
                    
$deferred['execute_class'], unserialize($deferred['execute_data']), $deferred['unique_key'], $deferred['manual_execute'], $deferred['trigger_date'], true
                
);
            }
        }
        catch (
Exception $e) {}
    }

    public function 
runDeferred(array $deferred$targetRunTime, &$status, &$canCancel)
    {
        
$this->resetUniqueDeferInserts();

        
$canCancel false;

        if (!
$this->deleteDeferredById($deferred['deferred_id']))
        {
            
// already being run
            
return false;
        }

        
$runner XenForo_Deferred_Abstract::create($deferred['execute_class']);
        if (!
$runner)
        {
            return 
false;
        }

        
$data unserialize($deferred['execute_data']);

        if (!
self::$_shutdownRegistered)
        {
            
self::$_shutdownRegistered true;
            
register_shutdown_function(array(__CLASS__'shutdownHandleFatalDeferred'));
        }

        
self::$_runningDeferred $deferred;

        try
        {
            
$output $runner->execute($deferred$data$targetRunTime$status);
            
self::$_runningDeferred false;
        }
        catch (
Exception $e)
        {
            
self::$_runningDeferred false;

            
// transactions are likely from the manual runner, so we need to roll them back
            // as they probably won't be committed
            
XenForo_Db::rollbackAll();

            if (
$deferred['manual_execute'])
            {
                
// reinsert and throw so a refresh will catch it
                
XenForo_Application::defer(
                    
$deferred['execute_class'], $data$deferred['unique_key'], $deferred['manual_execute'], $deferred['trigger_date'], true
                
);

                throw 
$e;
            }
            else
            {
                
// log and ignore
                
XenForo_Error::logException($efalse);
                
$output false;
                
$status "$deferred[execute_class] threw exception. See error log."// TODO: phrase?
            
}
        }

        if (
$output === 'exit')
        {
            
// this is for debugging - restore to previous state
            
XenForo_Db::rollbackAll();
            
XenForo_Application::defer(
                
$deferred['execute_class'], $data$deferred['unique_key'], $deferred['manual_execute'], $deferred['trigger_date'], true
            
);
            exit;
        }
        else if (
is_array($output))
        {
            
$canCancel $runner->canCancel();

            return 
XenForo_Application::defer(
                
$deferred['execute_class'], $output$deferred['unique_key'], $deferred['manual_execute'], $deferred['trigger_date'], true
            
);
        }
        else
        {
            return 
false;
        }
    }

    public function 
runByUniqueKey($uniqueKey$targetRunTime null, &$status '', &$canCancel null)
    {
        
$deferred $this->getDeferredByKey($uniqueKey);
        if (!
$deferred)
        {
            return 
false;
        }

        
$continued $this->_runInternal(array($deferred), $targetRunTime$status$canCancel);
        return 
$continued reset($continued) : false;
    }

    public function 
runById($id$targetRunTime null, &$status '', &$canCancel null)
    {
        
$deferred $this->getDeferredById($id);
        if (!
$deferred)
        {
            return 
false;
        }

        
$continued $this->_runInternal(array($deferred), $targetRunTime$status$canCancel);
        return 
$continued reset($continued) : false;
    }

    public function 
run($manual false$targetRunTime null, &$status '', &$canCancel null)
    {
        
$runnable $this->getRunnableDeferreds($manual);
        
$continued $this->_runInternal($runnable$targetRunTime$status$canCancel);

        if (!
$manual)
        {
            
$nextRun $this->updateNextDeferredTime();
            if (
$nextRun && $nextRun <= time())
            {
                
$continued true;
            }
            if (!
$nextRun)
            {
                
XenForo_Application::defer('Cron', array(), 'cron'falsetime() + 300);
            }
        }

        return 
$continued;
    }

    protected function 
_runInternal(array $runnable$targetRunTime null, &$status '', &$canCancel null)
    {
        if (
$targetRunTime === null)
        {
            
$targetRunTime XenForo_Application::getConfig()->rebuildMaxExecution;
        }

        if (
$targetRunTime 0)
        {
            
$targetRunTime 0;
        }
        else if (
$targetRunTime && $targetRunTime 2)
        {
            
$targetRunTime 2;
        }

        
$continued = array();
        
$limitTime = ($targetRunTime 0);
        
$startTime microtime(true);

        foreach (
$runnable AS $deferred)
        {
            if (
$limitTime)
            {
                
$remainingTime $targetRunTime - (microtime(true) - $startTime);
                if (
$remainingTime 1)
                {
                    
// ran out of time - have some pick up later
                    
$continued[] = $deferred['deferred_id'];
                    continue;
                }
            }
            else
            {
                
$remainingTime 0;
            }

            
$continue $this->runDeferred($deferred$remainingTime$status$canCancel);
            if (
$continue)
            {
                
$continued[] = $continue;
            }
        }

        return 
$continued;
    }
}
Онлайн: 1
Реклама