Source for file RequestContext.class.php

Documentation is available at RequestContext.class.php

  1. <?php
  2.  
  3. /**
  4. * This class is responsible for maintaining contextual and request data to be accessed
  5. * by the program. It is also responsible for the creation of new URLs (see {@link URLWriter}),
  6. * and keeping all contextual/request variables within the namespace (or scope) of the
  7. * program that uses them.
  8. *
  9. * @package harmoni.architecture.request
  10. * @abstract
  11. * @copyright Copyright &copy; 2005, Middlebury College
  12. * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License (GPL)
  13. *
  14. * @version $Id: RequestContext.class.php,v 1.24 2007/09/04 20:25:31 adamfranco Exp $
  15. */
  16.  
  17. define("REQUEST_HANDLER_CONTEXT_DELIMETER", "___");
  18.  
  19. class RequestContext {
  20.  
  21. /*******************************************************
  22. * Class Methods
  23. *********************************************************/
  24.  
  25.  
  26. /**
  27. * A quick shortcut function to get the expanded contextual name for a form
  28. * field or request variable. Calls {@link RequestContext::getName()} with the
  29. * passed $key.
  30. *
  31. * Returns the full contextual name of a field or variable. If passed "test",
  32. * it may return something like "context1.context2.test". This function is useful
  33. * when creating HTML forms.
  34. *
  35. * @param string $key
  36. * @return string
  37. * @static
  38. * @access public
  39. */
  40. function name($key) {
  41. $harmoni = Harmoni::instance();
  42. return $harmoni->request->getName($key);
  43. }
  44. /**
  45. * A quick shortcut function to get the value of the variable in the current context.
  46. * Calls {@link RequestContext::get()} with the passed $key.
  47. *
  48. * Returns the string-value of the $key passsed. It will first check for request
  49. * data under that name, then context data. The key passed will be located
  50. * within the current namespace. If you pass a name like "context1/context2/name",
  51. * the RequestContext uses it as a context-insensitive name (ie, you are specifying
  52. * the absolute namespace).
  53. *
  54. * @return string
  55. * @param string $key
  56. * @static
  57. * @access public
  58. */
  59. function value($key) {
  60. $harmoni = Harmoni::instance();
  61. return $harmoni->request->get($key);
  62. }
  63. /**
  64. * Send a properly formatted location header. XHTML specifies that the
  65. * ampersand character, '&' be replaced with '&amp;'. Location headers however
  66. * fail to redirect with '&amp;'. This method properly formats location headers.
  67. *
  68. * @param string $url
  69. * @return void
  70. * @access public
  71. * @static
  72. * @since 7/19/05
  73. */
  74. function locationHeader ( $url ) {
  75. header("Location: ".str_replace("&amp;", "&", $url));
  76. exit;
  77. }
  78. /**
  79. * Send the browser to the url specified. Location headers will be sent if
  80. * possible, otherwise a javascript redirect will be printed outside of
  81. * all output buffers
  82. *
  83. * @param string $url
  84. * @return void
  85. * @access public
  86. * @static
  87. * @since 6/14/06
  88. */
  89. function sendTo ( $url ) {
  90. // use headers if possible
  91. if (!headers_sent())
  92. RequestContext::locationHeader($url);
  93. // Use javascript
  94. else {
  95. $harmoni = Harmoni::instance();
  96. // get rid of all output buffers;
  97. $harmoni->request->ob_jump();
  98. $unescapedurl = preg_replace("/&amp;/", "&", $url);
  99. $label = _("You should be automatically redirected. If not, click here to continue.");
  100. print <<< END
  101. <script type='text/javascript'>
  102. /* <![CDATA[ */
  103. window.location = '$unescapedurl';
  104. /* ]]> */
  105. </script>
  106. <a href='$url'>$label</a>
  107. END;
  108. $harmoni->request->ob_land();
  109. exit;
  110. }
  111. }
  112.  
  113. /*******************************************************
  114. * Instance Vars
  115. *********************************************************/
  116.  
  117. /**
  118. * @access private
  119. * @var array $_namespaces
  120. */
  121. var $_namespaces;
  122. /**
  123. * @access private
  124. * @var string $_currentNamespace
  125. */
  126. var $_currentNamespace;
  127. /**
  128. * @access private
  129. * @var array $_contextData
  130. */
  131. var $_contextData;
  132. /**
  133. * @access private
  134. * @var array $_requestData
  135. */
  136. var $_requestData;
  137. /**
  138. * @access private
  139. * @var array $_fileData
  140. */
  141. var $_fileData;
  142. /**
  143. * @access private
  144. * @var object RequestHandler $_requestHandler
  145. */
  146. var $_requestHandler;
  147. /*******************************************************
  148. * Instance Methods
  149. *********************************************************/
  150.  
  151. /**
  152. * Constructor.
  153. * @access public
  154. */
  155. function RequestContext() {
  156. $this->_namespaces = array(); // normal
  157. $this->_contextData = array(); // associative
  158. $this->_requestData = array(); // associative
  159. $this->_currentNamespace = null;
  160. }
  161. /**
  162. * Assigns a {@link RequestHandler} to this RequestContext object. The RequestHandler
  163. * provides the bridge between the browser and the data driving requests.
  164. * @param ref object RequestHandler $handler
  165. * @return void
  166. * @access public
  167. */
  168. function assignRequestHandler($handler) {
  169. $this->_requestHandler =$handler;
  170. }
  171. /**
  172. * Tells the {@link RequestHandler} to retrieve any request variables from
  173. * the browser and updates the internal context and request data to match.
  174. * @return void
  175. * @access public
  176. */
  177. function update() {
  178. // get from the RequestHandler any new request variables, etc
  179. // ... update any "persistent" values we may have
  180. $this->_checkForHandler();
  181. $newValues = $this->_requestHandler->getRequestVariables();
  182. foreach ($newValues as $key=>$value) {
  183. if (isset($this->_contextData[$key])) {
  184. $this->_contextData[$key] = $value;
  185. } else {
  186. $this->_requestData[$key] = $value;
  187. }
  188. }
  189. $this->_fileData = $this->_requestHandler->getFileVariables();
  190. }
  191. /**
  192. * Returns a string ("module.action" format) of the requested module and
  193. * action.
  194. * @return string
  195. * @access public
  196. */
  197. function getRequestedModuleAction() {
  198. $this->_checkForHandler();
  199. return $this->_requestHandler->getRequestedModuleAction();
  200. }
  201. /**
  202. * Answer the currently requested module
  203. *
  204. * @return string
  205. * @access public
  206. * @since 5/23/07
  207. */
  208. function getRequestedModule () {
  209. $parts = explode(".", $this->getRequestedModuleAction());
  210. return $parts[0];
  211. }
  212. /**
  213. * Answer the currently requested action
  214. *
  215. * @return string
  216. * @access public
  217. * @since 5/23/07
  218. */
  219. function getRequestedAction () {
  220. $parts = explode(".", $this->getRequestedModuleAction());
  221. return $parts[1];
  222. }
  223. /**
  224. * Returns a new {@link URLWriter} from the {@link RequestHandler}, assigning
  225. * the module/action passed or keeping the current module/action.
  226. * @param optional string $module
  227. * @param optional string $action
  228. * @param optional array $variables
  229. * @return ref object URLWriter
  230. * @access public
  231. */
  232. function mkURL($module = null, $action = null, $variables = null ) {
  233. // create a new URLWriter from the RequestHandler
  234. $this->_checkForHandler();
  235. $url =$this->_requestHandler->createURLWriter();
  236. // Set the Module and Action
  237. if ($module != null && $action != null) {
  238. $url->setModuleAction($module, $action);
  239. } else {
  240. $harmoni = Harmoni::instance();
  241. list($module, $action) = explode(".",$harmoni->getCurrentAction());
  242. if (!$module)
  243. list($module, $action) = explode(".",$this->getRequestedModuleAction());
  244.  
  245. $url->setModuleAction($module, $action);
  246. }
  247. // Add the current context data.
  248. $url->batchSetValues($this->_contextData);
  249. // Addition $variables passed
  250. if (is_array($variables)) {
  251. $url->setValues($variables);
  252. }
  253. return $url;
  254. }
  255. /**
  256. * Returns a new {@link URLWriter} from the {@link RequestHandler}, assigning
  257. * the module/action passed or keeping the current module/action. As well,
  258. * mkFullURL passes through all Request and Context data through to the resulting
  259. * Url.
  260. *
  261. * @param optional string $module
  262. * @param optional string $action
  263. * @return ref object URLWriter
  264. * @since 6/7/05
  265. */
  266. function mkURLWithPassthrough ( $module = null, $action = null ) {
  267. $url =$this->mkURL($module, $action);
  268. $url->batchSetValues($this->_requestData);
  269. return $url;
  270. }
  271. /**
  272. * Quickly generates a URL string from the optional passed module/action and the
  273. * optional array of values to replace in the URL request data. The function can
  274. * take any of the following forms:
  275. *
  276. * quickURL(string $module, string $action)
  277. * quickURL(string $module, string $action, array $variables)
  278. * quickURL(array $variables)
  279. * quickURL()
  280. *
  281. * if the module/action are omitted, the last requested module/action is used.
  282. * @return string
  283. * @access public
  284. */
  285. function quickURL(/* variable-length argument list */) {
  286. $num = func_num_args();
  287. $args = func_get_args();
  288. // Special Case, only an array of variables is passed
  289. if ($num == 1 && is_array($args[0])) {
  290. $url =$this->mkURL();
  291. $url->setValues($args[0]);
  292. }
  293. // Normal Case
  294. else {
  295. if (!isset($args[0]))
  296. $args[0] = NULL;
  297.  
  298. if (!isset($args[1]))
  299. $args[1] = NULL;
  300.  
  301. if (!isset($args[2]))
  302. $args[2] = NULL;
  303. $url =$this->mkURL($args[0], $args[1], $args[2]);
  304. }
  305. return $url->write();
  306. }
  307. /**
  308. * Returns the full contextual name of a field or variable. If passed "test",
  309. * it may return something like "context1.context2.test". This function is useful
  310. * when creating HTML forms.
  311. * @param string $name
  312. * @return string
  313. * @access public
  314. */
  315. function getName($name) {
  316. return $this->_mkFullName($name);
  317. }
  318. /**
  319. * Starts a new namespace below the current namespace. This allows for the
  320. * separation of context/request variables. Namespaces can be embedded, so
  321. * it is important to call {@link RequestContext::endNamespace()}.
  322. * @param string $name The name of the new namespace.
  323. * @return void
  324. * @access public
  325. */
  326. function startNamespace($name) {
  327. $this->_checkName($name);
  328. if ($this->_currentNamespace) $this->_namespaces[] = $this->_currentNamespace;
  329. $this->_currentNamespace = $name;
  330. }
  331. /**
  332. * Ends a namespace started previously. The last-started namespace is ended.
  333. * @return string The name of the namespace just ended.
  334. * @access public
  335. */
  336. function endNamespace() {
  337. if ($this->_currentNamespace == null) return;
  338. $curr = $this->_currentNamespace;
  339. $n = count($this->_namespaces);
  340. if ($n == 0) {
  341. $this->_currentNamespace = null;
  342. return $curr;
  343. }
  344. $this->_currentNamespace = array_pop($this->_namespaces);
  345. return $curr;
  346. }
  347. /**
  348. * Sets $key to $value in the context-data (this data is included automatically
  349. * when building new URLs). If you pass a name like "context1/context2/name",
  350. * the RequestContext uses it as a context-insensitive name (ie, you are specifying
  351. * the absolute namespace).
  352. * @param string $key
  353. * @param string $value
  354. * @return void
  355. * @access public
  356. */
  357. function set($key, $value) {
  358. $this->_checkName($key);
  359. $nKey = $this->_mkFullName($key);
  360. $this->_contextData[$nKey] = $value;
  361. }
  362. /**
  363. * Ensures that $key is removed from the context-data (and not included in URLs
  364. * generated later). If you pass a name like "context1/context2/name",
  365. * the RequestContext uses it as a context-insensitive name (ie, you are specifying
  366. * the absolute namespace).
  367. * @param string $key the key to forget.
  368. * @return void
  369. * @access public
  370. */
  371. function forget($key) {
  372. // makes sure that the key is not present in the saved context any more
  373. $nKey = $this->_mkFullName($key);
  374. unset($this->_contextData[$nKey]);
  375. }
  376. /**
  377. * Copies the value(s) of keys passed from the request-data to the context
  378. * data. This is useful when request variables will be re-used later as contextual
  379. * variables. If you pass a name like "context1/context2/name",
  380. * the RequestContext uses it as a context-insensitive name (ie, you are specifying
  381. * the absolute namespace). If no parameters are passed, all variables in the
  382. * current namespaced are passed through.
  383. * @param optional string $key1,...
  384. * @return void
  385. * @access public
  386. */
  387. function passthrough(/* variable-length argument list */) {
  388. // copy the request data for each key passed into the context data
  389. // (ensures its transfer in the next URL request)
  390. if (func_num_args() == 0) $args = $this->getKeys();
  391. else $args = func_get_args();
  392. foreach ($args as $arg) {
  393. $this->_checkName($arg);
  394. $nKey = $this->_mkFullName($arg);
  395. if (isset($this->_requestData[$nKey])) $this->_contextData[$nKey] = $this->_requestData[$nKey];
  396. }
  397. }
  398. /**
  399. * Returns the string-value of the $key passsed. It will first check for request
  400. * data under that name, then context data. The key passed will be located
  401. * within the current namespace. If you pass a name like "context1/context2/name",
  402. * the RequestContext uses it as a context-insensitive name (ie, you are specifying
  403. * the absolute namespace).
  404. * @return mixed Either a string, in the case of a regular key, or an array in the case of a file.
  405. * @param string $key
  406. * @access public
  407. */
  408. function get($key) {
  409. // first check if we have a request variable appropriately named,
  410. // then check our context data, lastly return NULL
  411. $nKey = $this->_mkFullName($key);
  412. if (isset($this->_requestData[$nKey])) return $this->_requestData[$nKey];
  413. if (isset($this->_fileData[$nKey])) return $this->_fileData[$nKey];
  414. if (isset($this->_contextData[$nKey])) return $this->_contextData[$nKey];
  415. return null;
  416. }
  417. /**
  418. * Returns a list of keys within the current context.
  419. * @return array
  420. * @access public
  421. */
  422. function getKeys() {
  423. $pre = $this->_currentNamespace;
  424. $array = array();
  425. if ($pre) {
  426. $keys = array_unique(array_merge(array_keys($this->_fileData), array_merge(array_keys($this->_requestData), array_keys($this->_contextData))));
  427. foreach ($keys as $key) {
  428. if (ereg("^$pre\\".REQUEST_HANDLER_CONTEXT_DELIMETER."(.+)", $key, $r)) {
  429. $array[] = $r[1];
  430. }
  431. }
  432. } else {
  433. $skip = array('module', 'action');
  434. foreach ($this->_requestData as $key => $val) {
  435. if (!in_array($key, $skip))
  436. $array[] = $key;
  437. }
  438. }
  439. return $array;
  440. }
  441. /**
  442. * @access private
  443. * @return void
  444. */
  445. function _checkForHandler() {
  446. if (!isset($this->_requestHandler)) {
  447. throwError( new Error("RequestContext requires a RequestHandler for proper functionality! Please set one by calling RequestContext::assignRequestHandler()", "RequestContext", true));
  448. }
  449. }
  450. /**
  451. * @access private
  452. * @return string
  453. */
  454. function _mkFullName($key) {
  455. // in the request, periods and spaces will get converted to '_'s, so
  456. // do that preemptively
  457. $key = preg_replace('/[\.\s\n\r\t]/', '_', $key);
  458. if ($this->_currentNamespace == null)
  459. return $key;
  460. else
  461. return $this->_currentNamespace.REQUEST_HANDLER_CONTEXT_DELIMETER.$key;
  462. }
  463. /**
  464. * @access private
  465. * @return void
  466. */
  467. function _checkName($name) {
  468. if (ereg("\\".REQUEST_HANDLER_CONTEXT_DELIMETER, $name)) {
  469. throwError( new Error("Namespaces and field names cannot contain \"".REQUEST_HANDLER_CONTEXT_DELIMETER."\"s!", "RequestHandler", true));
  470. }
  471. }
  472. /**
  473. * Climbs down the ob ladder and saves the data to put back later
  474. *
  475. * @access public
  476. * @return void
  477. * @since 6/14/06
  478. */
  479. function ob_jump () {
  480. if (!isset($this->_ob_data))
  481. $this->_ob_data = array();
  482. $level = ob_get_level();
  483. while ($level > 0) {
  484. $this->_ob_data[$level] = ob_get_clean();
  485. $level = ob_get_level();
  486. }
  487. }
  488.  
  489. /**
  490. * Climps back up the ob ladder adding back the data that was there
  491. *
  492. * @access public
  493. * @return void
  494. * @since 6/14/06
  495. */
  496. function ob_land() {
  497. if (!isset($this->_ob_data))
  498. return;
  499. foreach ($this->_ob_data as $level => $data) {
  500. ob_start();
  501. print $data;
  502. unset($this->_ob_data[$level]);
  503. }
  504. }
  505. }
  506.  
  507. ?>

Documentation generated on Wed, 19 Sep 2007 10:26:04 -0400 by phpDocumentor 1.3.0RC3