PageRenderTime 60ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/class/patchwork/serverside.php

https://github.com/Patchworkx/Patchwork
PHP | 462 lines | 418 code | 33 blank | 11 comment | 20 complexity | bab82ae4a40a6331ff281d3edcb0b056 MD5 | raw file
  1. <?php /*********************************************************************
  2. *
  3. * Copyright : (C) 2007 Nicolas Grekas. All rights reserved.
  4. * Email : p@tchwork.org
  5. * License : http://www.gnu.org/licenses/agpl.txt GNU/AGPL
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. ***************************************************************************/
  13. use patchwork as p;
  14. class patchwork_serverside extends patchwork
  15. {
  16. protected static
  17. $args,
  18. $values,
  19. $get,
  20. $masterCache = array(),
  21. $cache;
  22. static function returnAgent($agent, $args, $lang = false)
  23. {
  24. if (false !== $lang)
  25. {
  26. $a = p::$lang;
  27. p::setLang($lang);
  28. $lang = $a;
  29. }
  30. $a =& $_GET;
  31. $g =& self::$get;
  32. $_GET =& $args;
  33. self::$get =& $f;
  34. ob_start();
  35. try {self::loadAgent(p::resolveAgentClass($agent, $_GET), false, false);}
  36. catch (Exception $e) {ob_end_clean(); throw $e;}
  37. $agent = ob_get_clean();
  38. $_GET =& $a;
  39. self::$get =& $g;
  40. false !== $lang && p::setLang($lang);
  41. return $agent;
  42. }
  43. static function loadAgent($agent, $args, $is_exo)
  44. {
  45. if (null === $agent) return;
  46. $a =& $_GET;
  47. if (false === $args)
  48. {
  49. /**/ if (DEBUG)
  50. p::$binaryMode || p::touch('debugSync');
  51. $reset_get = true;
  52. $cache = '';
  53. if ('s' === p::$requestMode)
  54. {
  55. ob_start(array(__CLASS__, 'ob_htmlspecialchars'), 32768);
  56. ++p::$ob_level;
  57. self::$get = (object) $a;
  58. }
  59. else
  60. {
  61. $args = array();
  62. if (is_array($a))
  63. {
  64. reset($a);
  65. while (list($k, $v) = each($a)) $args[$k] = is_string($v) ? htmlspecialchars($v) : $v;
  66. }
  67. self::$get = (object) $args;
  68. }
  69. self::$get->__DEBUG__ = DEBUG ? DEBUG : 0;
  70. self::$get->__HOST__ = htmlspecialchars(p::__HOST__());
  71. $cache .= self::$get->__LANG__ = htmlspecialchars(p::__LANG__());
  72. $cache .= self::$get->__BASE__ = htmlspecialchars(p::__BASE__());
  73. self::$get->__AGENT__ = 'agent_index' === $agent ? '' : (patchwork_class2file(substr($agent, 6)) . '/');
  74. self::$get->__URI__ = htmlspecialchars(p::$uri);
  75. self::$get->__REFERER__ = isset($_SERVER['HTTP_REFERER']) ? htmlspecialchars($_SERVER['HTTP_REFERER']) : '';
  76. self::$get->__LANG_ALT__ = new loop_altLang;
  77. self::$args = self::$get;
  78. if (!isset(self::$masterCache[$cache])) self::$masterCache[$cache] = array();
  79. self::$cache =& self::$masterCache[$cache];
  80. }
  81. else
  82. {
  83. $reset_get = false;
  84. $_GET =& $args;
  85. if ($agent instanceof loop && $agent->__toString())
  86. {
  87. $agent->autoResolve = false;
  88. while ($i = $agent->loop()) $data = $i;
  89. if (!(p::$binaryMode || $agent instanceof L_)) foreach ($data as &$v) is_string($v) && $v = htmlspecialchars($v);
  90. $agent = $data->{'a$'};
  91. $args = array_merge((array) $data, $args);
  92. }
  93. $BASE = p::__BASE__();
  94. $agent = p::base($agent, true);
  95. if (0 === strncmp($agent, $BASE, strlen($BASE)))
  96. {
  97. $agent = substr($agent, strlen($BASE));
  98. if ($is_exo)
  99. {
  100. W("patchwork Security Restriction Error: an AGENT ({$agent}) is called with EXOAGENT");
  101. $_GET =& $a;
  102. return;
  103. }
  104. }
  105. else
  106. {
  107. if ($is_exo)
  108. {
  109. $k = $CONFIG['i18n.lang_list'][p::__LANG__()];
  110. $agent = implode($k, explode('__', $agent, 2)) . '?p:=s';
  111. foreach ($args as $k => $v) $agent .= '&' . urlencode($k) . '=' . urlencode(p::string($v));
  112. if (ini_get_bool('allow_url_fopen')) $agent = file_get_contents($agent);
  113. else
  114. {
  115. $agent = new HTTP_Request($agent);
  116. $agent->sendRequest();
  117. $agent = $agent->getResponseBody();
  118. }
  119. echo str_replace(
  120. array('&gt;', '&lt;', '&quot;', '&#039;', '&amp;'),
  121. array('>' , '<' , '"' , "'" , '&' ),
  122. $agent
  123. );
  124. }
  125. else W("patchwork Security Restriction Error: an EXOAGENT ({$agent}) is called with AGENT");
  126. $_GET =& $a;
  127. return;
  128. }
  129. try
  130. {
  131. $agent = p::resolveAgentClass($agent, $args);
  132. }
  133. catch (patchwork_exception_static $agent)
  134. {
  135. readfile($agent->getMessage());
  136. $_GET =& $a;
  137. return;
  138. }
  139. self::$args = (object) $args;
  140. }
  141. self::render($agent);
  142. $_GET =& $a;
  143. if ($reset_get) self::$get = false;
  144. }
  145. protected static function render($agentClass)
  146. {
  147. p::openMeta($agentClass);
  148. $a = self::$args;
  149. $g = self::$get;
  150. $agent = new $agentClass($_GET);
  151. $group = p::closeGroupStage();
  152. $is_cacheable = !in_array('private', $group);
  153. $cagent = p::agentCache($agentClass, $agent->get, 'ser', $group);
  154. $filter = false;
  155. if (isset(self::$cache[$cagent]))
  156. {
  157. $cagent =& self::$cache[$cagent];
  158. $v = clone $cagent[0];
  159. $template = $cagent[1];
  160. }
  161. else
  162. {
  163. if (!($is_cacheable && list($v, $template) = self::getFromCache($cagent)))
  164. {
  165. ob_start();
  166. ++p::$ob_level;
  167. $v = (object) $agent->compose((object) array());
  168. if (!p::$is_enabled)
  169. {
  170. p::closeMeta();
  171. return;
  172. }
  173. $template = $agent->getTemplate();
  174. if (!p::$binaryMode)
  175. {
  176. foreach ($v as &$h) is_string($h) && $h = htmlspecialchars($h);
  177. unset($h);
  178. }
  179. $filter = true;
  180. $rawdata = ob_get_flush();
  181. --p::$ob_level;
  182. }
  183. isset(p::$headers['content-type']) || p::header('Content-Type: text/html');
  184. $vClone = clone $v;
  185. }
  186. p::$catchMeta = false;
  187. self::$values = $v->{'$'} = $v;
  188. $ctemplate = p::getContextualCachePath('templates/' . $template, (p::$binaryMode ? 'bin' : 'html') . '.php');
  189. $ftemplate = 'template' . md5($ctemplate);
  190. p::$lockedContentType = true;
  191. if (function_exists($ftemplate)) $ftemplate($v, $a, $g);
  192. else
  193. {
  194. TURBO || p::syncTemplate($template, $ctemplate);
  195. if ($h = p::fopenX($ctemplate))
  196. {
  197. p::openMeta('agent__template/' . $template, false);
  198. $compiler = new ptlCompiler_php($template, p::$binaryMode);
  199. $ftemplate = "<?php function {$ftemplate}(&\$v,&\$a,&\$g){global \$a\x9D,\$c\x9D;\$d=\$v;" . $compiler->compile() . "} {$ftemplate}(\$v,\$a,\$g);";
  200. fwrite($h, $ftemplate);
  201. fclose($h);
  202. list(,,, $watch) = p::closeMeta();
  203. p::writeWatchTable($watch, $ctemplate);
  204. }
  205. require $ctemplate;
  206. }
  207. if ($filter)
  208. {
  209. p::$catchMeta = true;
  210. $agent->metaCompose();
  211. list($maxage, $group, $expires, $watch, $headers, $canPost) = p::closeMeta();
  212. if ('ontouch' === $expires && !$watch) $expires = 'auto';
  213. $expires = 'auto' === $expires && $watch ? 'ontouch' : 'onmaxage';
  214. p::setExpires($expires);
  215. if ($is_cacheable && !IS_POSTING && !in_array('private', $group) && ($maxage || 'ontouch' === $expires))
  216. {
  217. $fagent = $cagent;
  218. if ($canPost) $fagent = substr($cagent, 0, -4) . '.post' . substr($cagent, -4);
  219. if ($h = p::fopenX($fagent))
  220. {
  221. $rawdata = array(
  222. 'rawdata' => $rawdata,
  223. 'v' => array(),
  224. );
  225. self::freezeAgent($rawdata['v'], $vClone);
  226. $rawdata['template'] = $template;
  227. $rawdata['maxage'] = $maxage;
  228. $rawdata['expires'] = $expires;
  229. $rawdata['watch'] = $watch;
  230. $rawdata['headers'] = $headers;
  231. $rawdata = serialize($rawdata);
  232. fwrite($h, $rawdata);
  233. fclose($h);
  234. touch($fagent, $_SERVER['REQUEST_TIME'] + ('ontouch' === $expires ? $CONFIG['maxage'] : $maxage));
  235. p::writeWatchTable($watch, $fagent);
  236. }
  237. }
  238. }
  239. else p::closeMeta();
  240. if (isset($vClone)) self::$cache[$cagent] = array($vClone, $template);
  241. }
  242. protected static function freezeAgent(&$v, $data)
  243. {
  244. foreach ($data as $key => $value)
  245. {
  246. if ($value instanceof loop)
  247. {
  248. if ($value->__toString())
  249. {
  250. $a = array();
  251. while ($b = $value->loop())
  252. {
  253. $c = array();
  254. $a[] =& $c;
  255. self::freezeAgent($c, $b);
  256. unset($c);
  257. }
  258. $v[$key] = new L_($a);
  259. unset($a);
  260. }
  261. }
  262. else $v[$key] = is_string($value) && !p::$binaryMode ? htmlspecialchars($value) : $value;
  263. }
  264. }
  265. protected static function getFromCache($cagent)
  266. {
  267. if (!file_exists($cagent))
  268. {
  269. $cagent = substr($cagent, 0, -4) . '.post' . substr($cagent, -4);
  270. if (IS_POSTING || !file_exists($cagent)) $cagent = false;
  271. }
  272. if ($cagent)
  273. {
  274. if (filemtime($cagent) > $_SERVER['REQUEST_TIME'])
  275. {
  276. $data = unserialize(file_get_contents($cagent));
  277. p::setMaxage($data['maxage']);
  278. p::setExpires($data['expires']);
  279. p::writeWatchTable($data['watch']);
  280. array_map(array('patchwork', 'header'), $data['headers']);
  281. echo $data['rawdata'];
  282. return array((object) $data['v'], $data['template']);
  283. }
  284. else @unlink($cagent);
  285. }
  286. return false;
  287. }
  288. /*
  289. * Used internaly at template execution time, for counters.
  290. */
  291. static function increment($var, $step, $pool)
  292. {
  293. if (!isset($pool->$var)) $pool->$var = 0;
  294. $var =& $pool->$var;
  295. if (!$var) $var = '0';
  296. $a = $var;
  297. $var += $step;
  298. return $a;
  299. }
  300. static function makeLoopByLength(&$length)
  301. {
  302. $length = new loop_length_($length);
  303. return true;
  304. }
  305. static function getLoopNext($loop)
  306. {
  307. if (p::$binaryMode || $loop instanceof L_) return $loop->loop();
  308. if ($loop = $loop->loop())
  309. {
  310. foreach ($loop as &$i) is_string($i) && $i = htmlspecialchars($i);
  311. }
  312. return $loop;
  313. }
  314. static function ob_htmlspecialchars($a, $mode)
  315. {
  316. if (PHP_OUTPUT_HANDLER_END & $mode) --p::$ob_level;
  317. return htmlspecialchars($a);
  318. }
  319. }
  320. class L_ extends loop
  321. {
  322. protected
  323. $array,
  324. $len,
  325. $i = 0;
  326. function __construct(&$array)
  327. {
  328. $this->array =& $array;
  329. }
  330. protected function prepare()
  331. {
  332. return $this->len = count($this->array);
  333. }
  334. protected function next()
  335. {
  336. if ($this->i < $this->len) return (object) $this->array[$this->i++];
  337. else $this->i = 0;
  338. }
  339. }
  340. class loop_length_ extends loop
  341. {
  342. protected
  343. $length,
  344. $counter;
  345. function __construct($length)
  346. {
  347. $this->length = $length;
  348. }
  349. protected function prepare()
  350. {
  351. $this->counter = 0;
  352. return $this->length;
  353. }
  354. protected function next()
  355. {
  356. if ($this->counter++ < $this->length) return (object) array();
  357. }
  358. }