123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- <?php
- namespace Widget;
- use Typecho\Common;
- use Typecho\Http\Client;
- use Typecho\Response;
- use Typecho\Widget\Exception;
- use Widget\Base\Contents;
- use Widget\Base\Options as BaseOptions;
- if (!defined('__TYPECHO_ROOT_DIR__')) {
- exit;
- }
- /**
- * 通用异步服务组件
- *
- * @author qining
- * @category typecho
- * @package Widget
- */
- class Service extends BaseOptions implements ActionInterface
- {
- /**
- * 异步请求
- *
- * @var array
- */
- public $asyncRequests = [];
- /**
- * 发送pingback实现
- *
- * @throws Exception|Client\Exception
- */
- public function sendPingHandle()
- {
- /** 验证权限 */
- $data = $this->request->get('@json');
- $token = $data['token'] ?? '';
- $permalink = $data['permalink'];
- $title = $data['title'];
- $excerpt = $data['excerpt'];
- $response = ['trackback' => [], 'pingback' => []];
- if (!Common::timeTokenValidate($token, $this->options->secret, 3) || empty($permalink)) {
- throw new Exception(_t('禁止访问'), 403);
- }
- /** 忽略超时 */
- if (function_exists('ignore_user_abort')) {
- ignore_user_abort(true);
- }
- if (function_exists('set_time_limit')) {
- set_time_limit(30);
- }
- if (!empty($data['pingback'])) {
- $links = $data['pingback'];
- $permalinkPart = parse_url($permalink);
- /** 发送pingback */
- foreach ($links as $url) {
- $urlPart = parse_url($url);
- if (isset($urlPart['scheme'])) {
- if ('http' != $urlPart['scheme'] && 'https' != $urlPart['scheme']) {
- continue;
- }
- } else {
- $urlPart['scheme'] = 'http';
- $url = Common::buildUrl($urlPart);
- }
- if ($permalinkPart['host'] == $urlPart['host'] && $permalinkPart['path'] == $urlPart['path']) {
- continue;
- }
- $spider = Client::get();
- if ($spider) {
- $spider->setTimeout(10)
- ->send($url);
- if (!($xmlrpcUrl = $spider->getResponseHeader('x-pingback'))) {
- if (
- preg_match(
- "/<link[^>]*rel=[\"']pingback[\"'][^>]*href=[\"']([^\"']+)[\"'][^>]*>/i",
- $spider->getResponseBody(),
- $out
- )
- ) {
- $xmlrpcUrl = $out[1];
- }
- }
- if (!empty($xmlrpcUrl)) {
- $response['pingback'][] = $url;
- try {
- $xmlrpc = new \IXR\Client($xmlrpcUrl);
- $xmlrpc->pingback->ping($permalink, $url);
- unset($xmlrpc);
- } catch (\IXR\Exception $e) {
- continue;
- }
- }
- }
- unset($spider);
- }
- }
- /** 发送trackback */
- if (!empty($data['trackback'])) {
- $links = $data['trackback'];
- foreach ($links as $url) {
- $client = Client::get();
- $response['trackback'][] = $url;
- if ($client) {
- try {
- $client->setTimeout(5)
- ->setData([
- 'blog_name' => $this->options->title . ' » ' . $title,
- 'url' => $permalink,
- 'excerpt' => $excerpt
- ])
- ->send($url);
- unset($client);
- } catch (Client\Exception $e) {
- continue;
- }
- }
- }
- }
- $this->response->throwJson($response);
- }
- /**
- * 发送pingback
- * <code>
- * $this->sendPing($post);
- * </code>
- *
- * @param Contents $content 内容url
- * @param array|null $trackback
- */
- public function sendPing(Contents $content, ?array $trackback = null)
- {
- $this->user->pass('contributor');
- if ($client = Client::get()) {
- try {
- $input = [
- 'do' => 'ping',
- 'permalink' => $content->permalink,
- 'excerpt' => $content->excerpt,
- 'title' => $content->title,
- 'token' => Common::timeToken($this->options->secret)
- ];
- if (preg_match_all("|<a[^>]*href=[\"'](.*?)[\"'][^>]*>(.*?)</a>|", $content->content, $matches)) {
- $pingback = array_unique($matches[1]);
- if (!empty($pingback)) {
- $input['pingback'] = $pingback;
- }
- }
- if (!empty($trackback)) {
- $input['trackback'] = $trackback;
- }
- $client->setHeader('User-Agent', $this->options->generator)
- ->setTimeout(2)
- ->setJson($input)
- ->send($this->getServiceUrl('ping'));
- } catch (Client\Exception $e) {
- return;
- }
- }
- }
- /**
- * 获取真实的 URL
- *
- * @param string $do 动作名
- * @return string
- */
- private function getServiceUrl(string $do): string
- {
- $url = Common::url('/action/service', $this->options->index);
- if (defined('__TYPECHO_SERVICE_URL__')) {
- $rootPath = rtrim(parse_url($this->options->rootUrl, PHP_URL_PATH), '/');
- $path = parse_url($url, PHP_URL_PATH);
- $parts = parse_url(__TYPECHO_SERVICE_URL__);
- if (
- !empty($parts['path'])
- && $parts['path'] != '/'
- && rtrim($parts['path'], '/') != $rootPath
- ) {
- $path = Common::url($path, $parts['path']);
- }
- $parts['path'] = $path;
- $url = Common::buildUrl($parts);
- }
- return $url . '?do=' . $do;
- }
- /**
- * 请求异步服务
- *
- * @param $method
- * @param mixed $params
- */
- public function requestService($method, $params = null)
- {
- static $called;
- if (!$called) {
- Response::getInstance()->addResponder(function () {
- if (!empty($this->asyncRequests) && $client = Client::get()) {
- try {
- $client->setHeader('User-Agent', $this->options->generator)
- ->setTimeout(2)
- ->setJson([
- 'requests' => $this->asyncRequests,
- 'token' => Common::timeToken($this->options->secret)
- ])
- ->send($this->getServiceUrl('async'));
- } catch (Client\Exception $e) {
- return;
- }
- }
- });
- $called = true;
- }
- $this->asyncRequests[] = [$method, $params];
- }
- /**
- * 执行回调
- *
- * @throws Exception
- */
- public function asyncHandle()
- {
- /** 验证权限 */
- $data = $this->request->get('@json');
- $token = $data['token'] ?? '';
- if (!Common::timeTokenValidate($token, $this->options->secret, 3)) {
- throw new Exception(_t('禁止访问'), 403);
- }
- /** 忽略超时 */
- if (function_exists('ignore_user_abort')) {
- ignore_user_abort(true);
- }
- if (function_exists('set_time_limit')) {
- set_time_limit(30);
- }
- $requests = $data['requests'] ?? null;
- $plugin = self::pluginHandle();
- if (!empty($requests)) {
- foreach ($requests as $request) {
- [$method, $params] = $request;
- $plugin->{$method}($params);
- }
- }
- }
- /**
- * 异步请求入口
- */
- public function action()
- {
- $this->on($this->request->isPost() && $this->request->is('do=ping'))->sendPingHandle();
- $this->on($this->request->isPost() && $this->request->is('do=async'))->asyncHandle();
- }
- }
|