2
0

Action.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Controller
  17. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * @see Zend_Controller_Action_HelperBroker
  22. */
  23. require_once 'Zend/Controller/Action/HelperBroker.php';
  24. /**
  25. * @see Zend_Controller_Action_Interface
  26. */
  27. require_once 'Zend/Controller/Action/Interface.php';
  28. /**
  29. * @see Zend_Controller_Front
  30. */
  31. require_once 'Zend/Controller/Front.php';
  32. /**
  33. * @category Zend
  34. * @package Zend_Controller
  35. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. abstract class Zend_Controller_Action implements Zend_Controller_Action_Interface
  39. {
  40. /**
  41. * @var array of existing class methods
  42. */
  43. protected $_classMethods;
  44. /**
  45. * Word delimiters (used for normalizing view script paths)
  46. * @var array
  47. */
  48. protected $_delimiters;
  49. /**
  50. * Array of arguments provided to the constructor, minus the
  51. * {@link $_request Request object}.
  52. * @var array
  53. */
  54. protected $_invokeArgs = array();
  55. /**
  56. * Front controller instance
  57. * @var Zend_Controller_Front
  58. */
  59. protected $_frontController;
  60. /**
  61. * Zend_Controller_Request_Abstract object wrapping the request environment
  62. * @var Zend_Controller_Request_Abstract
  63. */
  64. protected $_request = null;
  65. /**
  66. * Zend_Controller_Response_Abstract object wrapping the response
  67. * @var Zend_Controller_Response_Abstract
  68. */
  69. protected $_response = null;
  70. /**
  71. * View script suffix; defaults to 'phtml'
  72. * @see {render()}
  73. * @var string
  74. */
  75. public $viewSuffix = 'phtml';
  76. /**
  77. * View object
  78. * @var Zend_View_Interface
  79. */
  80. public $view;
  81. /**
  82. * Helper Broker to assist in routing help requests to the proper object
  83. *
  84. * @var Zend_Controller_Action_HelperBroker
  85. */
  86. protected $_helper = null;
  87. /**
  88. * Class constructor
  89. *
  90. * The request and response objects should be registered with the
  91. * controller, as should be any additional optional arguments; these will be
  92. * available via {@link getRequest()}, {@link getResponse()}, and
  93. * {@link getInvokeArgs()}, respectively.
  94. *
  95. * When overriding the constructor, please consider this usage as a best
  96. * practice and ensure that each is registered appropriately; the easiest
  97. * way to do so is to simply call parent::__construct($request, $response,
  98. * $invokeArgs).
  99. *
  100. * After the request, response, and invokeArgs are set, the
  101. * {@link $_helper helper broker} is initialized.
  102. *
  103. * Finally, {@link init()} is called as the final action of
  104. * instantiation, and may be safely overridden to perform initialization
  105. * tasks; as a general rule, override {@link init()} instead of the
  106. * constructor to customize an action controller's instantiation.
  107. *
  108. * @param Zend_Controller_Request_Abstract $request
  109. * @param Zend_Controller_Response_Abstract $response
  110. * @param array $invokeArgs Any additional invocation arguments
  111. * @return void
  112. */
  113. public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array())
  114. {
  115. $this->setRequest($request)
  116. ->setResponse($response)
  117. ->_setInvokeArgs($invokeArgs);
  118. $this->_helper = new Zend_Controller_Action_HelperBroker($this);
  119. $this->init();
  120. }
  121. /**
  122. * Initialize object
  123. *
  124. * Called from {@link __construct()} as final step of object instantiation.
  125. *
  126. * @return void
  127. */
  128. public function init()
  129. {
  130. }
  131. /**
  132. * Initialize View object
  133. *
  134. * Initializes {@link $view} if not otherwise a Zend_View_Interface.
  135. *
  136. * If {@link $view} is not otherwise set, instantiates a new Zend_View
  137. * object, using the 'views' subdirectory at the same level as the
  138. * controller directory for the current module as the base directory.
  139. * It uses this to set the following:
  140. * - script path = views/scripts/
  141. * - helper path = views/helpers/
  142. * - filter path = views/filters/
  143. *
  144. * @return Zend_View_Interface
  145. * @throws Zend_Controller_Exception if base view directory does not exist
  146. */
  147. public function initView()
  148. {
  149. if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
  150. return $this->view;
  151. }
  152. require_once 'Zend/View/Interface.php';
  153. if (isset($this->view) && ($this->view instanceof Zend_View_Interface)) {
  154. return $this->view;
  155. }
  156. $request = $this->getRequest();
  157. $module = $request->getModuleName();
  158. $dirs = $this->getFrontController()->getControllerDirectory();
  159. if (empty($module) || !isset($dirs[$module])) {
  160. $module = $this->getFrontController()->getDispatcher()->getDefaultModule();
  161. }
  162. $baseDir = dirname($dirs[$module]) . DIRECTORY_SEPARATOR . 'views';
  163. if (!file_exists($baseDir) || !is_dir($baseDir)) {
  164. require_once 'Zend/Controller/Exception.php';
  165. throw new Zend_Controller_Exception('Missing base view directory ("' . $baseDir . '")');
  166. }
  167. require_once 'Zend/View.php';
  168. $this->view = new Zend_View(array('basePath' => $baseDir));
  169. return $this->view;
  170. }
  171. /**
  172. * Render a view
  173. *
  174. * Renders a view. By default, views are found in the view script path as
  175. * <controller>/<action>.phtml. You may change the script suffix by
  176. * resetting {@link $viewSuffix}. You may omit the controller directory
  177. * prefix by specifying boolean true for $noController.
  178. *
  179. * By default, the rendered contents are appended to the response. You may
  180. * specify the named body content segment to set by specifying a $name.
  181. *
  182. * @see Zend_Controller_Response_Abstract::appendBody()
  183. * @param string|null $action Defaults to action registered in request object
  184. * @param string|null $name Response object named path segment to use; defaults to null
  185. * @param bool $noController Defaults to false; i.e. use controller name as subdir in which to search for view script
  186. * @return void
  187. */
  188. public function render($action = null, $name = null, $noController = false)
  189. {
  190. if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
  191. return $this->_helper->viewRenderer->render($action, $name, $noController);
  192. }
  193. $view = $this->initView();
  194. $script = $this->getViewScript($action, $noController);
  195. $this->getResponse()->appendBody(
  196. $view->render($script),
  197. $name
  198. );
  199. }
  200. /**
  201. * Render a given view script
  202. *
  203. * Similar to {@link render()}, this method renders a view script. Unlike render(),
  204. * however, it does not autodetermine the view script via {@link getViewScript()},
  205. * but instead renders the script passed to it. Use this if you know the
  206. * exact view script name and path you wish to use, or if using paths that do not
  207. * conform to the spec defined with getViewScript().
  208. *
  209. * By default, the rendered contents are appended to the response. You may
  210. * specify the named body content segment to set by specifying a $name.
  211. *
  212. * @param string $script
  213. * @param string $name
  214. * @return void
  215. */
  216. public function renderScript($script, $name = null)
  217. {
  218. if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
  219. return $this->_helper->viewRenderer->renderScript($script, $name);
  220. }
  221. $view = $this->initView();
  222. $this->getResponse()->appendBody(
  223. $view->render($script),
  224. $name
  225. );
  226. }
  227. /**
  228. * Construct view script path
  229. *
  230. * Used by render() to determine the path to the view script.
  231. *
  232. * @param string $action Defaults to action registered in request object
  233. * @param bool $noController Defaults to false; i.e. use controller name as subdir in which to search for view script
  234. * @return string
  235. * @throws Zend_Controller_Exception with bad $action
  236. */
  237. public function getViewScript($action = null, $noController = null)
  238. {
  239. if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
  240. $viewRenderer = $this->_helper->getHelper('viewRenderer');
  241. if (null !== $noController) {
  242. $viewRenderer->setNoController($noController);
  243. }
  244. return $viewRenderer->getViewScript($action);
  245. }
  246. $request = $this->getRequest();
  247. if (null === $action) {
  248. $action = $request->getActionName();
  249. } elseif (!is_string($action)) {
  250. require_once 'Zend/Controller/Exception.php';
  251. throw new Zend_Controller_Exception('Invalid action specifier for view render');
  252. }
  253. if (null === $this->_delimiters) {
  254. $dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
  255. $wordDelimiters = $dispatcher->getWordDelimiter();
  256. $pathDelimiters = $dispatcher->getPathDelimiter();
  257. $this->_delimiters = array_unique(array_merge($wordDelimiters, (array) $pathDelimiters));
  258. }
  259. $action = str_replace($this->_delimiters, '-', $action);
  260. $script = $action . '.' . $this->viewSuffix;
  261. if (!$noController) {
  262. $controller = $request->getControllerName();
  263. $controller = str_replace($this->_delimiters, '-', $controller);
  264. $script = $controller . DIRECTORY_SEPARATOR . $script;
  265. }
  266. return $script;
  267. }
  268. /**
  269. * Return the Request object
  270. *
  271. * @return Zend_Controller_Request_Abstract
  272. */
  273. public function getRequest()
  274. {
  275. return $this->_request;
  276. }
  277. /**
  278. * Set the Request object
  279. *
  280. * @param Zend_Controller_Request_Abstract $request
  281. * @return Zend_Controller_Action
  282. */
  283. public function setRequest(Zend_Controller_Request_Abstract $request)
  284. {
  285. $this->_request = $request;
  286. return $this;
  287. }
  288. /**
  289. * Return the Response object
  290. *
  291. * @return Zend_Controller_Response_Abstract
  292. */
  293. public function getResponse()
  294. {
  295. return $this->_response;
  296. }
  297. /**
  298. * Set the Response object
  299. *
  300. * @param Zend_Controller_Response_Abstract $response
  301. * @return Zend_Controller_Action
  302. */
  303. public function setResponse(Zend_Controller_Response_Abstract $response)
  304. {
  305. $this->_response = $response;
  306. return $this;
  307. }
  308. /**
  309. * Set invocation arguments
  310. *
  311. * @param array $args
  312. * @return Zend_Controller_Action
  313. */
  314. protected function _setInvokeArgs(array $args = array())
  315. {
  316. $this->_invokeArgs = $args;
  317. return $this;
  318. }
  319. /**
  320. * Return the array of constructor arguments (minus the Request object)
  321. *
  322. * @return array
  323. */
  324. public function getInvokeArgs()
  325. {
  326. return $this->_invokeArgs;
  327. }
  328. /**
  329. * Return a single invocation argument
  330. *
  331. * @param string $key
  332. * @return mixed
  333. */
  334. public function getInvokeArg($key)
  335. {
  336. if (isset($this->_invokeArgs[$key])) {
  337. return $this->_invokeArgs[$key];
  338. }
  339. return null;
  340. }
  341. /**
  342. * Get a helper by name
  343. *
  344. * @param string $helperName
  345. * @return Zend_Controller_Action_Helper_Abstract
  346. */
  347. public function getHelper($helperName)
  348. {
  349. return $this->_helper->{$helperName};
  350. }
  351. /**
  352. * Get a clone of a helper by name
  353. *
  354. * @param string $helperName
  355. * @return Zend_Controller_Action_Helper_Abstract
  356. */
  357. public function getHelperCopy($helperName)
  358. {
  359. return clone $this->_helper->{$helperName};
  360. }
  361. /**
  362. * Set the front controller instance
  363. *
  364. * @param Zend_Controller_Front $front
  365. * @return Zend_Controller_Action
  366. */
  367. public function setFrontController(Zend_Controller_Front $front)
  368. {
  369. $this->_frontController = $front;
  370. return $this;
  371. }
  372. /**
  373. * Retrieve Front Controller
  374. *
  375. * @return Zend_Controller_Front
  376. */
  377. public function getFrontController()
  378. {
  379. // Used cache version if found
  380. if (null !== $this->_frontController) {
  381. return $this->_frontController;
  382. }
  383. // Grab singleton instance, if class has been loaded
  384. if (class_exists('Zend_Controller_Front')) {
  385. $this->_frontController = Zend_Controller_Front::getInstance();
  386. return $this->_frontController;
  387. }
  388. // Throw exception in all other cases
  389. require_once 'Zend/Controller/Exception.php';
  390. throw new Zend_Controller_Exception('Front controller class has not been loaded');
  391. }
  392. /**
  393. * Pre-dispatch routines
  394. *
  395. * Called before action method. If using class with
  396. * {@link Zend_Controller_Front}, it may modify the
  397. * {@link $_request Request object} and reset its dispatched flag in order
  398. * to skip processing the current action.
  399. *
  400. * @return void
  401. */
  402. public function preDispatch()
  403. {
  404. }
  405. /**
  406. * Post-dispatch routines
  407. *
  408. * Called after action method execution. If using class with
  409. * {@link Zend_Controller_Front}, it may modify the
  410. * {@link $_request Request object} and reset its dispatched flag in order
  411. * to process an additional action.
  412. *
  413. * Common usages for postDispatch() include rendering content in a sitewide
  414. * template, link url correction, setting headers, etc.
  415. *
  416. * @return void
  417. */
  418. public function postDispatch()
  419. {
  420. }
  421. /**
  422. * Proxy for undefined methods. Default behavior is to throw an
  423. * exception on undefined methods, however this function can be
  424. * overridden to implement magic (dynamic) actions, or provide run-time
  425. * dispatching.
  426. *
  427. * @param string $methodName
  428. * @param array $args
  429. * @return void
  430. * @throws Zend_Controller_Action_Exception
  431. */
  432. public function __call($methodName, $args)
  433. {
  434. require_once 'Zend/Controller/Action/Exception.php';
  435. if ('Action' == substr($methodName, -6)) {
  436. $action = substr($methodName, 0, strlen($methodName) - 6);
  437. throw new Zend_Controller_Action_Exception(sprintf('Action "%s" does not exist and was not trapped in __call()', $action), 404);
  438. }
  439. throw new Zend_Controller_Action_Exception(sprintf('Method "%s" does not exist and was not trapped in __call()', $methodName), 500);
  440. }
  441. /**
  442. * Dispatch the requested action
  443. *
  444. * @param string $action Method name of action
  445. * @return void
  446. */
  447. public function dispatch($action)
  448. {
  449. // Notify helpers of action preDispatch state
  450. $this->_helper->notifyPreDispatch();
  451. $this->preDispatch();
  452. if ($this->getRequest()->isDispatched()) {
  453. if (null === $this->_classMethods) {
  454. $this->_classMethods = get_class_methods($this);
  455. }
  456. // preDispatch() didn't change the action, so we can continue
  457. if ($this->getInvokeArg('useCaseSensitiveActions') || in_array($action, $this->_classMethods)) {
  458. if ($this->getInvokeArg('useCaseSensitiveActions')) {
  459. trigger_error('Using case sensitive actions without word separators is deprecated; please do not rely on this "feature"');
  460. }
  461. $this->$action();
  462. } else {
  463. $this->__call($action, array());
  464. }
  465. $this->postDispatch();
  466. }
  467. // whats actually important here is that this action controller is
  468. // shutting down, regardless of dispatching; notify the helpers of this
  469. // state
  470. $this->_helper->notifyPostDispatch();
  471. }
  472. /**
  473. * Call the action specified in the request object, and return a response
  474. *
  475. * Not used in the Action Controller implementation, but left for usage in
  476. * Page Controller implementations. Dispatches a method based on the
  477. * request.
  478. *
  479. * Returns a Zend_Controller_Response_Abstract object, instantiating one
  480. * prior to execution if none exists in the controller.
  481. *
  482. * {@link preDispatch()} is called prior to the action,
  483. * {@link postDispatch()} is called following it.
  484. *
  485. * @param null|Zend_Controller_Request_Abstract $request Optional request
  486. * object to use
  487. * @param null|Zend_Controller_Response_Abstract $response Optional response
  488. * object to use
  489. * @return Zend_Controller_Response_Abstract
  490. */
  491. public function run(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null)
  492. {
  493. if (null !== $request) {
  494. $this->setRequest($request);
  495. } else {
  496. $request = $this->getRequest();
  497. }
  498. if (null !== $response) {
  499. $this->setResponse($response);
  500. }
  501. $action = $request->getActionName();
  502. if (empty($action)) {
  503. $action = 'index';
  504. }
  505. $action = $action . 'Action';
  506. $request->setDispatched(true);
  507. $this->dispatch($action);
  508. return $this->getResponse();
  509. }
  510. /**
  511. * Gets a parameter from the {@link $_request Request object}. If the
  512. * parameter does not exist, NULL will be returned.
  513. *
  514. * If the parameter does not exist and $default is set, then
  515. * $default will be returned instead of NULL.
  516. *
  517. * @param string $paramName
  518. * @param mixed $default
  519. * @return mixed
  520. */
  521. protected function _getParam($paramName, $default = null)
  522. {
  523. $value = $this->getRequest()->getParam($paramName);
  524. if ((null == $value) && (null !== $default)) {
  525. $value = $default;
  526. }
  527. return $value;
  528. }
  529. /**
  530. * Set a parameter in the {@link $_request Request object}.
  531. *
  532. * @param string $paramName
  533. * @param mixed $value
  534. * @return Zend_Controller_Action
  535. */
  536. protected function _setParam($paramName, $value)
  537. {
  538. $this->getRequest()->setParam($paramName, $value);
  539. return $this;
  540. }
  541. /**
  542. * Determine whether a given parameter exists in the
  543. * {@link $_request Request object}.
  544. *
  545. * @param string $paramName
  546. * @return boolean
  547. */
  548. protected function _hasParam($paramName)
  549. {
  550. return null !== $this->getRequest()->getParam($paramName);
  551. }
  552. /**
  553. * Return all parameters in the {@link $_request Request object}
  554. * as an associative array.
  555. *
  556. * @return array
  557. */
  558. protected function _getAllParams()
  559. {
  560. return $this->getRequest()->getParams();
  561. }
  562. /**
  563. * Forward to another controller/action.
  564. *
  565. * It is important to supply the unformatted names, i.e. "article"
  566. * rather than "ArticleController". The dispatcher will do the
  567. * appropriate formatting when the request is received.
  568. *
  569. * If only an action name is provided, forwards to that action in this
  570. * controller.
  571. *
  572. * If an action and controller are specified, forwards to that action and
  573. * controller in this module.
  574. *
  575. * Specifying an action, controller, and module is the most specific way to
  576. * forward.
  577. *
  578. * A fourth argument, $params, will be used to set the request parameters.
  579. * If either the controller or module are unnecessary for forwarding,
  580. * simply pass null values for them before specifying the parameters.
  581. *
  582. * @param string $action
  583. * @param string $controller
  584. * @param string $module
  585. * @param array $params
  586. * @return void
  587. */
  588. final protected function _forward($action, $controller = null, $module = null, array $params = null)
  589. {
  590. $request = $this->getRequest();
  591. if (null !== $params) {
  592. $request->setParams($params);
  593. }
  594. if (null !== $controller) {
  595. $request->setControllerName($controller);
  596. // Module should only be reset if controller has been specified
  597. if (null !== $module) {
  598. $request->setModuleName($module);
  599. }
  600. }
  601. $request->setActionName($action)
  602. ->setDispatched(false);
  603. }
  604. /**
  605. * Redirect to another URL
  606. *
  607. * Proxies to {@link Zend_Controller_Action_Helper_Redirector::gotoUrl()}.
  608. *
  609. * @param string $url
  610. * @param array $options Options to be used when redirecting
  611. * @return void
  612. */
  613. protected function _redirect($url, array $options = array())
  614. {
  615. $this->_helper->redirector->gotoUrl($url, $options);
  616. }
  617. }