diff --git a/var/Helper.php b/var/Helper.php index ad4f41f0..037997eb 100644 --- a/var/Helper.php +++ b/var/Helper.php @@ -63,6 +63,17 @@ class Helper return $widget; } + /** + * 请求异步服务 + * + * @param $method + * @param $params + */ + public static function requestService($method, $params) + { + Typecho_Widget::widget('Widget_Service')->requestService($method, $params); + } + /** * 强行删除某个插件 * diff --git a/var/Typecho/Common.php b/var/Typecho/Common.php index a502f02e..cd4fcc0f 100644 --- a/var/Typecho/Common.php +++ b/var/Typecho/Common.php @@ -22,7 +22,7 @@ define('__TYPECHO_MB_SUPPORTED__', function_exists('mb_get_info') && function_ex class Typecho_Common { /** 程序版本 */ - const VERSION = '1.1/17.10.30'; + const VERSION = '1.1/17.11.15'; /** * 允许的属性 @@ -910,6 +910,39 @@ EOF; } } + /** + * 创建一个会过期的Token + * + * @param $secret + * @return string + */ + public static function timeToken($secret) + { + return sha1($secret . '&' . time()); + } + + /** + * 在时间范围内验证token + * + * @param $token + * @param $secret + * @param int $timeout + * @return bool + */ + public static function timeTokenValidate($token, $secret, $timeout = 5) + { + $now = time(); + $from = $now - $timeout; + + for ($i = $now; $i >= $from; $i --) { + if (sha1($secret . '&' . $i) == $token) { + return true; + } + } + + return false; + } + /** * 将路径转化为链接 * diff --git a/var/Typecho/Response.php b/var/Typecho/Response.php index e7b037c0..d5e449ec 100644 --- a/var/Typecho/Response.php +++ b/var/Typecho/Response.php @@ -87,6 +87,13 @@ class Typecho_Response */ private static $_instance = null; + /** + * 结束前回调函数 + * + * @var array + */ + private static $_callbacks = array(); + /** * 获取单例句柄 * @@ -126,6 +133,33 @@ class Typecho_Response } } + /** + * 结束前的统一回调函数 + */ + public static function callback() + { + static $called; + + if ($called) { + return; + } + + $called = true; + foreach (self::$_callbacks as $callback) { + call_user_func($callback); + } + } + + /** + * 新增回调 + * + * @param $callback + */ + public static function addCallback($callback) + { + self::$_callbacks[] = $callback; + } + /** * 设置默认回执编码 * @@ -211,6 +245,7 @@ class Typecho_Response ''; /** 终止后续输出 */ + self::callback(); exit; } @@ -229,6 +264,7 @@ class Typecho_Response echo Json::encode($message); /** 终止后续输出 */ + self::callback(); exit; } @@ -246,9 +282,11 @@ class Typecho_Response $location = Typecho_Common::safeUrl($location); if ($isPermanently) { + self::callback(); header('Location: ' . $location, false, 301); exit; } else { + self::callback(); header('Location: ' . $location, false, 302); exit; } @@ -296,6 +334,7 @@ class Typecho_Response $this->redirect($default); } + self::callback(); exit; } } diff --git a/var/Typecho/Router.php b/var/Typecho/Router.php index 927a6772..743c114d 100644 --- a/var/Typecho/Router.php +++ b/var/Typecho/Router.php @@ -138,6 +138,7 @@ class Typecho_Router $widget->{$route['action']}(); } + Typecho_Response::callback(); return; } catch (Exception $e) { diff --git a/var/Widget/Ajax.php b/var/Widget/Ajax.php index 673b4ddf..3825c39c 100644 --- a/var/Widget/Ajax.php +++ b/var/Widget/Ajax.php @@ -51,7 +51,7 @@ class Widget_Ajax extends Widget_Abstract_Options implements Widget_Interface_Do /** 匹配内容体 */ $response = $client->getResponseBody(); - $json = json_decode($response, true); + $json = Json::decode($response, true); if (!empty($json)) { list($soft, $version) = explode(' ', $this->options->generator); diff --git a/var/Widget/Service.php b/var/Widget/Service.php index 35403ddd..d810d81c 100644 --- a/var/Widget/Service.php +++ b/var/Widget/Service.php @@ -19,6 +19,13 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; */ class Widget_Service extends Widget_Abstract_Options implements Widget_Interface_Do { + /** + * 异步请求 + * + * @var array + */ + public $asyncRequests = array(); + /** * 发送pingback实现 * @@ -31,7 +38,13 @@ class Widget_Service extends Widget_Abstract_Options implements Widget_Interface $this->user->pass('contributor'); /** 忽略超时 */ - ignore_user_abort(true); + if (function_exists('ignore_user_abort')) { + ignore_user_abort(true); + } + + if (function_exists('set_time_limit')) { + set_time_limit(30); + } /** 获取post */ $post = $this->widget('Widget_Archive', "type=post", "cid={$this->request->cid}"); @@ -138,7 +151,7 @@ class Widget_Service extends Widget_Abstract_Options implements Widget_Interface $client->setCookie('__typecho_uid', Typecho_Cookie::get('__typecho_uid')) ->setCookie('__typecho_authCode', Typecho_Cookie::get('__typecho_authCode')) ->setHeader('User-Agent', $this->options->generator) - ->setTimeout(3) + ->setTimeout(2) ->setData($input) ->send(Typecho_Common::url('/action/service', $this->options->index)); @@ -148,6 +161,78 @@ class Widget_Service extends Widget_Abstract_Options implements Widget_Interface } } + /** + * 请求异步服务 + * + * @param $method + * @param mixed $params + */ + public function requestService($method, $params = NULL) + { + static $called; + + if (!$called) { + $self = $this; + + Typecho_Response::addCallback(function () use ($self) { + if (!empty($self->asyncRequests) && $client = Typecho_Http_Client::get()) { + try { + $client->setHeader('User-Agent', $this->options->generator) + ->setTimeout(2) + ->setData(array( + 'do' => 'async', + 'requests' => Json::encode($self->asyncRequests), + 'token' => Typecho_Common::timeToken($this->options->secret) + )) + ->setMethod(Typecho_Http_Client::METHOD_POST) + ->send(Typecho_Common::url('/action/service', $this->options->index)); + + } catch (Typecho_Http_Client_Exception $e) { + return; + } + } + }); + + $called = true; + } + + $this->asyncRequests[] = array($method, $params); + } + + /** + * 执行回调 + * + * @throws Typecho_Widget_Exception + */ + public function asyncHandle() + { + /** 验证权限 */ + $token = $this->request->token; + + if (!Typecho_Common::timeTokenValidate($token, $this->options->secret, 3)) { + throw new Typecho_Widget_Exception(_t('禁止访问'), 403); + } + + /** 忽略超时 */ + if (function_exists('ignore_user_abort')) { + ignore_user_abort(true); + } + + if (function_exists('set_time_limit')) { + set_time_limit(30); + } + + $requests = Json::decode($this->request->requests, true); + $plugin = Typecho_Plugin::factory(__CLASS__); + + if (!empty($requests)) { + foreach ($requests as $request) { + list ($method, $params) = $request; + $plugin->{$method}($params); + } + } + } + /** * 异步请求入口 * @@ -157,5 +242,6 @@ class Widget_Service extends Widget_Abstract_Options implements Widget_Interface public function action() { $this->on($this->request->is('do=ping'))->sendPingHandle(); + $this->on($this->request->is('do=async'))->asyncHandle(); } }