mirror of
https://github.com/typecho/typecho.git
synced 2025-03-18 08:59:40 +01:00
add sandbox
This commit is contained in:
parent
f40c5c178e
commit
675efe1e43
@ -18,7 +18,15 @@ class Request
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* 内部参数
|
||||
* 沙箱参数
|
||||
*
|
||||
* @access private
|
||||
* @var Config|null
|
||||
*/
|
||||
private $sandbox;
|
||||
|
||||
/**
|
||||
* 用户参数
|
||||
*
|
||||
* @access private
|
||||
* @var Config|null
|
||||
@ -92,6 +100,25 @@ class Request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function beginSandbox(Config $sandbox): Request
|
||||
{
|
||||
$this->sandbox = $sandbox;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function endSandbox(): Request
|
||||
{
|
||||
$this->sandbox = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $params
|
||||
* @return $this
|
||||
*/
|
||||
public function proxy(Config $params): Request
|
||||
{
|
||||
$this->params = $params;
|
||||
@ -117,6 +144,14 @@ class Request
|
||||
case isset($this->params) && isset($this->params[$key]):
|
||||
$value = $this->params[$key];
|
||||
break;
|
||||
case isset($this->sandbox):
|
||||
if (isset($this->sandbox[$key])) {
|
||||
$value = $this->sandbox[$key];
|
||||
} else {
|
||||
$value = $default;
|
||||
$exists = false;
|
||||
}
|
||||
break;
|
||||
case isset($_GET[$key]):
|
||||
$value = $_GET[$key];
|
||||
break;
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Typecho;
|
||||
|
||||
use Typecho\Widget\Terminal;
|
||||
|
||||
/**
|
||||
* Typecho公用方法
|
||||
*
|
||||
@ -107,6 +109,11 @@ class Response
|
||||
*/
|
||||
private $enableAutoSendHeaders = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $sandbox = false;
|
||||
|
||||
/**
|
||||
* init responder
|
||||
*/
|
||||
@ -129,6 +136,24 @@ class Response
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function beginSandbox(): Response
|
||||
{
|
||||
$this->sandbox = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function endSandbox(): Response
|
||||
{
|
||||
$this->sandbox = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $enable
|
||||
*/
|
||||
@ -154,6 +179,10 @@ class Response
|
||||
*/
|
||||
public function sendHeaders()
|
||||
{
|
||||
if ($this->sandbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
header('HTTP/1.1 ' . $this->status . ' ' . self::HTTP_CODE[$this->status], true, $this->status);
|
||||
|
||||
// set header
|
||||
@ -177,9 +206,14 @@ class Response
|
||||
|
||||
/**
|
||||
* respond data
|
||||
* @throws Terminal
|
||||
*/
|
||||
public function respond()
|
||||
{
|
||||
if ($this->sandbox) {
|
||||
throw new Terminal();
|
||||
}
|
||||
|
||||
if ($this->enableAutoSendHeaders) {
|
||||
$this->sendHeaders();
|
||||
}
|
||||
@ -200,7 +234,10 @@ class Response
|
||||
*/
|
||||
public function setStatus(int $code): Response
|
||||
{
|
||||
$this->status = $code;
|
||||
if (!$this->sandbox) {
|
||||
$this->status = $code;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -213,8 +250,11 @@ class Response
|
||||
*/
|
||||
public function setHeader(string $name, string $value): Response
|
||||
{
|
||||
$name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $name)));
|
||||
$this->headers[$name] = $value;
|
||||
if (!$this->sandbox) {
|
||||
$name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $name)));
|
||||
$this->headers[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -235,7 +275,10 @@ class Response
|
||||
string $path = '/',
|
||||
string $domain = null
|
||||
): Response {
|
||||
$this->cookies[] = [$key, $value, $timeout, $path, $domain];
|
||||
if (!$this->sandbox) {
|
||||
$this->cookies[] = [$key, $value, $timeout, $path, $domain];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -247,8 +290,11 @@ class Response
|
||||
*/
|
||||
public function setContentType(string $contentType = 'text/html'): Response
|
||||
{
|
||||
$this->contentType = $contentType;
|
||||
$this->setHeader('Content-Type', $this->contentType . '; charset=' . $this->charset);
|
||||
if (!$this->sandbox) {
|
||||
$this->contentType = $contentType;
|
||||
$this->setHeader('Content-Type', $this->contentType . '; charset=' . $this->charset);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -270,8 +316,11 @@ class Response
|
||||
*/
|
||||
public function setCharset(string $charset): Response
|
||||
{
|
||||
$this->charset = $charset;
|
||||
$this->setHeader('Content-Type', $this->contentType . '; charset=' . $this->charset);
|
||||
if (!$this->sandbox) {
|
||||
$this->charset = $charset;
|
||||
$this->setHeader('Content-Type', $this->contentType . '; charset=' . $this->charset);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -283,7 +332,10 @@ class Response
|
||||
*/
|
||||
public function addResponder(callable $responder): Response
|
||||
{
|
||||
$this->responders[] = $responder;
|
||||
if (!$this->sandbox) {
|
||||
$this->responders[] = $responder;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ namespace Typecho;
|
||||
use Typecho\Widget\Helper\EmptyClass;
|
||||
use Typecho\Widget\Request as WidgetRequest;
|
||||
use Typecho\Widget\Response as WidgetResponse;
|
||||
use Typecho\Widget\Terminal;
|
||||
|
||||
/**
|
||||
* Typecho组件基类
|
||||
@ -117,14 +118,14 @@ abstract class Widget
|
||||
* @param class-string $alias 组件别名
|
||||
* @param mixed $params 传递的参数
|
||||
* @param mixed $request 前端参数
|
||||
* @param boolean $enableResponse 是否允许http回执
|
||||
* @param bool|callable $call 回调
|
||||
* @return Widget
|
||||
*/
|
||||
public static function widget(
|
||||
string $alias,
|
||||
$params = null,
|
||||
$request = null,
|
||||
bool $enableResponse = true
|
||||
$call = true
|
||||
): Widget {
|
||||
[$className] = explode('@', $alias);
|
||||
$key = Common::nativeClassName($alias);
|
||||
@ -133,17 +134,36 @@ abstract class Widget
|
||||
$className = self::$widgetAlias[$className];
|
||||
}
|
||||
|
||||
if (!isset(self::$widgetPool[$key])) {
|
||||
/** 初始化request */
|
||||
$requestObject = new WidgetRequest(Request::getInstance(), Config::factory($request));
|
||||
$sandbox = false;
|
||||
|
||||
/** 初始化response */
|
||||
$responseObject = new WidgetResponse(Request::getInstance(), Response::getInstance(), $enableResponse);
|
||||
if (isset($request) || $call === true || is_callable($call)) {
|
||||
$sandbox = true;
|
||||
Request::getInstance()->beginSandbox(new Config($request));
|
||||
Response::getInstance()->beginSandbox();
|
||||
}
|
||||
|
||||
/** 初始化组件 */
|
||||
$widget = new $className($requestObject, $responseObject, $params);
|
||||
if ($sandbox || !isset(self::$widgetPool[$key])) {
|
||||
$requestObject = new WidgetRequest(Request::getInstance());
|
||||
$responseObject = new WidgetResponse(Request::getInstance(), Response::getInstance());
|
||||
|
||||
try {
|
||||
$widget = new $className($requestObject, $responseObject, $params);
|
||||
$widget->execute();
|
||||
|
||||
if ($sandbox && is_callable($call)) {
|
||||
call_user_func($call, $widget);
|
||||
}
|
||||
} catch (Terminal $e) {
|
||||
$widget = null;
|
||||
} finally {
|
||||
if ($sandbox) {
|
||||
Response::getInstance()->endSandbox();
|
||||
Request::getInstance()->endSandbox();
|
||||
|
||||
return $widget;
|
||||
}
|
||||
}
|
||||
|
||||
$widget->execute();
|
||||
self::$widgetPool[$key] = $widget;
|
||||
}
|
||||
|
||||
@ -155,12 +175,12 @@ abstract class Widget
|
||||
*
|
||||
* @param mixed $params
|
||||
* @param mixed $request
|
||||
* @param bool $enableResponse
|
||||
* @param mixed $call
|
||||
* @return $this
|
||||
*/
|
||||
public static function alloc($params = null, $request = null, bool $enableResponse = true): Widget
|
||||
public static function alloc($params = null, $request = null, $call = true): Widget
|
||||
{
|
||||
return self::widget(static::class, $params, $request, $enableResponse);
|
||||
return self::widget(static::class, $params, $request, $call);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,16 +189,16 @@ abstract class Widget
|
||||
* @param string $alias
|
||||
* @param mixed $params
|
||||
* @param mixed $request
|
||||
* @param bool $enableResponse
|
||||
* @param mixed $call
|
||||
* @return $this
|
||||
*/
|
||||
public static function allocWithAlias(
|
||||
string $alias,
|
||||
$params = null,
|
||||
$request = null,
|
||||
bool $enableResponse = true
|
||||
$call = true
|
||||
): Widget {
|
||||
return self::widget(static::class . '@' . $alias, $params, $request, $enableResponse);
|
||||
return self::widget(static::class . '@' . $alias, $params, $request, $call);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,7 +260,7 @@ abstract class Widget
|
||||
* 将类本身赋值
|
||||
*
|
||||
* @param mixed $variable 变量名
|
||||
* @return Widget
|
||||
* @return $this
|
||||
*/
|
||||
public function to(&$variable): Widget
|
||||
{
|
||||
|
@ -45,12 +45,11 @@ class Request
|
||||
|
||||
/**
|
||||
* @param HttpRequest $request
|
||||
* @param Config $params
|
||||
*/
|
||||
public function __construct(HttpRequest $request, Config $params)
|
||||
public function __construct(HttpRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->params = $params;
|
||||
$this->params = new Config();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,21 +21,14 @@ class Response
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $enabled;
|
||||
|
||||
/**
|
||||
* @param HttpRequest $request
|
||||
* @param HttpResponse $response
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function __construct(HttpRequest $request, HttpResponse $response, bool $enabled = true)
|
||||
public function __construct(HttpRequest $request, HttpResponse $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->enabled = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,9 +37,7 @@ class Response
|
||||
*/
|
||||
public function setStatus(int $code): Response
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$this->response->setStatus($code);
|
||||
}
|
||||
$this->response->setStatus($code);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -57,9 +48,7 @@ class Response
|
||||
*/
|
||||
public function setHeader(string $name, $value): Response
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$this->response->setHeader($name, (string)$value);
|
||||
}
|
||||
$this->response->setHeader($name, (string)$value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -71,9 +60,7 @@ class Response
|
||||
*/
|
||||
public function setCharset(string $charset): Response
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$this->response->setCharset($charset);
|
||||
}
|
||||
$this->response->setCharset($charset);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -83,9 +70,7 @@ class Response
|
||||
*/
|
||||
public function setContentType(string $contentType = 'text/html'): Response
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$this->response->setContentType($contentType);
|
||||
}
|
||||
$this->response->setContentType($contentType);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -95,10 +80,6 @@ class Response
|
||||
*/
|
||||
public function throwContent(string $content, string $contentType = 'text/html')
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->setContentType($contentType)
|
||||
->addResponder(function () use ($content) {
|
||||
echo $content;
|
||||
@ -111,10 +92,6 @@ class Response
|
||||
*/
|
||||
public function throwXml(string $message)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->setContentType('text/xml')
|
||||
->addResponder(function () use ($message) {
|
||||
echo '<?xml version="1.0" encoding="' . $this->response->getCharset() . '"?>',
|
||||
@ -132,10 +109,6 @@ class Response
|
||||
*/
|
||||
public function throwJson($message)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** 设置http头信息 */
|
||||
$this->response->setContentType('application/json')
|
||||
->addResponder(function () use ($message) {
|
||||
@ -150,10 +123,6 @@ class Response
|
||||
*/
|
||||
public function throwFile($file, ?string $contentType = null)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($contentType)) {
|
||||
$this->response->setContentType($contentType);
|
||||
}
|
||||
@ -173,10 +142,6 @@ class Response
|
||||
*/
|
||||
public function redirect(string $location, bool $isPermanently = false)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$location = Common::safeUrl($location);
|
||||
|
||||
$this->response->setStatus($isPermanently ? 301 : 302)
|
||||
@ -192,10 +157,6 @@ class Response
|
||||
*/
|
||||
public function goBack(string $suffix = null, string $default = null)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
//获取来源
|
||||
$referer = $this->request->getReferer();
|
||||
|
||||
|
58
var/Typecho/Widget/Sandbox.php
Normal file
58
var/Typecho/Widget/Sandbox.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Typecho\Widget;
|
||||
|
||||
use Typecho\Config;
|
||||
use Typecho\Request as HttpRequest;
|
||||
use Typecho\Response as HttpResponse;
|
||||
|
||||
/**
|
||||
* sandbox env
|
||||
*/
|
||||
class Sandbox
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* @param Config $params
|
||||
*/
|
||||
public function __construct(Config $params)
|
||||
{
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $params
|
||||
* @return Sandbox
|
||||
*/
|
||||
public static function factory($params = null): Sandbox
|
||||
{
|
||||
return new self(new Config($params));
|
||||
}
|
||||
|
||||
/**
|
||||
* run function in a sandbox
|
||||
*
|
||||
* @param callable $call
|
||||
* @return mixed
|
||||
*/
|
||||
public function run(callable $call)
|
||||
{
|
||||
HttpRequest::getInstance()->beginSandbox($this->params);
|
||||
HttpResponse::getInstance()->beginSandbox();
|
||||
|
||||
try {
|
||||
$result = call_user_func($call);
|
||||
} catch (Terminal $e) {
|
||||
$result = null;
|
||||
} finally {
|
||||
HttpResponse::getInstance()->endSandbox();
|
||||
HttpRequest::getInstance()->endSandbox();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
14
var/Typecho/Widget/Terminal.php
Normal file
14
var/Typecho/Widget/Terminal.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typecho\Widget;
|
||||
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special exception to break executor
|
||||
*/
|
||||
class Terminal extends Exception
|
||||
{
|
||||
}
|
@ -5,6 +5,7 @@ namespace Utils;
|
||||
use Typecho\Common;
|
||||
use Typecho\Db;
|
||||
use Typecho\Exception;
|
||||
use Typecho\Widget\Sandbox;
|
||||
use Widget\Options;
|
||||
use Widget\Themes\Edit;
|
||||
use Widget\Upload;
|
||||
@ -962,7 +963,10 @@ Typecho_Date::setTimezoneOffset($options->timezone);
|
||||
*/
|
||||
public static function v0_8r10_5_17($db, $options)
|
||||
{
|
||||
Edit::alloc(null, 'change=' . $options->theme, false)->action();
|
||||
Sandbox::factory('change=' . $options->theme)
|
||||
->run(function () {
|
||||
Edit::alloc()->action();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -627,7 +627,8 @@ class Edit extends Contents implements ActionInterface
|
||||
if (!empty($attachments)) {
|
||||
foreach ($attachments as $key => $attachment) {
|
||||
$this->db->query($this->db->update('table.contents')->rows([
|
||||
'parent' => $cid, 'status' => 'publish',
|
||||
'parent' => $cid,
|
||||
'status' => 'publish',
|
||||
'order' => $key + 1
|
||||
])->where('cid = ? AND type = ?', $attachment, 'attachment'));
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use Typecho\Http\Client;
|
||||
use Typecho\Plugin;
|
||||
use Typecho\Response;
|
||||
use Typecho\Widget\Exception;
|
||||
use Typecho\Widget\Sandbox;
|
||||
use Widget\Base\Options as BaseOptions;
|
||||
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) {
|
||||
@ -53,7 +54,10 @@ class Service extends BaseOptions implements ActionInterface
|
||||
}
|
||||
|
||||
/** 获取post */
|
||||
$post = Archive::alloc("type=post", "cid={$this->request->cid}", false);
|
||||
$post = Sandbox::factory("cid={$this->request->cid}")
|
||||
->run(function () {
|
||||
return Archive::alloc('type=post');
|
||||
});
|
||||
|
||||
if ($post->have() && preg_match_all("|<a[^>]*href=[\"'](.*?)[\"'][^>]*>(.*?)</a>|", $post->text, $matches)) {
|
||||
$links = array_unique($matches[1]);
|
||||
|
@ -439,11 +439,13 @@ class XmlRpc extends Contents implements ActionInterface, Hook
|
||||
|
||||
/** 调用已有组件 */
|
||||
if ('page' == $type) {
|
||||
$widget = PageEdit::alloc(null, $input, false);
|
||||
$widget->writePage();
|
||||
$widget = PageEdit::alloc(null, $input, function (PageEdit $page) {
|
||||
$page->writePage();
|
||||
});
|
||||
} else {
|
||||
$widget = PostEdit::alloc(null, $input, false);
|
||||
$widget->writePost();
|
||||
$widget = PostEdit::alloc(null, $input, function (PostEdit $post) {
|
||||
$post->writePost();
|
||||
});
|
||||
}
|
||||
|
||||
return $widget->cid;
|
||||
@ -469,8 +471,9 @@ class XmlRpc extends Contents implements ActionInterface, Hook
|
||||
$input['description'] = $category['description'] ?? $category['name'];
|
||||
|
||||
/** 调用已有组件 */
|
||||
$categoryWidget = CategoryEdit::alloc(null, $input, false);
|
||||
$categoryWidget->insertCategory();
|
||||
$categoryWidget = CategoryEdit::alloc(null, $input, function (CategoryEdit $category) {
|
||||
$category->insertCategory();
|
||||
});
|
||||
return $categoryWidget->mid;
|
||||
}
|
||||
|
||||
@ -486,7 +489,9 @@ class XmlRpc extends Contents implements ActionInterface, Hook
|
||||
*/
|
||||
public function wpDeletePage(int $blogId, string $userName, string $password, int $pageId): bool
|
||||
{
|
||||
PageEdit::alloc(null, ['cid' => $pageId], false)->deletePage();
|
||||
PageEdit::alloc(null, ['cid' => $pageId], function (PageEdit $page) {
|
||||
$page->deletePage();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user