PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/pragwork/modules/Test/Application/Controller.php

https://github.com/szw/pragwork
PHP | 810 lines | 794 code | 16 blank | 0 comment | 9 complexity | a6266ed12a192bff562715296600df5a MD5 | raw file
  1. <?php
  2. namespace Application;
  3. abstract class Controller extends Singleton
  4. {
  5. static $layout = null;
  6. static $before_filter = null;
  7. static $before_render_filter = null;
  8. static $after_filter = null;
  9. static $exception_filter = null;
  10. static $caches_page = null;
  11. static $caches_action = null;
  12. public $params;
  13. public $session;
  14. public $cookies;
  15. public $flash;
  16. public $request;
  17. public $response;
  18. public $action;
  19. public $controller;
  20. protected static $instance;
  21. private static $_content;
  22. private static $_ch_file;
  23. private static $_ch_dir;
  24. private static $_ch_layout;
  25. private static $_ch_if;
  26. private static $_rendered;
  27. private static $_ssl_url;
  28. private static $_http_url;
  29. private static $_cache;
  30. protected function __construct()
  31. {
  32. $class = get_class($this);
  33. do
  34. {
  35. require HELPERS . str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 12, -10)) . 'Helper.php';
  36. }
  37. while (($class = get_parent_class($class)) !== __CLASS__);
  38. }
  39. /**
  40. * Resets all data of the current instance of the controller. It is handled
  41. * automatically by the test environment.
  42. */
  43. public final function clean_up()
  44. {
  45. self::$_content = null;
  46. self::$_ch_file = null;
  47. self::$_ch_dir = null;
  48. self::$_ch_layout = null;
  49. self::$_ch_if = null;
  50. self::$_rendered = null;
  51. self::$_http_url = null;
  52. self::$_ssl_url = null;
  53. self::$_cache = null;
  54. $_SERVER['SERVER_PORT'] = 80;
  55. if ($this->params)
  56. $this->params->clean_up();
  57. if ($this->session)
  58. $this->session->clean_up();
  59. if ($this->flash)
  60. $this->flash->clean_up();
  61. if ($this->cookies)
  62. $this->cookies->clean_up();
  63. if ($this->request)
  64. $this->request->clean_up();
  65. if ($this->response)
  66. $this->response->clean_up();
  67. static::$instance = null;
  68. $_SESSION = array();
  69. $_COOKIE = array();
  70. }
  71. public function process($request, $response)
  72. {
  73. $this->action = $action = $request->route['action'];
  74. $this->controller = $request->route['controller'];
  75. $config = Configuration::instance();
  76. self::$_ssl_url = $config->ssl_url();
  77. self::$_http_url = $config->http_url();
  78. self::$_cache = $config->cache;
  79. $this->request = $request;
  80. $this->response = $response;
  81. $this->params = Parameters::instance();
  82. $this->session = Session::instance();
  83. $this->flash = Flash::instance();
  84. $this->cookies = Cookies::instance();
  85. try
  86. {
  87. ob_start();
  88. $this->invoke_filters('before_filter');
  89. if (self::$_cache)
  90. {
  91. if (static::$caches_action)
  92. $this->set_action_cache($request->uri());
  93. if (static::$caches_page)
  94. $this->set_page_cache($request->uri());
  95. }
  96. $this->$action();
  97. if (!self::$_rendered)
  98. $this->render();
  99. $this->response->body = ob_get_clean();
  100. $this->invoke_filters('after_filter');
  101. }
  102. catch (StopException $e)
  103. {
  104. $this->response->body = ob_get_clean();
  105. }
  106. catch (\Exception $e)
  107. {
  108. if ($this->invoke_filters('exception_filter', $e) !== false)
  109. {
  110. $this->response->body = ob_get_clean();
  111. $this->response->status = 500;
  112. $this->response->exception = $e;
  113. throw $e;
  114. }
  115. }
  116. }
  117. public function default_url_options() {}
  118. public function expire_page($options=array())
  119. {
  120. $key = url_for($options);
  121. $qpos = strpos($key, '?');
  122. $start = strlen(($key[4] === 's') ? self::$_ssl_url : self::$_http_url);
  123. $key = ($qpos === false)
  124. ? trim(substr($key, $start), '/.')
  125. : trim(substr($key, $start, $qpos - $start), '/.');
  126. if (isset($key[0]))
  127. {
  128. $cached_page = str_replace('/', DIRECTORY_SEPARATOR, $key);
  129. if (substr($key, -5) !== '.html')
  130. $cached_page .= '.html';
  131. }
  132. else
  133. $cached_page = 'index.html';
  134. if ($this->response->storage->is_file($cached_page))
  135. {
  136. $this->response->storage->unlink($cached_page);
  137. while (($dir = dirname($cached_page)) !== '.')
  138. {
  139. $handle =& $this->response->storage->get_handle($dir);
  140. if (!empty($handle))
  141. break;
  142. $handle = null;
  143. $cached_page = $dir;
  144. }
  145. }
  146. }
  147. public function expire_action($options=array())
  148. {
  149. $key = url_for($options);
  150. $key = substr($key, strlen(($key[4] === 's') ? self::$_ssl_url : self::$_http_url));
  151. # if longer than 1 char (e.g. longer than '/')
  152. if (isset($key[1]))
  153. $key = rtrim($key, '/.');
  154. $cached_dir = TEMP . 'ca_' . md5($key);
  155. if ($this->response->storage->is_dir($cached_dir))
  156. $this->response->storage->unlink($cached_dir);
  157. $cached_action = $cached_dir . '.cache';
  158. if ($this->response->storage->is_file($cached_action))
  159. $this->response->storage->storage->unlink($cached_action);
  160. }
  161. public function fragment_exists($options=array())
  162. {
  163. return $this->response->storage->is_file(self::fragment_file($options));
  164. }
  165. public function expire_fragment($options=array())
  166. {
  167. $fragment = self::fragment_file($options);
  168. if ($this->response->storage->is_file($fragment))
  169. $this->response->storage->unlink($fragment);
  170. }
  171. public function cache($options, $closure)
  172. {
  173. if (!self::$_cache)
  174. return $closure($this);
  175. $frag = self::fragment_file($options);
  176. if ($this->response->storage->is_file($frag))
  177. echo $this->response->storage->file_get_contents($frag);
  178. ob_start();
  179. $closure($this);
  180. $output = ob_get_clean();
  181. $this->response->storage->file_put_contents($frag, $output);
  182. echo $output;
  183. }
  184. private static final function fragment_file($options)
  185. {
  186. $key = url_for($options);
  187. $key = substr($key,strlen(($key[4]==='s') ? self::$_ssl_url : self::$_http_url));
  188. if (isset($options['action_suffix']))
  189. $key .= $options['action_suffix'];
  190. return TEMP . 'cf_' . md5($key) . '.cache';
  191. }
  192. private final function render_cache_in_layout()
  193. {
  194. if ($this->response->storage->is_dir(self::$_ch_dir))
  195. {
  196. foreach($this->response->storage->scandir(self::$_ch_dir) as $f)
  197. {
  198. self::$_content[$f] = $this->response->storage->file_get_contents(
  199. self::$_ch_dir . DIRECTORY_SEPARATOR . $file);
  200. }
  201. }
  202. $this->render(array(
  203. 'text' => $this->response->storage->file_get_contents(self::$_ch_file),
  204. 'layout' => true
  205. ));
  206. throw new StopException;
  207. }
  208. private final function set_action_cache($key)
  209. {
  210. if (self::$_ch_file)
  211. return;
  212. foreach (self::normalize_defs(static::$caches_action) as $ch)
  213. {
  214. if ($ch[0] !== $this->action)
  215. continue;
  216. if (isset($ch['cache_path']))
  217. {
  218. if ($ch['cache_path'][0] === '/')
  219. self::$_ch_dir = TEMP . 'ca_' . md5($ch['cache_path']);
  220. else
  221. {
  222. $chp = $this->$ch['cache_path']();
  223. if ($chp[0] === '/')
  224. self::$_ch_dir = TEMP . 'ca_' . md5($chp);
  225. elseif ($chp[4] === 's')
  226. self::$_ch_dir = TEMP . 'ca_' . md5(substr($chp, strlen(self::$_ssl_url)));
  227. else
  228. self::$_ch_dir = TEMP . 'ca_' . md5(substr($chp, strlen(self::$_http_url)));
  229. }
  230. }
  231. else
  232. self::$_ch_dir = TEMP . 'ca_' . md5(isset($key[1]) ? rtrim($key, '/.') : $key);
  233. self::$_ch_file = self::$_ch_dir . '.cache';
  234. if (isset($ch['layout']) && !$ch['layout'])
  235. {
  236. if ($this->response->storage->is_file(self::$_ch_file))
  237. $this->render_cache_in_layout();
  238. self::$_ch_layout = self::resolve_layout($this->action);
  239. static::$layout = null;
  240. }
  241. elseif ($this->response->storage->is_file(self::$_ch_file))
  242. {
  243. echo $this->response->storage->file_get_contents(self::$_ch_file);
  244. throw new StopException;
  245. }
  246. if (isset($ch['if']))
  247. self::$_ch_if = $ch['if'];
  248. self::add_to_filter('after_filter', 'write_and_show_cache');
  249. break;
  250. }
  251. }
  252. private final function set_page_cache($key)
  253. {
  254. if (self::$_ch_file)
  255. return;
  256. foreach (self::normalize_defs(static::$caches_page) as $ch)
  257. {
  258. if ($ch[0] !== $this->action)
  259. continue;
  260. $key = trim($key, '/.');
  261. if (isset($key[0]))
  262. {
  263. self::$_ch_file = str_replace('/', DIRECTORY_SEPARATOR, $key);
  264. if (!strpos($key, '.'))
  265. self::$_ch_file .= '.html';
  266. }
  267. else
  268. self::$_ch_file = 'index.html';
  269. if (isset($ch['if']))
  270. self::$_ch_if = $ch['if'];
  271. self::add_to_filter('after_filter', 'write_and_show_cache');
  272. break;
  273. }
  274. }
  275. private final function write_and_show_cache()
  276. {
  277. if (self::$_ch_if)
  278. {
  279. if ((array) self::$_ch_if === self::$_ch_if)
  280. {
  281. foreach (self::$_ch_if as $ifm)
  282. {
  283. if (!$this->$ifm())
  284. return;
  285. }
  286. }
  287. else
  288. {
  289. $ifm = self::$_ch_if;
  290. if (!$this->$ifm())
  291. return;
  292. }
  293. }
  294. if (!self::$_ch_dir) # if caches page
  295. {
  296. $dir = dirname(self::$_ch_file);
  297. if (!$this->response->storage->is_dir($dir))
  298. $this->response->storage->mkdir($dir);
  299. }
  300. $this->response->storage->file_put_contents(self::$_ch_file, $this->response->body);
  301. if (self::$_ch_layout)
  302. {
  303. if (self::$_content)
  304. {
  305. if ($this->response->storage->is_dir(self::$_ch_dir))
  306. {
  307. foreach ($this->response->storage->scandir(self::$_ch_dir) as $f)
  308. $this->response->storage->unlink(self::$_ch_dir . DIRECTORY_SEPARATOR . $f);
  309. }
  310. else
  311. $this->response->storage->mkdir(self::$_ch_dir);
  312. foreach (self::$_content as $r => $c)
  313. $this->response->storage->file_put_contents(self::$_ch_dir . DIRECTORY_SEPARATOR . $r, $c);
  314. }
  315. ob_start();
  316. $this->render(array('text' => $this->response->body, 'layout' => self::$_ch_layout));
  317. $this->response->body = ob_get_clean();
  318. }
  319. }
  320. public function redirect_to($options=array())
  321. {
  322. if ((array) $options !== $options)
  323. $this->redirect_to_url(url_for($options));
  324. elseif (!$this->session)
  325. $this->redirect_to_url(url_for($options), $options);
  326. $url = url_for($options);
  327. unset(
  328. $options['params'],
  329. $options[0],
  330. $options['name'],
  331. $options['ssl'],
  332. $options['anchor'],
  333. $options['locale'],
  334. $options['action'],
  335. $options['controller']
  336. );
  337. $this->redirect_to_url($url, $options);
  338. }
  339. public function redirect_to_url($url, $options=array())
  340. {
  341. if (isset($options['status']))
  342. {
  343. $this->response->status = $options['status'];
  344. unset($options['status']);
  345. }
  346. else
  347. $this->response->status = 302;
  348. if ($this->flash)
  349. {
  350. foreach ($options as $name => $msg)
  351. $this->flash->$name = $msg;
  352. }
  353. $this->response->location = $url;
  354. throw new StopException;
  355. }
  356. public function render($options=array())
  357. {
  358. if ((array) $options !== $options)
  359. {
  360. $template = VIEWS . ((strpos($options, '\\') === false)
  361. ? str_replace('\\', DIRECTORY_SEPARATOR, $this->controller) . DIRECTORY_SEPARATOR . $options
  362. : str_replace('\\', DIRECTORY_SEPARATOR, $options));
  363. $partial = $template . '.part.php';
  364. if (is_file($partial))
  365. {
  366. ob_start();
  367. require $partial;
  368. return ob_get_clean();
  369. }
  370. if (!self::$_rendered)
  371. {
  372. $this->invoke_filters('before_render_filter');
  373. self::$_rendered = true;
  374. }
  375. $this->response->template = str_replace(DIRECTORY_SEPARATOR, '\\', substr($template, strlen(VIEWS)));
  376. $layout = self::resolve_layout($this->action);
  377. if ($layout)
  378. {
  379. $this->response->layout = $layout;
  380. ob_start();
  381. require $template . '.php';
  382. self::$_content[0] = ob_get_clean();
  383. require VIEWS . 'layouts' . DIRECTORY_SEPARATOR . str_replace('\\',DIRECTORY_SEPARATOR,$layout) .'.php';
  384. }
  385. else
  386. require $template . '.php';
  387. return;
  388. }
  389. elseif (isset($options[0]))
  390. {
  391. $template = VIEWS . ((strpos($options[0], '\\') === false)
  392. ? str_replace('\\', DIRECTORY_SEPARATOR, $this->controller) . DIRECTORY_SEPARATOR . $options[0]
  393. : str_replace('\\', DIRECTORY_SEPARATOR, $options[0]));
  394. $partial = $template . '.part.php';
  395. if (is_file($partial))
  396. {
  397. unset($options[0]);
  398. ob_start();
  399. if (isset($options['collection']))
  400. {
  401. $name = basename($partial, '.part.php');
  402. $key_name = $name . '_key';
  403. foreach ($options['collection'] as $key => $item)
  404. {
  405. $this->render_partial(
  406. $partial,
  407. array($name => $item, $key_name => $key) + $options
  408. );
  409. }
  410. }
  411. else
  412. $this->render_partial($partial, $options);
  413. return ob_get_clean();
  414. }
  415. }
  416. elseif (isset($options['xml']))
  417. {
  418. if (!isset($options['content_type']))
  419. $options['content_type'] = 'application/xml';
  420. $options['text'] = $xml;
  421. }
  422. elseif (isset($options['json']))
  423. {
  424. if (!isset($options['content_type']))
  425. $options['content_type'] = 'application/json';
  426. $options['text'] = isset($options['json_options'])
  427. ? json_encode($options['json'], $options['json_options'])
  428. : json_encode($options['json']);
  429. }
  430. elseif (!isset($options['text']))
  431. $template = VIEWS . str_replace('\\', DIRECTORY_SEPARATOR,
  432. isset($options['controller']) ? $options['controller'] : $this->controller)
  433. . DIRECTORY_SEPARATOR . (isset($options['action']) ? $options['action'] : $this->action);
  434. if (isset($options['status']))
  435. $this->response->status = $options['status'];
  436. if (isset($options['text']))
  437. {
  438. if (isset($options['content_type']))
  439. $this->response->content_type = $options['content_type'];
  440. if (!self::$_rendered)
  441. {
  442. $this->invoke_filters('before_render_filter');
  443. self::$_rendered = true;
  444. }
  445. if (isset($options['layout']))
  446. {
  447. if ($options['layout'] === true)
  448. $options['layout'] = self::resolve_layout($this->action);
  449. $this->response->layout = $options['layout'];
  450. self::$_content[0] = $options['text'];
  451. require VIEWS . 'layouts' . DIRECTORY_SEPARATOR
  452. . str_replace('\\', DIRECTORY_SEPARATOR, $options['layout']) . '.php';
  453. }
  454. else
  455. echo $options['text'];
  456. return;
  457. }
  458. if (!isset($options[0]) && !isset($options['action']) && !isset($options['controller'])
  459. && $this->response->status_code() >= 400)
  460. {
  461. if (isset($options['content_type']))
  462. $this->response->content_type = $options['content_type'];
  463. throw new StopException;
  464. }
  465. if (isset($options['format']) && $options['format'] === 'xml')
  466. {
  467. if (!isset($options['content_type']))
  468. $options['content_type'] = 'text/xml';
  469. if (!isset($options['layout']))
  470. $options['layout'] = false;
  471. }
  472. elseif (!isset($options['layout']))
  473. $options['layout'] = self::resolve_layout($this->action);
  474. if (isset($options['content_type']))
  475. $this->response->content_type = $options['content_type'];
  476. if (!self::$_rendered)
  477. {
  478. $this->invoke_filters('before_render_filter');
  479. self::$_rendered = true;
  480. }
  481. $this->response->template = str_replace(DIRECTORY_SEPARATOR, '\\', substr($template, strlen(VIEWS)));
  482. if ($options['layout'])
  483. {
  484. $this->response->layout = $options['layout'];
  485. ob_start();
  486. require $template . '.php';
  487. self::$_content[0] = ob_get_clean();
  488. require VIEWS . 'layouts' . DIRECTORY_SEPARATOR
  489. . str_replace('\\', DIRECTORY_SEPARATOR, $options['layout']) . '.php';
  490. }
  491. else
  492. require $template . '.php';
  493. }
  494. public function render_to_string($options=array())
  495. {
  496. ob_start();
  497. if (self::$_rendered)
  498. $str = $this->render($options);
  499. else
  500. {
  501. self::$_rendered = true;
  502. $str = $this->render($options);
  503. self::$_rendered = false;
  504. }
  505. return ob_get_clean() ?: $str;
  506. }
  507. private function render_partial($___path___, $___args___)
  508. {
  509. foreach ($___args___ as $___n___ => $___v___)
  510. $$___n___ = $___v___;
  511. require $___path___;
  512. }
  513. public function yield($region=0)
  514. {
  515. if (isset(self::$_content[$region]))
  516. return self::$_content[$region];
  517. }
  518. public function content_for($region, $closure)
  519. {
  520. ob_start();
  521. $closure($this);
  522. self::$_content[$region] = ob_get_clean();
  523. }
  524. private static final function resolve_layout($action)
  525. {
  526. if (!static::$layout)
  527. return;
  528. static $layout;
  529. if (!isset($layout))
  530. {
  531. $layout = null;
  532. foreach (self::normalize_defs(static::$layout) as $l)
  533. {
  534. if (isset($l['only']))
  535. {
  536. if ((((array) $l['only'] === $l['only']) && in_array($action, $l['only'], true))
  537. || ($l['only'] === $action))
  538. {
  539. $layout = $l[0];
  540. break;
  541. }
  542. continue;
  543. }
  544. elseif (isset($l['except']) && ((((array) $l['except'] === $l['except'])
  545. && in_array($action, $l['except'], true)) || ($l['except'] === $action)))
  546. continue;
  547. $layout = $l[0];
  548. break;
  549. }
  550. }
  551. return $layout;
  552. }
  553. private static final function get_filter_mods(&$entry)
  554. {
  555. $modifiers = array();
  556. if (isset($entry['only']))
  557. {
  558. $modifiers['only'] = ((array) $entry['only'] === $entry['only'])
  559. ? $entry['only'] : array($entry['only']);
  560. unset($entry['only']);
  561. }
  562. if (isset($entry['except']))
  563. {
  564. $modifiers['except'] = ((array) $entry['except'] === $entry['except'])
  565. ? $entry['except'] : array($entry['except']);
  566. unset($entry['except']);
  567. }
  568. if (isset($entry['exception']))
  569. {
  570. $modifiers['exception'] = $entry['exception'];
  571. unset($entry['exception']);
  572. }
  573. return $modifiers;
  574. }
  575. private static final function normalize_defs($definitions)
  576. {
  577. if ((array) $definitions !== $definitions)
  578. return array(array($definitions));
  579. $normalized_definitions = array();
  580. $outer_options = array();
  581. foreach ($definitions as $key => $body)
  582. {
  583. if ((string) $key === $key)
  584. $outer_options[$key] = $body;
  585. elseif ((array) $body === $body)
  586. {
  587. $inner_options = array();
  588. foreach ($body as $k => $v)
  589. {
  590. if ((string) $k === $k)
  591. {
  592. $inner_options[$k] = $v;
  593. unset($body[$k]);
  594. }
  595. }
  596. foreach ($body as $b)
  597. $normalized_definitions[] = array($b) + $inner_options;
  598. }
  599. else
  600. $normalized_definitions[] = array($body);
  601. }
  602. if ($outer_options)
  603. {
  604. foreach ($normalized_definitions as &$nd)
  605. $nd += $outer_options;
  606. }
  607. return $normalized_definitions;
  608. }
  609. private static final function add_to_filter($filter, $method)
  610. {
  611. if (!static::$$filter)
  612. static::$$filter = $method;
  613. elseif ((array) static::$$filter === static::$$filter)
  614. {
  615. if (array_key_exists('except', static::$$filter) || array_key_exists('only', static::$$filter))
  616. static::$$filter = self::normalize_defs(static::$$filter);
  617. array_push(static::$$filter, $method);
  618. }
  619. else
  620. static::$$filter = array(static::$$filter, $method);
  621. }
  622. private final function invoke_filters($filter, $value=null)
  623. {
  624. $filter_chain = array();
  625. $class = get_class($this);
  626. do
  627. {
  628. $class_filters = $class::$$filter;
  629. if (!$class_filters)
  630. continue;
  631. if ((array) $class_filters !== $class_filters)
  632. {
  633. if (!isset($filter_chain[$class_filters]))
  634. $filter_chain[$class_filters] = null;
  635. }
  636. else
  637. {
  638. $class_mods = self::get_filter_mods($class_filters);
  639. foreach (array_reverse($class_filters) as $entry)
  640. {
  641. if ((array) $entry !== $entry)
  642. {
  643. if (!isset($filter_chain[$entry]))
  644. $filter_chain[$entry] = $class_mods;
  645. }
  646. else
  647. {
  648. $mods = self::get_filter_mods($entry);
  649. foreach (array_reverse($entry) as $e)
  650. {
  651. if (!isset($filter_chain[$e]))
  652. $filter_chain[$e] = $mods ?: $class_mods;
  653. }
  654. }
  655. }
  656. }
  657. } while (($class = get_parent_class($class)) !== __CLASS__);
  658. foreach (array_reverse($filter_chain) as $flt => $mods)
  659. {
  660. if (isset($mods['only']) && !in_array($this->action, $mods['only']))
  661. continue;
  662. elseif (isset($mods['except']) && in_array($this->action, $mods['except']))
  663. continue;
  664. elseif (isset($mods['exception']) && !($value && is_a($value, $mods['exception'])))
  665. continue;
  666. if ($this->$flt($value) === false)
  667. return false;
  668. }
  669. }
  670. }
  671. ?>