/products/photocrati_nextgen/modules/router/package.module.router.php

  1. <?php 
  2. /** 
  3. * Class A_Routing_App_Factory 
  4. * @mixin C_Component_Factory 
  5. * @adapts I_Component_Factory 
  6. */ 
  7. class A_Routing_App_Factory extends Mixin 
  8. function routing_app($context = FALSE, $router = FALSE) 
  9. return new C_Routing_App($context, $router); 
  10. /** 
  11. * Class C_Http_Response_Controller 
  12. * @mixin Mixin_Http_Response_Actions 
  13. * @implements I_Http_Response 
  14. */ 
  15. class C_Http_Response_Controller extends C_Component 
  16. static $_instance = NULL; 
  17. static function get_instance($context = FALSE) 
  18. if (!isset(self::$_instance)) { 
  19. $klass = get_class(); 
  20. self::$_instance = new $klass(); 
  21. return self::$_instance; 
  22. function define($context = FALSE) 
  23. $this->add_mixin('Mixin_Http_Response_Actions'); 
  24. $this->implement('I_Http_Response'); 
  25. class Mixin_Http_Response_Actions extends Mixin 
  26. function http_301_action() 
  27. header('HTTP/1.1 301 Permanent Redirect'); 
  28. header("Location: {$this->object->get_routed_url()}"); 
  29. function http_302_action() 
  30. header('HTTP/1.1 302 Temporary Redirect'); 
  31. header("Location: {$this->object->get_routed_url()}"); 
  32. function http_501_action() 
  33. header('HTTP/1.1 501 Not Implemented'); 
  34. function http_404_action() 
  35. header('HTTP/1.1 404 Not found'); 
  36. function http_202_action() 
  37. header('HTTP/1.1 202 Accepted'); 
  38. class Mixin_Router extends Mixin 
  39. function set_routed_app($app) 
  40. $this->object->_routed_app = $app; 
  41. function &get_routed_app() 
  42. $retval = $this->object->_routed_app ? $this->object->_routed_app : $this->object->get_default_app(); 
  43. return $retval; 
  44. function &get_default_app() 
  45. if (is_null($this->object->_default_app)) { 
  46. $this->object->_default_app = $this->object->create_app(); 
  47. $retval = $this->object->_default_app; 
  48. return $retval; 
  49. function route($patterns, $handler = FALSE) 
  50. $this->object->get_default_app()->route($patterns, $handler); 
  51. function rewrite($src, $dst, $redirect = FALSE) 
  52. $this->object->get_default_app()->rewrite($src, $dst, $redirect); 
  53. function get_parameter($key, $prefix = NULL, $default = NULL) 
  54. return $this->object->get_routed_app()->get_parameter($key, $prefix, $default); 
  55. function param($key, $prefix = NULL, $default = NULL) 
  56. return $this->object->get_parameter($key, $prefix, $default); 
  57. function has_parameter_segments() 
  58. return $this->object->get_routed_app()->has_parameter_segments(); 
  59. function passthru() 
  60. return $this->object->get_default_app()->passthru(); 
  61. /** 
  62. * Gets url for the router 
  63. * @param string $uri 
  64. * @return string 
  65. */ 
  66. function get_url($uri = '/', $with_qs = TRUE, $site_url = FALSE) 
  67. $retval = $this->object->join_paths($this->object->get_base_url($site_url), $uri); 
  68. if ($with_qs) { 
  69. $parts = parse_url($retval); 
  70. if (!isset($parts['query'])) { 
  71. $parts['query'] = $this->object->get_querystring(); 
  72. } else { 
  73. $parts['query'] = $this->object->join_querystrings($parts['query'], $this->object->get_querystring()); 
  74. $retval = $this->object->construct_url_from_parts($parts); 
  75. return str_replace("\\", "/", $retval); 
  76. /** 
  77. * Currents the relative url 
  78. * @param string $uri 
  79. * @param boolean $with_qs 
  80. * @return string 
  81. */ 
  82. function get_relative_url($uri = '/', $with_qs = TRUE) 
  83. $url = $this->object->get_url($uri, $with_qs = TRUE); 
  84. if ($url !== '/') { 
  85. $retval = str_replace($this->object->get_base_url(), '', $url); 
  86. return '/' . lrtim($retval, '/'); 
  87. /** 
  88. * Returns a static url 
  89. * @param string $path 
  90. * @param string $module 
  91. * @return string 
  92. */ 
  93. function get_static_url($path, $module = FALSE) 
  94. $fs = C_Fs::get_instance(); 
  95. $path = $fs->find_abspath($path, $module); 
  96. $base_url = $this->object->get_base_url(TRUE); 
  97. $base_url = $this->object->remove_url_segment('/index.php', $base_url); 
  98. $path = str_replace($fs->get_document_root('plugins'), $base_url, $path); 
  99. // adjust for possible windows hosts 
  100. return str_replace("\\", '/', $path); 
  101. /** 
  102. * Gets the routed url 
  103. * @returns string 
  104. */ 
  105. function get_routed_url() 
  106. $retval = $this->object->get_url($this->object->get_request_uri()); 
  107. if ($app = $this->object->get_routed_app()) { 
  108. $retval = $this->object->get_url($app->get_app_uri()); 
  109. return $retval; 
  110. /** 
  111. * Gets the base url for the router 
  112. * 
  113. * @param bool $site_url Unused 
  114. * @return string 
  115. */ 
  116. function get_base_url($site_url = FALSE) 
  117. $protocol = $this->object->is_https() ? 'https://' : 'http://'; 
  118. $retval = "{$protocol}{$_SERVER['SERVER_NAME']}{$this->object->context}"; 
  119. return str_replace("\\", "/", rtrim($retval, "/\\")); 
  120. /** 
  121. * Determines if the current request is over HTTPs or not 
  122. */ 
  123. function is_https() 
  124. return !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' || !empty($_SERVER['HTTP_USESSL']) && strtolower($_SERVER['HTTP_USESSL']) !== 'off' || !empty($_SERVER['REDIRECT_HTTPS']) && strtolower($_SERVER['REDIRECT_HTTPS']) !== 'off' || !empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443; 
  125. /** 
  126. * Serve request using defined Routing Apps 
  127. * 
  128. * @param string|FALSE $request_uri 
  129. */ 
  130. function serve_request() 
  131. $served = FALSE; 
  132. // iterate over all apps, and serve the route 
  133. foreach ($this->object->get_apps() as $app) { 
  134. if ($served = $app->serve_request($this->object->context)) { 
  135. break; 
  136. return $served; 
  137. /** 
  138. * Gets the querystring of the current request 
  139. * @return string 
  140. */ 
  141. function get_querystring() 
  142. return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : null; 
  143. function set_querystring($value) 
  144. $_SERVER['QUERY_STRING'] = $value; 
  145. /** 
  146. * Gets the request for the router 
  147. * @return string 
  148. */ 
  149. function get_request_uri($with_params = TRUE) 
  150. if (!empty($_SERVER['ORIG_REQUEST_URI'])) { 
  151. $retval = $_SERVER['ORIG_REQUEST_URI']; 
  152. } elseif (!empty($_SERVER['PATH_INFO'])) { 
  153. $retval = $_SERVER['PATH_INFO']; 
  154. } else { 
  155. $retval = $_SERVER['REQUEST_URI']; 
  156. // Remove the querystring 
  157. if (($index = strpos($retval, '?')) !== FALSE) { 
  158. $retval = substr($retval, 0, $index); 
  159. // Remove the router's context 
  160. $retval = preg_replace('#^' . preg_quote($this->object->context, '#') . '#', '', $retval); 
  161. // Remove the params 
  162. if (!$with_params) { 
  163. $retval = $this->object->strip_param_segments($retval); 
  164. // Ensure that request uri starts with a slash 
  165. if (strpos($retval, '/') !== 0) { 
  166. $retval = "/{$retval}"; 
  167. return $retval; 
  168. /** 
  169. * Gets the method of the HTTP request 
  170. * @return string 
  171. */ 
  172. function get_request_method() 
  173. return $this->object->_request_method; 
  174. function &create_app($name = '/') 
  175. $factory = C_Component_Factory::get_instance(); 
  176. $app = $factory->create('routing_app', $name); 
  177. $this->object->_apps[] = $app; 
  178. return $app; 
  179. /** 
  180. * Gets a list of apps registered for the router 
  181. * 
  182. * @return array 
  183. */ 
  184. function get_apps() 
  185. usort($this->object->_apps, array(&$this, '_sort_apps')); 
  186. return array_reverse($this->object->_apps); 
  187. /** 
  188. * Sorts apps.This is needed because we want the most specific app to be 
  189. * executed first 
  190. * @param C_Routed_App $a 
  191. * @param C_Routed_App $b 
  192. * @return int 
  193. */ 
  194. function _sort_apps($a, $b) 
  195. return strnatcmp($a->context, $b->context); 
  196. /** 
  197. * A router is configured to match patterns against a url and route the request to a particular controller and action 
  198. * @mixin Mixin_Url_Manipulation 
  199. * @mixin Mixin_Router 
  200. * @implements I_Router 
  201. */ 
  202. class C_Router extends C_Component 
  203. static $_instances = array(); 
  204. var $_apps = array(); 
  205. var $_default_app = NULL; 
  206. function define($context = FALSE) 
  207. if (!$context or $context == 'all') { 
  208. $context = '/'; 
  209. parent::define($context); 
  210. $this->add_mixin('Mixin_Url_Manipulation'); 
  211. $this->add_mixin('Mixin_Router'); 
  212. $this->implement('I_Router'); 
  213. function initialize() 
  214. parent::initialize(); 
  215. $this->_request_method = !empty($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null; 
  216. static function &get_instance($context = False) 
  217. if (!isset(self::$_instances[$context])) { 
  218. $klass = get_class(); 
  219. self::$_instances[$context] = new $klass($context); 
  220. return self::$_instances[$context]; 
  221. class Mixin_Routing_App extends Mixin 
  222. /** 
  223. * Creates a new route endpoint with the assigned handler 
  224. * 
  225. * @param array $routes URL to route, eg /page/{page}/ 
  226. * @param array $handler Formatted array 
  227. */ 
  228. function route($routes, $handler) 
  229. // ensure that the routing patterns array exists 
  230. if (!is_array($this->object->_routing_patterns)) { 
  231. $this->object->_routing_patterns = array(); 
  232. if (!is_array($routes)) { 
  233. $routes = array($routes); 
  234. // fetch all routing patterns 
  235. $patterns = $this->object->_routing_patterns; 
  236. foreach ($routes as $route) { 
  237. // add the routing pattern 
  238. $patterns[$this->object->_route_to_regex($route)] = $handler; 
  239. // update routing patterns 
  240. $this->object->_routing_patterns = $patterns; 
  241. /** 
  242. * Handles internal url rewriting with optional HTTP redirection,  
  243. * 
  244. * @param string $src Original URL 
  245. * @param string $dst Destination URL 
  246. * @param bool $redirect FALSE for internal handling, otherwise the HTTP code to send 
  247. */ 
  248. function rewrite($src, $dst, $redirect = FALSE, $stop = FALSE) 
  249. // ensure that rewrite patterns array exists 
  250. if (!is_array($this->object->_rewrite_patterns)) { 
  251. $this->object->_rewrite_patterns = array(); 
  252. // fetch all rewrite patterns 
  253. $patterns = $this->object->_rewrite_patterns; 
  254. // Assign rewrite definition 
  255. $definition = array('dst' => $dst, 'redirect' => $redirect, 'stop' => $stop); 
  256. // We treat wildcards much differently than normal rewrites 
  257. if (preg_match("/\\{[\\.\\\\*]/", $src)) { 
  258. $pattern = str_replace('{*}', '(.*?)', $src); 
  259. $pattern = str_replace('{.*}', '(.*?)', $pattern); 
  260. $pattern = str_replace('{\\w}', '([^/]*)', $pattern); 
  261. $pattern = str_replace('{\\d}', '(\\d*)', $pattern); 
  262. $src = '#' . (strpos($src, '/') === 0 ? '^' : '') . $pattern . '/?$#'; 
  263. $definition['wildcards'] = TRUE; 
  264. } else { 
  265. $src = $this->object->_route_to_regex($src); 
  266. // add the rewrite pattern 
  267. $patterns[$src] = $definition; 
  268. // update rewrite patterns; 
  269. $this->object->_rewrite_patterns = $patterns; 
  270. /** 
  271. * Gets an instance of the router 
  272. * @return type 
  273. */ 
  274. function get_router() 
  275. return C_Router::get_instance(); 
  276. function get_app_url($request_uri = FALSE, $with_qs = FALSE) 
  277. return $this->object->get_router()->get_url($this->object->get_app_uri($request_uri), $with_qs); 
  278. function get_routed_url($with_qs = TRUE) 
  279. return $this->object->get_app_url(FALSE, $with_qs); 
  280. function get_app_uri($request_uri = FALSE) 
  281. if (!$request_uri) { 
  282. $request_uri = $this->object->get_app_request_uri(); 
  283. return $this->object->join_paths($this->object->context, $request_uri); 
  284. function get_app_request_uri() 
  285. $retval = FALSE; 
  286. if ($this->object->_request_uri) { 
  287. $retval = $this->object->_request_uri; 
  288. } else { 
  289. if ($retval = $this->object->does_app_serve_request()) { 
  290. if (strpos($retval, '/') !== 0) { 
  291. $retval = '/' . $retval; 
  292. $this->object->set_app_request_uri($retval); 
  293. return $retval; 
  294. /** 
  295. * Sets the application request uri 
  296. * @param type $uri 
  297. */ 
  298. function set_app_request_uri($uri) 
  299. $this->object->_request_uri = $uri; 
  300. /** 
  301. * Gets the application's routing regex pattern 
  302. * @return string 
  303. */ 
  304. function get_app_routing_pattern() 
  305. return $this->object->_route_to_regex($this->object->context); 
  306. /** 
  307. * Determines whether this app serves the request 
  308. * @return boolean|string 
  309. */ 
  310. function does_app_serve_request() 
  311. $retval = FALSE; 
  312. $request_uri = $this->object->get_router()->get_request_uri(TRUE); 
  313. // Is the context present in the uri? 
  314. if (($index = strpos($request_uri, $this->object->context)) !== FALSE) { 
  315. $starts_with_slash = strpos($this->object->context, '/') === 0; 
  316. if ($starts_with_slash && $index === 0 or !$starts_with_slash) { 
  317. $regex = implode('', array('#', $starts_with_slash ? '^' : '', preg_quote($this->object->context, '#'), '#')); 
  318. $retval = preg_replace($regex, '', $request_uri); 
  319. if (!$retval) { 
  320. $retval = '/'; 
  321. if (strpos($retval, '/') !== 0) { 
  322. $retval = '/' . $retval; 
  323. if (substr($retval, -1) != '/') { 
  324. $retval = $retval . '/'; 
  325. return $retval; 
  326. /** 
  327. * Performs the url rewriting routines. Returns the HTTP status code used to 
  328. * redirect, if we're to do so. Otherwise FALSE 
  329. * @return int|bool 
  330. */ 
  331. function do_rewrites($request_uri = FALSE) 
  332. $redirect = FALSE; 
  333. static $stop_processing = FALSE; 
  334. // Get the request uri if not provided, if provided decode it 
  335. if (!$request_uri) { 
  336. $request_uri = $this->object->get_app_request_uri(); 
  337. } else { 
  338. $request_uri = urldecode($request_uri); 
  339. // ensure that rewrite patterns array exists 
  340. if (!is_array($this->object->_rewrite_patterns)) { 
  341. $this->object->_rewrite_patterns = array(); 
  342. // Process each rewrite rule 
  343. // start rewriting urls 
  344. if (!$stop_processing) { 
  345. foreach ($this->object->_rewrite_patterns as $pattern => $details) { 
  346. // Remove this pattern from future processing for this request 
  347. unset($this->object->_rewrite_patterns[$pattern]); 
  348. // Wildcards are processed much differently 
  349. if (isset($details['wildcards']) && $details['wildcards']) { 
  350. if (preg_match($pattern, $request_uri, $matches)) { 
  351. foreach ($matches as $index => $match) { 
  352. if ($index == 0) { 
  353. $request_uri = str_replace($match, $details['dst'], $request_uri); 
  354. if ($index > 0) { 
  355. $request_uri = str_replace("{{$index}}", $match, $request_uri); 
  356. // Set the redirect flag if we're to do so 
  357. if (isset($details['redirect']) && $details['redirect']) { 
  358. $redirect = $details['redirect'] === TRUE ? 302 : intval($details['redirect']); 
  359. break; 
  360. // Stop processing rewrite patterns? 
  361. if ($details['stop']) { 
  362. $stop_processing = TRUE; 
  363. } elseif (preg_match_all($pattern, $request_uri, $matches, PREG_SET_ORDER)) { 
  364. // Assign new request URI 
  365. $request_uri = $details['dst']; 
  366. // Substitute placeholders 
  367. foreach ($matches as $match) { 
  368. if ($redirect) { 
  369. break; 
  370. foreach ($match as $key => $val) { 
  371. // If we have a placeholder that needs swapped, swap 
  372. // it now 
  373. if (is_numeric($key)) { 
  374. continue; 
  375. $request_uri = str_replace("{{$key}}", $val, $request_uri); 
  376. // Set the redirect flag if we're to do so 
  377. if (isset($details['redirect']) && $details['redirect']) { 
  378. $redirect = $details['redirect'] === TRUE ? 302 : intval($details['redirect']); 
  379. break; 
  380. if ($stop_processing) { 
  381. break; 
  382. // Cache all known data about the application request 
  383. $this->object->set_app_request_uri($request_uri); 
  384. $this->object->get_router()->set_routed_app($this->object); 
  385. return $redirect; 
  386. /** 
  387. * Determines if the current routing app meets our requirements and serves them 
  388. * 
  389. * @return bool 
  390. */ 
  391. function serve_request() 
  392. $served = FALSE; 
  393. // ensure that the routing patterns array exists 
  394. if (!is_array($this->object->_routing_patterns)) { 
  395. $this->object->_routing_patterns = array(); 
  396. // if the application root matches, then we'll try to route the request 
  397. if ($request_uri = $this->object->get_app_request_uri()) { 
  398. // Perform URL rewrites 
  399. $redirect = $this->object->do_rewrites($request_uri); 
  400. // Are we to perform a redirect? 
  401. if ($redirect) { 
  402. $this->object->execute_route_handler($this->object->parse_route_handler($redirect)); 
  403. } else { 
  404. foreach ($this->object->_routing_patterns as $pattern => $handler) { 
  405. if (preg_match($pattern, $this->object->get_app_request_uri(), $matches)) { 
  406. $served = TRUE; 
  407. // Add placeholder parameters 
  408. foreach ($matches as $key => $value) { 
  409. if (is_numeric($key)) { 
  410. continue; 
  411. $this->object->set_parameter_value($key, $value, NULL); 
  412. // If a handler is attached to the route, execute it. A 
  413. // handler can be 
  414. // - FALSE, meaning don't do any post-processing to the route 
  415. // - A string, such as controller#action 
  416. // - An array: array( 
  417. // 'controller' => 'I_Test_Controller',  
  418. // 'action' => 'index',  
  419. // 'context' => 'all', (optional) 
  420. // 'method' => array('GET') (optional) 
  421. // ) 
  422. if ($handler && ($handler = $this->object->parse_route_handler($handler))) { 
  423. // Is this handler for the current HTTP request method? 
  424. if (isset($handler['method'])) { 
  425. if (!is_array($handler['method'])) { 
  426. $handler['$method'] = array($handler['method']); 
  427. if (in_array($this->object->get_router()->get_request_method(), $handler['method'])) { 
  428. $this->object->execute_route_handler($handler); 
  429. } else { 
  430. $this->object->execute_route_handler($handler); 
  431. } else { 
  432. if (!$handler) { 
  433. $this->object->passthru(); 
  434. return $served; 
  435. /** 
  436. * Executes an action of a particular controller 
  437. * @param array $handler 
  438. */ 
  439. function execute_route_handler($handler) 
  440. // Get action 
  441. $action = $handler['action']; 
  442. // Get controller 
  443. $controller = $this->object->get_registry()->get_utility($handler['controller'], $handler['context']); 
  444. // Call action 
  445. $controller->{$action}(); 
  446. // Clean Exit (fastcgi safe) 
  447. C_NextGEN_Bootstrap::shutdown(); 
  448. /** 
  449. * Parses the route handler 
  450. * @param mixed $handler 
  451. * @return array 
  452. */ 
  453. function parse_route_handler($handler) 
  454. if (is_string($handler)) { 
  455. $handler = array_combine(array('controller', 'action'), explode('#', $handler)); 
  456. } elseif (is_numeric($handler)) { 
  457. $handler = array('controller' => 'I_Http_Response', 'action' => 'http_' . $handler); 
  458. if (!isset($handler['context'])) { 
  459. $handler['context'] = FALSE; 
  460. if (strpos($handler['action'], '_action') === FALSE) { 
  461. $handler['action'] .= '_action'; 
  462. return $handler; 
  463. function add_placeholder_params_from_matches($matches) 
  464. // Add the placeholder parameter values to the _params array 
  465. foreach ($matches as $key => $value) { 
  466. if (is_numeric($key)) { 
  467. continue; 
  468. $this->object->add_placeholder_param($key, $value, $matches[0]); 
  469. /** 
  470. * Used to pass execution to PHP and perhaps an above framework 
  471. */ 
  472. function passthru() 
  473. /** 
  474. * Adds a placeholder parameter 
  475. * @param string $name 
  476. * @param stirng $value 
  477. * @param string $source 
  478. */ 
  479. function add_placeholder_param($name, $value, $source = NULL) 
  480. if (!is_array($this->object->_parameters)) { 
  481. $this->object->_parameters = array('global'); 
  482. if (!isset($this->object->_parameters['global'])) { 
  483. $this->object->_parameters['global'] = array(); 
  484. $this->object->_parameters['global'][] = array('id' => '', 'name' => $name, 'value' => $value, 'source' => $source); 
  485. /** 
  486. * Converts the route to the regex 
  487. * 
  488. * @param string $route 
  489. * @return string 
  490. */ 
  491. function _route_to_regex($route) 
  492. // Get the settings manager 
  493. $settings = $this->object->_settings; 
  494. $param_slug = $settings->router_param_slug; 
  495. // convert route to RegEx pattern 
  496. $route_regex = preg_quote(str_replace(array('{', '}'), array('~', '~'), $route), '#'); 
  497. // Wrap the route 
  498. $route_regex = '(' . $route_regex . ')'; 
  499. // If the route starts with a slash, then it must appear at the beginning 
  500. // of a request uri 
  501. if (strpos($route, '/') === 0) { 
  502. $route_regex = '^' . $route_regex; 
  503. // If the route is not /, and perhaps /foo, then we need to optionally 
  504. // look for a trailing slash as well 
  505. if ($route != '/') { 
  506. $route_regex .= '/?'; 
  507. // If parameters come after a slug, it might appear as well 
  508. if ($param_slug) { 
  509. $route_regex .= "(" . preg_quote($param_slug, '#') . '/)?'; 
  510. // Parameter might follow the request uri 
  511. $route_regex .= "(/?([^/]+\\-\\-)?[^/]+\\-\\-[^/]+/?) {0, }"; 
  512. // Create the regex 
  513. $route_regex = '#' . $route_regex . '/?$#i'; 
  514. // convert placeholders to regex as well 
  515. return preg_replace('/~([^~]+)~/i', ($param_slug ? '(' . preg_quote($param_slug, '#') . '\\K)?' : '') . '(?P<\\1>[^/]+)/?', $route_regex); 
  516. /** 
  517. * Gets a request parameter from either the request uri or querystring 
  518. * This method takes into consideration the values of the router_param_prefix 
  519. * and router_param_separator settings when searching for the parameter 
  520. * 
  521. * Parameter can take on the following forms: 
  522. * /key--value 
  523. * /[MVC_PARAM_PREFIX]key--value 
  524. * /[MVC_PARAM_PREFIX]-key--value 
  525. * /[MVC_PARAM_PREFIX]_key--value 
  526. * /id--key--value 
  527. * /id--[MVC_PARAM_PREFIX]key--value 
  528. * /id--[MVC_PARAM_PREFIX]-key--value 
  529. * /id--[MVC_PARAM_PREFIX]_key--value 
  530. * 
  531. * @param string $key 
  532. * @param mixed $id 
  533. * @param mixed $default 
  534. * @return mixed 
  535. */ 
  536. function get_parameter($key, $id = NULL, $default = NULL, $segment = FALSE, $url = FALSE) 
  537. $retval = $default; 
  538. $settings = $this->object->_settings; 
  539. $quoted_key = preg_quote($key, '#'); 
  540. $id = $id ? preg_quote($id, '#') : "[^/]+"; 
  541. $param_prefix = preg_quote($settings->router_param_prefix, '#'); 
  542. $param_sep = preg_quote($settings->router_param_separator, '#'); 
  543. $param_regex = "#/((?P<id>{$id}) {$param_sep})?({$param_prefix}[-_]?)?{$quoted_key}{$param_sep}(?P<value>[^/\\?]+)/?#i"; 
  544. $found = FALSE; 
  545. $sources = $url ? array('custom' => $url) : $this->object->get_parameter_sources(); 
  546. foreach ($sources as $source_name => $source) { 
  547. if (preg_match($param_regex, $source, $matches)) { 
  548. if ($segment) { 
  549. $retval = array('segment' => $matches[0], 'source' => $source_name); 
  550. } else { 
  551. $retval = $this->object->recursive_stripslashes($matches['value']); 
  552. $found = TRUE; 
  553. break; 
  554. // Lastly, check the $_REQUEST 
  555. if (!$found && !$url && isset($_REQUEST[$key])) { 
  556. $found = TRUE; 
  557. $retval = $this->object->recursive_stripslashes($_REQUEST[$key]); 
  558. if (!$found && isset($_SERVER['REQUEST_URI'])) { 
  559. $params = array(); 
  560. parse_str(@parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $params); 
  561. if (isset($params[$key])) { 
  562. $found = TRUE; 
  563. $retval = $this->object->recursive_stripslashes($params[$key]); 
  564. return $retval; 
  565. /** 
  566. * Checks and cleans a URL. This function is from WordPress. 
  567. * 
  568. * A number of characters are removed from the URL. If the URL is for displaying 
  569. * (the default behaviour) ampersands are also replaced. The 'clean_url' filter 
  570. * is applied to the returned cleaned URL. 
  571. * 
  572. * @since 2.8.0 
  573. * @uses wp_kses_bad_protocol() To only permit protocols in the URL set 
  574. * via $protocols or the common ones set in the function. 
  575. * 
  576. * @param string $url The URL to be cleaned. 
  577. * @param array $protocols Optional. An array of acceptable protocols. 
  578. * Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn' if not set. 
  579. * @param string $_context Private. Use esc_url_raw() for database usage. 
  580. * @return string The cleaned $url after the 'clean_url' filter is applied. 
  581. */ 
  582. function esc_url($url, $protocols = null, $_context = 'display') 
  583. $original_url = $url; 
  584. $url = preg_replace('|[^a-z0-9-~+_.?#=!&;, /:%@$\\|*\'()\\x80-\\xff]|i', '', $url); 
  585. $strip = array('%0d', '%0a', '%0D', '%0A'); 
  586. $url = _deep_replace($strip, $url); 
  587. $url = str_replace(';//', '://', $url); 
  588. // Replace ampersands and single quotes only when displaying. 
  589. if ('display' == $_context) { 
  590. $url = wp_kses_normalize_entities($url); 
  591. $url = str_replace('&', '&', $url); 
  592. $url = str_replace("'", ''', $url); 
  593. if (!empty($url[0]) && '/' === $url[0]) { 
  594. $good_protocol_url = $url; 
  595. } else { 
  596. if (!is_array($protocols)) { 
  597. $protocols = wp_allowed_protocols(); 
  598. $good_protocol_url = wp_kses_bad_protocol($url, $protocols); 
  599. if (strtolower($good_protocol_url) != strtolower($url)) { 
  600. return ''; 
  601. /** 
  602. * Filter a string cleaned and escaped for output as a URL. 
  603. * 
  604. * @since 2.3.0 
  605. * 
  606. * @param string $good_protocol_url The cleaned URL to be returned. 
  607. * @param string $original_url The URL prior to cleaning. 
  608. * @param string $_context If 'display', replace ampersands and single quotes only. 
  609. */ 
  610. return apply_filters('clean_url', $good_protocol_url, $original_url, $_context); 
  611. /** 
  612. * Sets the value of a particular parameter 
  613. * @param string $key 
  614. * @param mixed $value 
  615. * @param mixed $id 
  616. */ 
  617. function set_parameter_value($key, $value, $id = NULL, $use_prefix = FALSE, $url = FALSE) 
  618. // Remove the parameter from both the querystring and request uri 
  619. $retval = $this->object->remove_parameter($key, $id, $url); 
  620. // Get the settings manager 
  621. $settings = $this->object->_settings; 
  622. $param_slug = $settings->router_param_slug; 
  623. // We're modifying a url passed in 
  624. if ($url) { 
  625. $parts = parse_url($retval); 
  626. if (!isset($parts['path'])) { 
  627. $parts['path'] = ''; 
  628. $parts['path'] = $this->object->join_paths($parts['path'], $param_slug && strpos($parts['path'], $param_slug) === FALSE ? $param_slug : '', $this->object->create_parameter_segment($key, $value, $id, $use_prefix)); 
  629. $parts['path'] = str_replace('//', '/', $parts['path']); 
  630. $retval = $this->object->construct_url_from_parts($parts); 
  631. } else { 
  632. // This parameter is being appended to the current request uri 
  633. $this->object->add_parameter_to_app_request_uri($key, $value, $id, $use_prefix); 
  634. // Return the new full url 
  635. $retval = $this->object->get_routed_url(); 
  636. return (is_null($retval) or is_numeric($retval) or is_array($retval)) ? $retval : $this->esc_url($retval); 
  637. /** 
  638. * Alias for remove_parameter() 
  639. * @param string $key 
  640. * @param mixed $id 
  641. * @return string 
  642. */ 
  643. function remove_param($key, $id = NULL, $url = FALSE) 
  644. return $this->object->remove_parameter($key, $id, $url); 
  645. /** 
  646. * Removes a parameter from the querystring and application request URI 
  647. * and returns the full application URL 
  648. * @param string $key 
  649. * @param mixed $id 
  650. * @return string 
  651. */ 
  652. function remove_parameter($key, $id = NULL, $url = FALSE) 
  653. $retval = $url; 
  654. $settings = $this->object->_settings; 
  655. $param_sep = $settings->router_param_separator; 
  656. $param_prefix = $settings->router_param_prefix ? preg_quote($settings->router_param_prefix, '#') : ''; 
  657. $param_slug = $settings->router_param_slug ? preg_quote($settings->router_param_slug, '#') : FALSE; 
  658. // Is the parameter already part of the request? If so, modify that 
  659. // parameter 
  660. if (($segment = $this->object->get_parameter_segment($key, $id, $url)) && is_array($segment)) { 
  661. extract($segment); 
  662. if ($source == 'querystring') { 
  663. $preg_id = $id ? '\\d+' : preg_quote($id, '#'); 
  664. $preg_key = preg_quote($key, '#'); 
  665. $regex = implode('', array('#', $id ? "{$preg_id}{$param_sep}" : '', "(({$param_prefix}{$param_sep})?)?{$preg_key}({$param_sep}|=)[^\\/&]+&?#i")); 
  666. $qs = preg_replace($regex, '', $this->get_router()->get_querystring()); 
  667. $this->object->get_router()->set_querystring($qs); 
  668. $retval = $this->object->get_routed_url(); 
  669. } elseif ($source == 'request_uri') { 
  670. $uri = $this->object->get_app_request_uri(); 
  671. $uri = $this->object->join_paths(explode($segment, $uri)); 
  672. if ($settings->router_param_slug && preg_match("#{$param_slug}/?\$#i", $uri, $match)) { 
  673. $retval = $this->object->remove_url_segment($match[0], $retval); 
  674. $this->object->set_app_request_uri($uri); 
  675. $retval = $this->object->get_routed_url(); 
  676. } else { 
  677. $retval = $this->object->join_paths(explode($segment, $url)); 
  678. if ($settings->router_param_slug && preg_match("#/{$param_slug}\$#i", $retval, $match)) { 
  679. $retval = $this->object->remove_url_segment($match[0], $retval); 
  680. if (is_string($retval)) { 
  681. $retval = rtrim($retval, ' ?&'); 
  682. return (is_null($retval) or is_numeric($retval) or is_array($retval)) ? $retval : $this->esc_url($retval); 
  683. /** 
  684. * Adds a parameter to the application's request URI 
  685. * @param string $key 
  686. * @param mixed $value 
  687. * @param mixed $id 
  688. */ 
  689. function add_parameter_to_app_request_uri($key, $value, $id = NULL, $use_prefix = FALSE) 
  690. $settings = $this->object->_settings; 
  691. $param_slug = $settings->router_param_slug; 
  692. $uri = $this->object->get_app_request_uri(); 
  693. $parts = array($uri); 
  694. if ($param_slug && strpos($uri, $param_slug) === FALSE) { 
  695. $parts[] = $param_slug; 
  696. $parts[] = $this->object->create_parameter_segment($key, $value, $id, $use_prefix); 
  697. $this->object->set_app_request_uri($this->object->join_paths($parts)); 
  698. return $this->object->get_app_request_uri(); 
  699. /** 
  700. * Creates a parameter segment 
  701. * @param string $key 
  702. * @param mixed $value 
  703. * @param mixed $id 
  704. * @return string 
  705. */ 
  706. function create_parameter_segment($key, $value, $id = NULL, $use_prefix = FALSE) 
  707. $settings = $this->object->_settings; 
  708. if ($use_prefix) { 
  709. $key = $settings->router_param_prefix . $key; 
  710. if ($value === TRUE) { 
  711. $value = 1; 
  712. } elseif ($value == FALSE) { 
  713. $value = 0; 
  714. // null and false values 
  715. $retval = $key . $settings->router_param_separator . $value; 
  716. if ($id) { 
  717. $retval = $id . $settings->router_param_separator . $retval; 
  718. return $retval; 
  719. /** 
  720. * Alias for set_parameter_value 
  721. * @param string $key 
  722. * @param mixed $value 
  723. * @param mixed $id 
  724. */ 
  725. function set_parameter($key, $value, $id = NULL, $use_prefix = FALSE, $url = FALSE) 
  726. return $this->object->set_parameter_value($key, $value, $id, $use_prefix, $url); 
  727. /** 
  728. * Alias for set_parameter_value 
  729. * @param string $key 
  730. * @param mixed $value 
  731. * @param mixed $id 
  732. */ 
  733. function set_param($key, $value, $id = NULL, $use_prefix = FALSE, $url = FALSE) 
  734. return $this->object->set_parameter_value($key, $value, $id, $use_prefix = FALSE, $url); 
  735. /** 
  736. * Gets a parameter's value 
  737. * @param string $key 
  738. * @param mixed $id 
  739. * @param mixed $default 
  740. * @return mixed 
  741. */ 
  742. function get_parameter_value($key, $id = NULL, $default = NULL, $url = FALSE) 
  743. return $this->object->get_parameter($key, $id, $default, FALSE, $url); 
  744. /** 
  745. * Gets a parameter's matching URI segment 
  746. * @param string $key 
  747. * @param mixed $id 
  748. * @param mixed $default 
  749. * @return mixed 
  750. */ 
  751. function get_parameter_segment($key, $id = NULL, $url = FALSE) 
  752. return $this->object->get_parameter($key, $id, NULL, TRUE, $url); 
  753. /** 
  754. * Gets sources used for parsing and extracting parameters 
  755. * @return array 
  756. */ 
  757. function get_parameter_sources() 
  758. return array('querystring' => $this->object->get_formatted_querystring(), 'request_uri' => $this->object->get_app_request_uri()); 
  759. function get_postdata() 
  760. $retval = '/' . urldecode(file_get_contents("php://input")); 
  761. $settings = $this->object->_settings; 
  762. $retval = str_replace(array('&', '='), array('/', $settings->router_param_separator), $retval); 
  763. return $retval; 
  764. function get_formatted_querystring() 
  765. $retval = '/' . $this->object->get_router()->get_querystring(); 
  766. $settings = $this->object->_settings; 
  767. $retval = str_replace(array('&', '='), array('/', $settings->router_param_separator), $retval); 
  768. return $retval; 
  769. function has_parameter_segments() 
  770. $retval = FALSE; 
  771. $settings = $this->object->_settings; 
  772. $request_uri = $this->object->get_app_request_uri(); 
  773. $sep = preg_quote($settings->router_param_separator, '#'); 
  774. // If we detect the MVC_PARAM_SLUG, then we assume that we have parameters 
  775. if ($settings->router_param_slug && strpos($request_uri, '/' . $settings->router_param_slug . '/') !== FALSE) { 
  776. $retval = TRUE; 
  777. // If the above didn't pass, then we try finding parameters in our 
  778. // desired format 
  779. if (!$retval) { 
  780. $regex = implode('', array('#', $settings->router_param_slug ? '/' . preg_quote($settings->router_param_slug, '#') . '/?' : '', "(/?([^/]+{$sep})?[^/]+{$sep}[^/]+/?) {0, }", '$#')); 
  781. $retval = preg_match($regex, $request_uri); 
  782. return $retval; 
  783. /** 
  784. * Recursively calls stripslashes() on strings, arrays, and objects 
  785. * 
  786. * @param mixed $value Value to be processed 
  787. * @return mixed Resulting value 
  788. */ 
  789. function recursive_stripslashes($value) 
  790. if (is_string($value)) { 
  791. $value = stripslashes($value); 
  792. } elseif (is_array($value)) { 
  793. foreach ($value as &$tmp) { 
  794. $tmp = $this->object->recursive_stripslashes($tmp); 
  795. } elseif (is_object($value)) { 
  796. foreach (get_object_vars($value) as $key => $data) { 
  797. $value->{$key} = recursive_stripslashes($data); 
  798. return $value; 
  799. /** 
  800. * Class C_Routing_App 
  801. * @mixin Mixin_Url_Manipulation 
  802. * @mixin Mixin_Routing_App 
  803. * @implements I_Routing_App 
  804. */ 
  805. class C_Routing_App extends C_Component 
  806. static $_instances = array(); 
  807. var $_request_uri = FALSE; 
  808. var $_settings = null; 
  809. function define($context = FALSE) 
  810. parent::define($context); 
  811. $this->add_mixin('Mixin_Url_Manipulation'); 
  812. $this->add_mixin('Mixin_Routing_App'); 
  813. $this->implement('I_Routing_App'); 
  814. function initialize() 
  815. parent::initialize(); 
  816. $this->_settings = $this->object->get_routing_settings(); 
  817. function get_routing_settings() 
  818. $settings = C_NextGen_Settings::get_instance(); 
  819. $object = new stdClass(); 
  820. $object->router_param_separator = $settings->router_param_separator; 
  821. $object->router_param_slug = $settings->router_param_slug; 
  822. $object->router_param_prefix = $settings->router_param_prefix; 
  823. return $object; 
  824. static function &get_instance($context = False) 
  825. if (!isset(self::$_instances[$context])) { 
  826. self::$_instances[$context] = new C_Routing_App($context); 
  827. return self::$_instances[$context]; 
  828. class Mixin_Url_Manipulation extends Mixin 
  829. function join_paths() 
  830. $args = func_get_args(); 
  831. $parts = $this->_flatten_array($args); 
  832. foreach ($parts as &$part) { 
  833. $part = trim(str_replace("\\", '/', $part), "/"); 
  834. return implode('/', $parts); 
  835. /** 
  836. * Removes a segment from a url 
  837. * @param string $segment 
  838. * @param string $url 
  839. * @return string 
  840. */ 
  841. function remove_url_segment($segment, $url) 
  842. $retval = $url; 
  843. $parts = parse_url($url); 
  844. // If the url has a path, then we can remove a segment 
  845. if (isset($parts['path']) && $segment != '/') { 
  846. if (substr($segment, -1) == '/') { 
  847. $segment = substr($segment, -1); 
  848. $segment = preg_quote($segment, '#'); 
  849. if (preg_match("#{$segment}#", $parts['path'], $matches)) { 
  850. $parts['path'] = str_replace('//', '/', str_replace($matches[0], '', $parts['path'])); 
  851. $retval = $this->object->construct_url_from_parts($parts); 
  852. return $retval; 
  853. /** 
  854. * Flattens an array of arrays to a single array 
  855. * @param array $array 
  856. * @param array $parent (optional) 
  857. * @param bool $exclude_duplicates (optional - defaults to TRUE) 
  858. * @return array 
  859. */ 
  860. function _flatten_array($array, $parent = NULL, $exclude_duplicates = TRUE) 
  861. if (is_array($array)) { 
  862. // We're to add each element to the parent array 
  863. if ($parent) { 
  864. foreach ($array as $index => $element) { 
  865. foreach ($this->_flatten_array($array) as $sub_element) { 
  866. if ($exclude_duplicates) { 
  867. if (!in_array($sub_element, $parent)) { 
  868. $parent[] = $sub_element; 
  869. } else { 
  870. $parent[] = $sub_element; 
  871. $array = $parent; 
  872. } else { 
  873. $index = 0; 
  874. while (isset($array[$index])) { 
  875. $element = $array[$index]; 
  876. if (is_array($element)) { 
  877. $array = $this->_flatten_array($element, $array); 
  878. unset($array[$index]); 
  879. $index += 1; 
  880. $array = array_values($array); 
  881. } else { 
  882. $array = array($array); 
  883. return $array; 
  884. function join_querystrings() 
  885. $parts = array(); 
  886. $retval = array(); 
  887. $params = func_get_args(); 
  888. $parts = $this->_flatten_array($params); 
  889. foreach ($parts as $part) { 
  890. $part = explode("&", $part); 
  891. foreach ($part as $segment) { 
  892. $segment = explode("=", $segment); 
  893. $key = $segment[0]; 
  894. $value = isset($segment[1]) ? $segment[1] : ''; 
  895. $retval[$key] = $value; 
  896. return $this->object->assoc_array_to_querystring($retval); 
  897. function assoc_array_to_querystring($arr) 
  898. $retval = array(); 
  899. foreach ($arr as $key => $val) { 
  900. if (strlen($key)) { 
  901. $retval[] = strlen($val) ? "{$key}={$val}" : $key; 
  902. return implode("&", $retval); 
  903. /** 
  904. * Constructs a url from individual parts, created by parse_url 
  905. * @param array $parts 
  906. * @return string 
  907. */ 
  908. function construct_url_from_parts($parts) 
  909. // let relative paths be relative, and full paths full 
  910. $prefix = ''; 
  911. if (!empty($parts['scheme']) && !empty($parts['host'])) { 
  912. $prefix = $parts['scheme'] . '://' . $parts['host']; 
  913. if (!empty($parts['port'])) { 
  914. $prefix .= ':' . $parts['port']; 
  915. $retval = $this->object->join_paths($prefix, isset($parts['path']) ? str_replace('//', '/', trailingslashit($parts['path'])) : ''); 
  916. if (isset($parts['query']) && $parts['query']) { 
  917. $retval .= untrailingslashit("?{$parts['query']}"); 
  918. return $retval; 
  919. function get_parameter_segments($request_uri) 
  920. return str_replace($this->strip_param_segments($request_uri), '', $request_uri); 
  921. /** 
  922. * Returns the request uri with the parameter segments stripped 
  923. * @param string $request_uri 
  924. * @return string 
  925. */ 
  926. function strip_param_segments($request_uri, $remove_slug = TRUE) 
  927. $retval = $request_uri ? $request_uri : '/'; 
  928. $settings = C_NextGen_Settings::get_instance(); 
  929. $sep = preg_quote($settings->router_param_separator, '#'); 
  930. $param_regex = "#((?P<id>\\w+) {$sep})?(?<key>\\w+) {$sep}(?P<value>.+)/?\$#"; 
  931. $slug = $settings->router_param_slug && $remove_slug ? '/' . preg_quote($settings->router_param_slug, '#') : ''; 
  932. $slug_regex = '#' . $slug . '/?$#'; 
  933. // Remove all parameters 
  934. while (@preg_match($param_regex, $retval, $matches)) { 
  935. $match_regex = '#' . preg_quote(array_shift($matches), '#') . '$#'; 
  936. $retval = preg_replace($match_regex, '', $retval); 
  937. // Remove the slug or trailing slash 
  938. if (@preg_match($slug_regex, $retval, $matches)) { 
  939. $match_regex = '#' . preg_quote(array_shift($matches), '#') . '$#'; 
  940. $retval = preg_replace($match_regex, '', $retval); 
  941. // If there's a slug, we can assume everything after is a parameter,  
  942. // even if it's not in our desired format. 
  943. $retval = preg_replace('#' . $slug . '.*$#', '', $retval); 
  944. if (!$retval) { 
  945. $retval = '/'; 
  946. return $retval; 
.