Widget.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <?php
  2. namespace Typecho;
  3. use Typecho\Widget\Helper\EmptyClass;
  4. use Typecho\Widget\Request as WidgetRequest;
  5. use Typecho\Widget\Response as WidgetResponse;
  6. use Typecho\Widget\Terminal;
  7. /**
  8. * Typecho组件基类
  9. *
  10. * @property $sequence
  11. * @property $length
  12. * @property-read $request
  13. * @property-read $response
  14. * @property-read $parameter
  15. */
  16. abstract class Widget
  17. {
  18. /**
  19. * widget对象池
  20. *
  21. * @var array
  22. */
  23. private static $widgetPool = [];
  24. /**
  25. * widget别名
  26. *
  27. * @var array
  28. */
  29. private static $widgetAlias = [];
  30. /**
  31. * request对象
  32. *
  33. * @var WidgetRequest
  34. */
  35. protected $request;
  36. /**
  37. * response对象
  38. *
  39. * @var WidgetResponse
  40. */
  41. protected $response;
  42. /**
  43. * 数据堆栈
  44. *
  45. * @var array
  46. */
  47. protected $stack = [];
  48. /**
  49. * 当前队列指针顺序值,从1开始
  50. *
  51. * @var integer
  52. */
  53. protected $sequence = 0;
  54. /**
  55. * 队列长度
  56. *
  57. * @var integer
  58. */
  59. protected $length = 0;
  60. /**
  61. * config对象
  62. *
  63. * @var Config
  64. */
  65. protected $parameter;
  66. /**
  67. * 数据堆栈每一行
  68. *
  69. * @var array
  70. */
  71. protected $row = [];
  72. /**
  73. * 构造函数,初始化组件
  74. *
  75. * @param WidgetRequest $request request对象
  76. * @param WidgetResponse $response response对象
  77. * @param mixed $params 参数列表
  78. */
  79. public function __construct(WidgetRequest $request, WidgetResponse $response, $params = null)
  80. {
  81. //设置函数内部对象
  82. $this->request = $request;
  83. $this->response = $response;
  84. $this->parameter = Config::factory($params);
  85. $this->init();
  86. }
  87. /**
  88. * init method
  89. */
  90. protected function init()
  91. {
  92. }
  93. /**
  94. * widget别名
  95. *
  96. * @param string $widgetClass
  97. * @param string $aliasClass
  98. */
  99. public static function alias(string $widgetClass, string $aliasClass)
  100. {
  101. self::$widgetAlias[$widgetClass] = $aliasClass;
  102. }
  103. /**
  104. * 工厂方法,将类静态化放置到列表中
  105. *
  106. * @param class-string $alias 组件别名
  107. * @param mixed $params 传递的参数
  108. * @param mixed $request 前端参数
  109. * @param bool|callable $disableSandboxOrCallback 回调
  110. * @return Widget
  111. */
  112. public static function widget(
  113. string $alias,
  114. $params = null,
  115. $request = null,
  116. $disableSandboxOrCallback = true
  117. ): Widget {
  118. [$className] = explode('@', $alias);
  119. $key = Common::nativeClassName($alias);
  120. if (isset(self::$widgetAlias[$className])) {
  121. $className = self::$widgetAlias[$className];
  122. }
  123. $sandbox = false;
  124. if ($disableSandboxOrCallback === false || is_callable($disableSandboxOrCallback)) {
  125. $sandbox = true;
  126. Request::getInstance()->beginSandbox(new Config($request));
  127. Response::getInstance()->beginSandbox();
  128. }
  129. if ($sandbox || !isset(self::$widgetPool[$key])) {
  130. $requestObject = new WidgetRequest(Request::getInstance(), isset($request) ? new Config($request) : null);
  131. $responseObject = new WidgetResponse(Request::getInstance(), Response::getInstance());
  132. try {
  133. $widget = new $className($requestObject, $responseObject, $params);
  134. $widget->execute();
  135. if ($sandbox && is_callable($disableSandboxOrCallback)) {
  136. call_user_func($disableSandboxOrCallback, $widget);
  137. }
  138. } catch (Terminal $e) {
  139. $widget = $widget ?? null;
  140. } finally {
  141. if ($sandbox) {
  142. Response::getInstance()->endSandbox();
  143. Request::getInstance()->endSandbox();
  144. return $widget;
  145. }
  146. }
  147. self::$widgetPool[$key] = $widget;
  148. }
  149. return self::$widgetPool[$key];
  150. }
  151. /**
  152. * alloc widget instance
  153. *
  154. * @param mixed $params
  155. * @param mixed $request
  156. * @param bool|callable $disableSandboxOrCallback
  157. * @return $this
  158. */
  159. public static function alloc($params = null, $request = null, $disableSandboxOrCallback = true): Widget
  160. {
  161. return self::widget(static::class, $params, $request, $disableSandboxOrCallback);
  162. }
  163. /**
  164. * alloc widget instance with alias
  165. *
  166. * @param string|null $alias
  167. * @param mixed $params
  168. * @param mixed $request
  169. * @param bool|callable $disableSandboxOrCallback
  170. * @return $this
  171. */
  172. public static function allocWithAlias(
  173. ?string $alias,
  174. $params = null,
  175. $request = null,
  176. $disableSandboxOrCallback = true
  177. ): Widget {
  178. return self::widget(
  179. static::class . (isset($alias) ? '@' . $alias : ''),
  180. $params,
  181. $request,
  182. $disableSandboxOrCallback
  183. );
  184. }
  185. /**
  186. * 释放组件
  187. *
  188. * @param string $alias 组件名称
  189. * @deprecated alias for destroy
  190. */
  191. public static function destory(string $alias)
  192. {
  193. self::destroy($alias);
  194. }
  195. /**
  196. * 释放组件
  197. *
  198. * @param string|null $alias 组件名称
  199. */
  200. public static function destroy(?string $alias = null)
  201. {
  202. if (Common::nativeClassName(static::class) == 'Typecho_Widget') {
  203. if (isset($alias)) {
  204. unset(self::$widgetPool[$alias]);
  205. } else {
  206. self::$widgetPool = [];
  207. }
  208. } else {
  209. $alias = static::class . (isset($alias) ? '@' . $alias : '');
  210. unset(self::$widgetPool[$alias]);
  211. }
  212. }
  213. /**
  214. * execute function.
  215. */
  216. public function execute()
  217. {
  218. }
  219. /**
  220. * post事件触发
  221. *
  222. * @param boolean $condition 触发条件
  223. *
  224. * @return $this|EmptyClass
  225. */
  226. public function on(bool $condition)
  227. {
  228. if ($condition) {
  229. return $this;
  230. } else {
  231. return new EmptyClass();
  232. }
  233. }
  234. /**
  235. * 将类本身赋值
  236. *
  237. * @param mixed $variable 变量名
  238. * @return $this
  239. */
  240. public function to(&$variable): Widget
  241. {
  242. return $variable = $this;
  243. }
  244. /**
  245. * 格式化解析堆栈内的所有数据
  246. *
  247. * @param string $format 数据格式
  248. */
  249. public function parse(string $format)
  250. {
  251. while ($this->next()) {
  252. echo preg_replace_callback(
  253. "/\{([_a-z0-9]+)\}/i",
  254. function (array $matches) {
  255. return $this->{$matches[1]};
  256. },
  257. $format
  258. );
  259. }
  260. }
  261. /**
  262. * 返回堆栈每一行的值
  263. *
  264. * @return mixed
  265. */
  266. public function next()
  267. {
  268. $key = key($this->stack);
  269. if ($key !== null && isset($this->stack[$key])) {
  270. $this->row = current($this->stack);
  271. next($this->stack);
  272. $this->sequence++;
  273. } else {
  274. reset($this->stack);
  275. $this->sequence = 0;
  276. return false;
  277. }
  278. return $this->row;
  279. }
  280. /**
  281. * 将每一行的值压入堆栈
  282. *
  283. * @param array $value 每一行的值
  284. * @return mixed
  285. */
  286. public function push(array $value)
  287. {
  288. //将行数据按顺序置位
  289. $this->row = $value;
  290. $this->length++;
  291. $this->stack[] = $value;
  292. return $value;
  293. }
  294. /**
  295. * 根据余数输出
  296. *
  297. * @param mixed ...$args
  298. */
  299. public function alt(...$args)
  300. {
  301. $num = count($args);
  302. $split = $this->sequence % $num;
  303. echo $args[(0 == $split ? $num : $split) - 1];
  304. }
  305. /**
  306. * 返回堆栈是否为空
  307. *
  308. * @return boolean
  309. */
  310. public function have(): bool
  311. {
  312. return !empty($this->stack);
  313. }
  314. /**
  315. * 魔术函数,用于挂接其它函数
  316. *
  317. * @param string $name 函数名
  318. * @param array $args 函数参数
  319. */
  320. public function __call(string $name, array $args)
  321. {
  322. $method = 'call' . ucfirst($name);
  323. self::pluginHandle()->trigger($plugged)->{$method}($this, $args);
  324. if (!$plugged) {
  325. echo $this->{$name};
  326. }
  327. }
  328. /**
  329. * 获取对象插件句柄
  330. *
  331. * @return Plugin
  332. */
  333. public static function pluginHandle(): Plugin
  334. {
  335. return Plugin::factory(static::class);
  336. }
  337. /**
  338. * 魔术函数,用于获取内部变量
  339. *
  340. * @param string $name 变量名
  341. * @return mixed
  342. */
  343. public function __get(string $name)
  344. {
  345. if (array_key_exists($name, $this->row)) {
  346. return $this->row[$name];
  347. } else {
  348. $method = '___' . $name;
  349. if (method_exists($this, $method)) {
  350. return $this->$method();
  351. } else {
  352. $return = self::pluginHandle()->trigger($plugged)->{$method}($this);
  353. if ($plugged) {
  354. return $return;
  355. }
  356. }
  357. }
  358. return null;
  359. }
  360. /**
  361. * 设定堆栈每一行的值
  362. *
  363. * @param string $name 值对应的键值
  364. * @param mixed $value 相应的值
  365. */
  366. public function __set(string $name, $value)
  367. {
  368. $this->row[$name] = $value;
  369. }
  370. /**
  371. * 验证堆栈值是否存在
  372. *
  373. * @param string $name
  374. * @return boolean
  375. */
  376. public function __isSet(string $name)
  377. {
  378. return isset($this->row[$name]);
  379. }
  380. /**
  381. * 输出顺序值
  382. *
  383. * @return int
  384. */
  385. public function ___sequence(): int
  386. {
  387. return $this->sequence;
  388. }
  389. /**
  390. * 输出数据长度
  391. *
  392. * @return int
  393. */
  394. public function ___length(): int
  395. {
  396. return $this->length;
  397. }
  398. /**
  399. * @return WidgetRequest
  400. */
  401. public function ___request(): WidgetRequest
  402. {
  403. return $this->request;
  404. }
  405. /**
  406. * @return WidgetResponse
  407. */
  408. public function ___response(): WidgetResponse
  409. {
  410. return $this->response;
  411. }
  412. /**
  413. * @return Config
  414. */
  415. public function ___parameter(): Config
  416. {
  417. return $this->parameter;
  418. }
  419. }