552 lines
12 KiB
PHP
552 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Typecho;
|
|
|
|
use Typecho\Widget\Helper\EmptyClass;
|
|
use Typecho\Widget\Request as WidgetRequest;
|
|
use Typecho\Widget\Response as WidgetResponse;
|
|
use Typecho\Widget\Terminal;
|
|
|
|
/**
|
|
* Typecho组件基类
|
|
*
|
|
* @property $sequence
|
|
* @property $length
|
|
* @property-read $request
|
|
* @property-read $response
|
|
* @property-read $parameter
|
|
*/
|
|
abstract class Widget
|
|
{
|
|
/**
|
|
* widget对象池
|
|
*
|
|
* @var array
|
|
*/
|
|
private static array $widgetPool = [];
|
|
|
|
/**
|
|
* widget别名
|
|
*
|
|
* @var array
|
|
*/
|
|
private static array $widgetAlias = [];
|
|
|
|
/**
|
|
* request对象
|
|
*
|
|
* @var WidgetRequest
|
|
*/
|
|
protected WidgetRequest $request;
|
|
|
|
/**
|
|
* response对象
|
|
*
|
|
* @var WidgetResponse
|
|
*/
|
|
protected WidgetResponse $response;
|
|
|
|
/**
|
|
* 数据堆栈
|
|
*
|
|
* @var array
|
|
*/
|
|
protected array $stack = [];
|
|
|
|
/**
|
|
* 当前队列指针顺序值,从1开始
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected int $sequence = 0;
|
|
|
|
/**
|
|
* 队列长度
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected int $length = 0;
|
|
|
|
/**
|
|
* config对象
|
|
*
|
|
* @var Config
|
|
*/
|
|
protected Config $parameter;
|
|
|
|
/**
|
|
* 数据堆栈每一行
|
|
*
|
|
* @var array
|
|
*/
|
|
protected array $row = [];
|
|
|
|
/**
|
|
* 构造函数,初始化组件
|
|
*
|
|
* @param WidgetRequest $request request对象
|
|
* @param WidgetResponse $response response对象
|
|
* @param mixed $params 参数列表
|
|
*/
|
|
public function __construct(WidgetRequest $request, WidgetResponse $response, $params = null)
|
|
{
|
|
//设置函数内部对象
|
|
$this->request = $request;
|
|
$this->response = $response;
|
|
$this->parameter = Config::factory($params);
|
|
|
|
$this->init();
|
|
}
|
|
|
|
/**
|
|
* init method
|
|
*/
|
|
protected function init()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* widget别名
|
|
*
|
|
* @param string $widgetClass
|
|
* @param string $aliasClass
|
|
*/
|
|
public static function alias(string $widgetClass, string $aliasClass)
|
|
{
|
|
self::$widgetAlias[$widgetClass] = $aliasClass;
|
|
}
|
|
|
|
/**
|
|
* 工厂方法,将类静态化放置到列表中
|
|
*
|
|
* @param class-string $alias 组件别名
|
|
* @param mixed $params 传递的参数
|
|
* @param mixed $request 前端参数
|
|
* @param bool|callable $disableSandboxOrCallback 回调
|
|
* @return Widget
|
|
*/
|
|
public static function widget(
|
|
string $alias,
|
|
$params = null,
|
|
$request = null,
|
|
$disableSandboxOrCallback = true
|
|
): Widget {
|
|
[$className] = explode('@', $alias);
|
|
$key = Common::nativeClassName($alias);
|
|
|
|
if (isset(self::$widgetAlias[$className])) {
|
|
$className = self::$widgetAlias[$className];
|
|
}
|
|
|
|
$sandbox = false;
|
|
|
|
if ($disableSandboxOrCallback === false || is_callable($disableSandboxOrCallback)) {
|
|
$sandbox = true;
|
|
Request::getInstance()->beginSandbox(new Config($request));
|
|
Response::getInstance()->beginSandbox();
|
|
}
|
|
|
|
if ($sandbox || !isset(self::$widgetPool[$key])) {
|
|
$requestObject = new WidgetRequest(Request::getInstance(), isset($request) ? new Config($request) : null);
|
|
$responseObject = new WidgetResponse(Request::getInstance(), Response::getInstance());
|
|
|
|
try {
|
|
$widget = new $className($requestObject, $responseObject, $params);
|
|
$widget->execute();
|
|
|
|
if ($sandbox && is_callable($disableSandboxOrCallback)) {
|
|
call_user_func($disableSandboxOrCallback, $widget);
|
|
}
|
|
} catch (Terminal $e) {
|
|
$widget = $widget ?? null;
|
|
} finally {
|
|
if ($sandbox) {
|
|
Response::getInstance()->endSandbox();
|
|
Request::getInstance()->endSandbox();
|
|
|
|
return $widget;
|
|
}
|
|
}
|
|
|
|
self::$widgetPool[$key] = $widget;
|
|
}
|
|
|
|
return self::$widgetPool[$key];
|
|
}
|
|
|
|
/**
|
|
* alloc widget instance
|
|
*
|
|
* @param mixed $params
|
|
* @param mixed $request
|
|
* @param bool|callable $disableSandboxOrCallback
|
|
* @return $this
|
|
*/
|
|
public static function alloc($params = null, $request = null, $disableSandboxOrCallback = true): Widget
|
|
{
|
|
return self::widget(static::class, $params, $request, $disableSandboxOrCallback);
|
|
}
|
|
|
|
/**
|
|
* alloc widget instance with alias
|
|
*
|
|
* @param string|null $alias
|
|
* @param mixed $params
|
|
* @param mixed $request
|
|
* @param bool|callable $disableSandboxOrCallback
|
|
* @return $this
|
|
*/
|
|
public static function allocWithAlias(
|
|
?string $alias,
|
|
$params = null,
|
|
$request = null,
|
|
$disableSandboxOrCallback = true
|
|
): Widget {
|
|
return self::widget(
|
|
static::class . (isset($alias) ? '@' . $alias : ''),
|
|
$params,
|
|
$request,
|
|
$disableSandboxOrCallback
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 释放组件
|
|
*
|
|
* @param string $alias 组件名称
|
|
* @deprecated alias for destroy
|
|
*/
|
|
public static function destory(string $alias)
|
|
{
|
|
self::destroy($alias);
|
|
}
|
|
|
|
/**
|
|
* 释放组件
|
|
*
|
|
* @param string|null $alias 组件名称
|
|
*/
|
|
public static function destroy(?string $alias = null)
|
|
{
|
|
if (Common::nativeClassName(static::class) == 'Typecho_Widget') {
|
|
if (isset($alias)) {
|
|
unset(self::$widgetPool[$alias]);
|
|
} else {
|
|
self::$widgetPool = [];
|
|
}
|
|
} else {
|
|
$alias = static::class . (isset($alias) ? '@' . $alias : '');
|
|
unset(self::$widgetPool[$alias]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* execute function.
|
|
*/
|
|
public function execute()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* post事件触发
|
|
*
|
|
* @param boolean $condition 触发条件
|
|
*
|
|
* @return $this|EmptyClass
|
|
*/
|
|
public function on(bool $condition)
|
|
{
|
|
if ($condition) {
|
|
return $this;
|
|
} else {
|
|
return new EmptyClass();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 将类本身赋值
|
|
*
|
|
* @param mixed $variable 变量名
|
|
* @return $this
|
|
*/
|
|
public function to(&$variable): Widget
|
|
{
|
|
return $variable = $this;
|
|
}
|
|
|
|
/**
|
|
* 按模版渲染
|
|
*
|
|
* @param string $template 模版
|
|
* @return string
|
|
*/
|
|
public function template(string $template): string
|
|
{
|
|
return preg_replace_callback(
|
|
"/\{([_a-z0-9]+)\}/i",
|
|
function (array $matches) {
|
|
return $this->{$matches[1]};
|
|
},
|
|
$template
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 格式化解析堆栈内的所有数据
|
|
*
|
|
* @param string $template 模版
|
|
*/
|
|
public function parse(string $template)
|
|
{
|
|
while ($this->next()) {
|
|
echo $this->template($template);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|array $column
|
|
* @return array|mixed|null
|
|
*/
|
|
public function toColumn($column)
|
|
{
|
|
if (is_array($column)) {
|
|
$item = [];
|
|
foreach ($column as $key) {
|
|
$item[$key] = $this->{$key};
|
|
}
|
|
|
|
return $item;
|
|
} else {
|
|
return $this->{$column};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|array $column
|
|
* @return array
|
|
*/
|
|
public function toArray($column): array
|
|
{
|
|
$result = [];
|
|
|
|
while ($this->next()) {
|
|
$result[] = $this->toColumn($column);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 返回堆栈每一行的值
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function next()
|
|
{
|
|
$key = key($this->stack);
|
|
|
|
if ($key !== null && isset($this->stack[$key])) {
|
|
$this->row = current($this->stack);
|
|
next($this->stack);
|
|
$this->sequence++;
|
|
} else {
|
|
reset($this->stack);
|
|
$this->sequence = 0;
|
|
return false;
|
|
}
|
|
|
|
return $this->row;
|
|
}
|
|
|
|
/**
|
|
* 将每一行的值压入堆栈
|
|
*
|
|
* @param array $value 每一行的值
|
|
* @return mixed
|
|
*/
|
|
public function push(array $value)
|
|
{
|
|
//将行数据按顺序置位
|
|
$this->row = $value;
|
|
$this->length++;
|
|
|
|
$this->stack[] = $value;
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* 将所有行的值压入堆栈
|
|
*
|
|
* @param array $values 所有行的值
|
|
*/
|
|
public function pushAll(array $values)
|
|
{
|
|
foreach ($values as $value) {
|
|
$this->push($value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 根据余数输出
|
|
*
|
|
* @param mixed ...$args
|
|
*/
|
|
public function alt(...$args)
|
|
{
|
|
$this->altBy($this->sequence, ...$args);
|
|
}
|
|
|
|
/**
|
|
* 根据余数输出
|
|
*
|
|
* @param int $current
|
|
* @param mixed ...$args
|
|
*/
|
|
public function altBy(int $current, ...$args)
|
|
{
|
|
$num = count($args);
|
|
$split = $current % $num;
|
|
echo $args[(0 == $split ? $num : $split) - 1];
|
|
}
|
|
|
|
/**
|
|
* 返回堆栈是否为空
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function have(): bool
|
|
{
|
|
return !empty($this->stack);
|
|
}
|
|
|
|
/**
|
|
* 魔术函数,用于挂接其它函数
|
|
*
|
|
* @param string $name 函数名
|
|
* @param array $args 函数参数
|
|
*/
|
|
public function __call(string $name, array $args)
|
|
{
|
|
$method = 'call' . ucfirst($name);
|
|
self::pluginHandle()->trigger($plugged)->call($method, $this, $args);
|
|
|
|
if (!$plugged) {
|
|
echo $this->{$name};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取对象插件句柄
|
|
*
|
|
* @return Plugin
|
|
*/
|
|
public static function pluginHandle(): Plugin
|
|
{
|
|
return Plugin::factory(static::class);
|
|
}
|
|
|
|
/**
|
|
* 魔术函数,用于获取内部变量
|
|
*
|
|
* @param string $name 变量名
|
|
* @return mixed
|
|
*/
|
|
public function __get(string $name)
|
|
{
|
|
$method = '___' . $name;
|
|
$key = '#' . $name;
|
|
|
|
if (array_key_exists($key, $this->row)) {
|
|
return $this->row[$key];
|
|
} elseif (method_exists($this, $method)) {
|
|
$this->row[$key] = $this->$method();
|
|
return $this->row[$key];
|
|
} elseif (array_key_exists($name, $this->row)) {
|
|
return $this->row[$name];
|
|
} else {
|
|
$return = self::pluginHandle()->trigger($plugged)->call($method, $this);
|
|
if ($plugged) {
|
|
return $return;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 设定堆栈每一行的值
|
|
*
|
|
* @param string $name 值对应的键值
|
|
* @param mixed $value 相应的值
|
|
*/
|
|
public function __set(string $name, $value)
|
|
{
|
|
$method = '___' . $name;
|
|
$key = '#' . $name;
|
|
|
|
if (isset($this->row[$key]) || method_exists($this, $method)) {
|
|
$this->row[$key] = $value;
|
|
} else {
|
|
$this->row[$name] = $value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证堆栈值是否存在
|
|
*
|
|
* @param string $name
|
|
* @return boolean
|
|
*/
|
|
public function __isSet(string $name)
|
|
{
|
|
$method = '___' . $name;
|
|
$key = '#' . $name;
|
|
|
|
return isset($this->row[$key]) || method_exists($this, $method) || isset($this->row[$name]);
|
|
}
|
|
|
|
/**
|
|
* 输出顺序值
|
|
*
|
|
* @return int
|
|
*/
|
|
public function ___sequence(): int
|
|
{
|
|
return $this->sequence;
|
|
}
|
|
|
|
/**
|
|
* 输出数据长度
|
|
*
|
|
* @return int
|
|
*/
|
|
public function ___length(): int
|
|
{
|
|
return $this->length;
|
|
}
|
|
|
|
/**
|
|
* @return WidgetRequest
|
|
*/
|
|
public function ___request(): WidgetRequest
|
|
{
|
|
return $this->request;
|
|
}
|
|
|
|
/**
|
|
* @return WidgetResponse
|
|
*/
|
|
public function ___response(): WidgetResponse
|
|
{
|
|
return $this->response;
|
|
}
|
|
|
|
/**
|
|
* @return Config
|
|
*/
|
|
public function ___parameter(): Config
|
|
{
|
|
return $this->parameter;
|
|
}
|
|
}
|