File indexing completed on 2025-01-26 05:24:53

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Controller
0017  * @subpackage Zend_Controller_Action_Helper
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Controller_Action_Helper_Abstract
0025  */
0026 // require_once 'Zend/Controller/Action/Helper/Abstract.php';
0027 
0028 /**
0029  * Simplify context switching based on requested format
0030  *
0031  * @uses       Zend_Controller_Action_Helper_Abstract
0032  * @category   Zend
0033  * @package    Zend_Controller
0034  * @subpackage Zend_Controller_Action_Helper
0035  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0036  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0037  */
0038 class Zend_Controller_Action_Helper_ContextSwitch extends Zend_Controller_Action_Helper_Abstract
0039 {
0040     /**
0041      * Trigger type constants
0042      */
0043     const TRIGGER_INIT = 'TRIGGER_INIT';
0044     const TRIGGER_POST = 'TRIGGER_POST';
0045 
0046     /**
0047      * Supported contexts
0048      * @var array
0049      */
0050     protected $_contexts = array();
0051 
0052     /**
0053      * JSON auto-serialization flag
0054      * @var boolean
0055      */
0056     protected $_autoJsonSerialization = true;
0057 
0058     /**
0059      * Controller property key to utilize for context switching
0060      * @var string
0061      */
0062     protected $_contextKey = 'contexts';
0063 
0064     /**
0065      * Request parameter containing requested context
0066      * @var string
0067      */
0068     protected $_contextParam = 'format';
0069 
0070     /**
0071      * Current context
0072      * @var string
0073      */
0074     protected $_currentContext;
0075 
0076     /**
0077      * Default context (xml)
0078      * @var string
0079      */
0080     protected $_defaultContext = 'xml';
0081 
0082     /**
0083      * Whether or not to disable layouts when switching contexts
0084      * @var boolean
0085      */
0086     protected $_disableLayout = true;
0087 
0088     /**
0089      * Methods that require special configuration
0090      * @var array
0091      */
0092     protected $_specialConfig = array(
0093         'setSuffix',
0094         'setHeaders',
0095         'setCallbacks',
0096     );
0097 
0098     /**
0099      * Methods that are not configurable via setOptions and setConfig
0100      * @var array
0101      */
0102     protected $_unconfigurable = array(
0103         'setOptions',
0104         'setConfig',
0105         'setHeader',
0106         'setCallback',
0107         'setContext',
0108         'setActionContext',
0109         'setActionContexts',
0110     );
0111 
0112     /**
0113      * @var Zend_Controller_Action_Helper_ViewRenderer
0114      */
0115     protected $_viewRenderer;
0116 
0117     /**
0118      * Original view suffix prior to detecting context switch
0119      * @var string
0120      */
0121     protected $_viewSuffixOrig;
0122 
0123     /**
0124      * Constructor
0125      *
0126      * @param  array|Zend_Config $options
0127      * @return void
0128      */
0129     public function __construct($options = null)
0130     {
0131         if ($options instanceof Zend_Config) {
0132             $this->setConfig($options);
0133         } elseif (is_array($options)) {
0134             $this->setOptions($options);
0135         }
0136 
0137         if (empty($this->_contexts)) {
0138             $this->addContexts(array(
0139                 'json' => array(
0140                     'suffix'    => 'json',
0141                     'headers'   => array('Content-Type' => 'application/json'),
0142                     'callbacks' => array(
0143                         'init' => 'initJsonContext',
0144                         'post' => 'postJsonContext'
0145                     )
0146                 ),
0147                 'xml'  => array(
0148                     'suffix'    => 'xml',
0149                     'headers'   => array('Content-Type' => 'application/xml'),
0150                 )
0151             ));
0152         }
0153 
0154         $this->init();
0155     }
0156 
0157     /**
0158      * Initialize at start of action controller
0159      *
0160      * Reset the view script suffix to the original state, or store the
0161      * original state.
0162      *
0163      * @return void
0164      */
0165     public function init()
0166     {
0167         if (null === $this->_viewSuffixOrig) {
0168             $this->_viewSuffixOrig = $this->_getViewRenderer()->getViewSuffix();
0169         } else {
0170             $this->_getViewRenderer()->setViewSuffix($this->_viewSuffixOrig);
0171         }
0172     }
0173 
0174     /**
0175      * Configure object from array of options
0176      *
0177      * @param  array $options
0178      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0179      */
0180     public function setOptions(array $options)
0181     {
0182         if (isset($options['contexts'])) {
0183             $this->setContexts($options['contexts']);
0184             unset($options['contexts']);
0185         }
0186 
0187         foreach ($options as $key => $value) {
0188             $method = 'set' . ucfirst($key);
0189             if (in_array($method, $this->_unconfigurable)) {
0190                 continue;
0191             }
0192 
0193             if (in_array($method, $this->_specialConfig)) {
0194                 $method = '_' . $method;
0195             }
0196 
0197             if (method_exists($this, $method)) {
0198                 $this->$method($value);
0199             }
0200         }
0201         return $this;
0202     }
0203 
0204     /**
0205      * Set object state from config object
0206      *
0207      * @param  Zend_Config $config
0208      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0209      */
0210     public function setConfig(Zend_Config $config)
0211     {
0212         return $this->setOptions($config->toArray());
0213     }
0214 
0215     /**
0216      * Strategy pattern: return object
0217      *
0218      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0219      */
0220     public function direct()
0221     {
0222         return $this;
0223     }
0224 
0225     /**
0226      * Initialize context detection and switching
0227      *
0228      * @param  mixed $format
0229      * @throws Zend_Controller_Action_Exception
0230      * @return void
0231      */
0232     public function initContext($format = null)
0233     {
0234         $this->_currentContext = null;
0235 
0236         $controller = $this->getActionController();
0237         $request    = $this->getRequest();
0238         $action     = $request->getActionName();
0239 
0240         // Return if no context switching enabled, or no context switching
0241         // enabled for this action
0242         $contexts = $this->getActionContexts($action);
0243         if (empty($contexts)) {
0244             return;
0245         }
0246 
0247         // Return if no context parameter provided
0248         if (!$context = $request->getParam($this->getContextParam())) {
0249             if ($format === null) {
0250                 return;
0251             }
0252             $context = $format;
0253             $format  = null;
0254         }
0255 
0256         // Check if context allowed by action controller
0257         if (!$this->hasActionContext($action, $context)) {
0258             return;
0259         }
0260 
0261         // Return if invalid context parameter provided and no format or invalid
0262         // format provided
0263         if (!$this->hasContext($context)) {
0264             if (empty($format) || !$this->hasContext($format)) {
0265 
0266                 return;
0267             }
0268         }
0269 
0270         // Use provided format if passed
0271         if (!empty($format) && $this->hasContext($format)) {
0272             $context = $format;
0273         }
0274 
0275         $suffix = $this->getSuffix($context);
0276 
0277         $this->_getViewRenderer()->setViewSuffix($suffix);
0278 
0279         $headers = $this->getHeaders($context);
0280         if (!empty($headers)) {
0281             $response = $this->getResponse();
0282             foreach ($headers as $header => $content) {
0283                 $response->setHeader($header, $content);
0284             }
0285         }
0286 
0287         if ($this->getAutoDisableLayout()) {
0288             /**
0289              * @see Zend_Layout
0290              */
0291             // require_once 'Zend/Layout.php';
0292             $layout = Zend_Layout::getMvcInstance();
0293             if (null !== $layout) {
0294                 $layout->disableLayout();
0295             }
0296         }
0297 
0298         if (null !== ($callback = $this->getCallback($context, self::TRIGGER_INIT))) {
0299             if (is_string($callback) && method_exists($this, $callback)) {
0300                 $this->$callback();
0301             } elseif (is_string($callback) && function_exists($callback)) {
0302                 $callback();
0303             } elseif (is_array($callback)) {
0304                 call_user_func($callback);
0305             } else {
0306                 /**
0307                  * @see Zend_Controller_Action_Exception
0308                  */
0309                 // require_once 'Zend/Controller/Action/Exception.php';
0310                 throw new Zend_Controller_Action_Exception(sprintf('Invalid context callback registered for context "%s"', $context));
0311             }
0312         }
0313 
0314         $this->_currentContext = $context;
0315     }
0316 
0317     /**
0318      * JSON context extra initialization
0319      *
0320      * Turns off viewRenderer auto-rendering
0321      *
0322      * @return void
0323      */
0324     public function initJsonContext()
0325     {
0326         if (!$this->getAutoJsonSerialization()) {
0327             return;
0328         }
0329 
0330         $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
0331         $view = $viewRenderer->view;
0332         if ($view instanceof Zend_View_Interface) {
0333             $viewRenderer->setNoRender(true);
0334         }
0335     }
0336 
0337     /**
0338      * Should JSON contexts auto-serialize?
0339      *
0340      * @param  boolean $flag
0341      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0342      */
0343     public function setAutoJsonSerialization($flag)
0344     {
0345         $this->_autoJsonSerialization = (bool) $flag;
0346         return $this;
0347     }
0348 
0349     /**
0350      * Get JSON context auto-serialization flag
0351      *
0352      * @return boolean
0353      */
0354     public function getAutoJsonSerialization()
0355     {
0356         return $this->_autoJsonSerialization;
0357     }
0358 
0359     /**
0360      * Set suffix from array
0361      *
0362      * @param  array $spec
0363      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0364      */
0365     protected function _setSuffix(array $spec)
0366     {
0367         foreach ($spec as $context => $suffixInfo) {
0368             if (!is_string($context)) {
0369                 $context = null;
0370             }
0371 
0372             if (is_string($suffixInfo)) {
0373                 $this->setSuffix($context, $suffixInfo);
0374                 continue;
0375             } elseif (is_array($suffixInfo)) {
0376                 if (isset($suffixInfo['suffix'])) {
0377                     $suffix                    = $suffixInfo['suffix'];
0378                     $prependViewRendererSuffix = true;
0379 
0380                     if ((null === $context) && isset($suffixInfo['context'])) {
0381                         $context = $suffixInfo['context'];
0382                     }
0383 
0384                     if (isset($suffixInfo['prependViewRendererSuffix'])) {
0385                         $prependViewRendererSuffix = $suffixInfo['prependViewRendererSuffix'];
0386                     }
0387 
0388                     $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
0389                     continue;
0390                 }
0391 
0392                 $count = count($suffixInfo);
0393                 switch (true) {
0394                     case (($count < 2) && (null === $context)):
0395                         /**
0396                          * @see Zend_Controller_Action_Exception
0397                          */
0398                         // require_once 'Zend/Controller/Action/Exception.php';
0399                         throw new Zend_Controller_Action_Exception('Invalid suffix information provided in config');
0400                     case ($count < 2):
0401                         $suffix = array_shift($suffixInfo);
0402                         $this->setSuffix($context, $suffix);
0403                         break;
0404                     case (($count < 3) && (null === $context)):
0405                         $context = array_shift($suffixInfo);
0406                         $suffix  = array_shift($suffixInfo);
0407                         $this->setSuffix($context, $suffix);
0408                         break;
0409                     case (($count == 3) && (null === $context)):
0410                         $context = array_shift($suffixInfo);
0411                         $suffix  = array_shift($suffixInfo);
0412                         $prependViewRendererSuffix = array_shift($suffixInfo);
0413                         $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
0414                         break;
0415                     case ($count >= 2):
0416                         $suffix  = array_shift($suffixInfo);
0417                         $prependViewRendererSuffix = array_shift($suffixInfo);
0418                         $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
0419                         break;
0420                 }
0421             }
0422         }
0423         return $this;
0424     }
0425 
0426     /**
0427      * Customize view script suffix to use when switching context.
0428      *
0429      * Passing an empty suffix value to the setters disables the view script
0430      * suffix change.
0431      *
0432      * @param  string  $context                   Context type for which to set suffix
0433      * @param  string  $suffix                    Suffix to use
0434      * @param  boolean $prependViewRendererSuffix Whether or not to prepend the new suffix to the viewrenderer suffix
0435      * @throws Zend_Controller_Action_Exception
0436      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0437      */
0438     public function setSuffix($context, $suffix, $prependViewRendererSuffix = true)
0439     {
0440         if (!isset($this->_contexts[$context])) {
0441             /**
0442              * @see Zend_Controller_Action_Exception
0443              */
0444             // require_once 'Zend/Controller/Action/Exception.php';
0445             throw new Zend_Controller_Action_Exception(sprintf('Cannot set suffix; invalid context type "%s"', $context));
0446         }
0447 
0448         if (empty($suffix)) {
0449             $suffix = '';
0450         }
0451 
0452         if (is_array($suffix)) {
0453             if (isset($suffix['prependViewRendererSuffix'])) {
0454                 $prependViewRendererSuffix = $suffix['prependViewRendererSuffix'];
0455             }
0456             if (isset($suffix['suffix'])) {
0457                 $suffix = $suffix['suffix'];
0458             } else {
0459                 $suffix = '';
0460             }
0461         }
0462 
0463         $suffix = (string) $suffix;
0464 
0465         if ($prependViewRendererSuffix) {
0466             if (empty($suffix)) {
0467                 $suffix = $this->_getViewRenderer()->getViewSuffix();
0468             } else {
0469                 $suffix .= '.' . $this->_getViewRenderer()->getViewSuffix();
0470             }
0471         }
0472 
0473         $this->_contexts[$context]['suffix'] = $suffix;
0474         return $this;
0475     }
0476 
0477     /**
0478      * Retrieve suffix for given context type
0479      *
0480      * @param  string $type Context type
0481      * @throws Zend_Controller_Action_Exception
0482      * @return string
0483      */
0484     public function getSuffix($type)
0485     {
0486         if (!isset($this->_contexts[$type])) {
0487             /**
0488              * @see Zend_Controller_Action_Exception
0489              */
0490             // require_once 'Zend/Controller/Action/Exception.php';
0491             throw new Zend_Controller_Action_Exception(sprintf('Cannot retrieve suffix; invalid context type "%s"', $type));
0492         }
0493 
0494         return $this->_contexts[$type]['suffix'];
0495     }
0496 
0497     /**
0498      * Does the given context exist?
0499      *
0500      * @param  string  $context
0501      * @param  boolean $throwException
0502      * @throws Zend_Controller_Action_Exception if context does not exist and throwException is true
0503      * @return bool
0504      */
0505     public function hasContext($context, $throwException = false)
0506     {
0507         if (is_string($context)) {
0508             if (isset($this->_contexts[$context])) {
0509                 return true;
0510             }
0511         } elseif (is_array($context)) {
0512             $error = false;
0513             foreach ($context as $test) {
0514                 if (!isset($this->_contexts[$test])) {
0515                     $error = (string) $test;
0516                     break;
0517                 }
0518             }
0519             if (false === $error) {
0520                 return true;
0521             }
0522             $context = $error;
0523         } elseif (true === $context) {
0524             return true;
0525         }
0526 
0527         if ($throwException) {
0528             /**
0529              * @see Zend_Controller_Action_Exception
0530              */
0531             // require_once 'Zend/Controller/Action/Exception.php';
0532             throw new Zend_Controller_Action_Exception(sprintf('Context "%s" does not exist', $context));
0533         }
0534 
0535         return false;
0536     }
0537 
0538     /**
0539      * Add header to context
0540      *
0541      * @param  string $context
0542      * @param  string $header
0543      * @param  string $content
0544      * @throws Zend_Controller_Action_Exception
0545      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0546      */
0547     public function addHeader($context, $header, $content)
0548     {
0549         $context = (string) $context;
0550         $this->hasContext($context, true);
0551 
0552         $header  = (string) $header;
0553         $content = (string) $content;
0554 
0555         if (isset($this->_contexts[$context]['headers'][$header])) {
0556             /**
0557              * @see Zend_Controller_Action_Exception
0558              */
0559             // require_once 'Zend/Controller/Action/Exception.php';
0560             throw new Zend_Controller_Action_Exception(sprintf('Cannot add "%s" header to context "%s": already exists', $header, $context));
0561         }
0562 
0563         $this->_contexts[$context]['headers'][$header] = $content;
0564         return $this;
0565     }
0566 
0567     /**
0568      * Customize response header to use when switching context
0569      *
0570      * Passing an empty header value to the setters disables the response
0571      * header.
0572      *
0573      * @param  string $type   Context type for which to set suffix
0574      * @param  string $header Header to set
0575      * @param  string $content Header content
0576      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0577      */
0578     public function setHeader($context, $header, $content)
0579     {
0580         $this->hasContext($context, true);
0581         $context = (string) $context;
0582         $header  = (string) $header;
0583         $content = (string) $content;
0584 
0585         $this->_contexts[$context]['headers'][$header] = $content;
0586         return $this;
0587     }
0588 
0589     /**
0590      * Add multiple headers at once for a given context
0591      *
0592      * @param  string $context
0593      * @param  array  $headers
0594      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0595      */
0596     public function addHeaders($context, array $headers)
0597     {
0598         foreach ($headers as $header => $content) {
0599             $this->addHeader($context, $header, $content);
0600         }
0601 
0602         return $this;
0603     }
0604 
0605     /**
0606      * Set headers from context => headers pairs
0607      *
0608      * @param  array $options
0609      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0610      */
0611     protected function _setHeaders(array $options)
0612     {
0613         foreach ($options as $context => $headers) {
0614             if (!is_array($headers)) {
0615                 continue;
0616             }
0617             $this->setHeaders($context, $headers);
0618         }
0619 
0620         return $this;
0621     }
0622 
0623     /**
0624      * Set multiple headers at once for a given context
0625      *
0626      * @param  string $context
0627      * @param  array  $headers
0628      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0629      */
0630     public function setHeaders($context, array $headers)
0631     {
0632         $this->clearHeaders($context);
0633         foreach ($headers as $header => $content) {
0634             $this->setHeader($context, $header, $content);
0635         }
0636 
0637         return $this;
0638     }
0639 
0640     /**
0641      * Retrieve context header
0642      *
0643      * Returns the value of a given header for a given context type
0644      *
0645      * @param  string $context
0646      * @param  string $header
0647      * @return string|null
0648      */
0649     public function getHeader($context, $header)
0650     {
0651         $this->hasContext($context, true);
0652         $context = (string) $context;
0653         $header  = (string) $header;
0654         if (isset($this->_contexts[$context]['headers'][$header])) {
0655             return $this->_contexts[$context]['headers'][$header];
0656         }
0657 
0658         return null;
0659     }
0660 
0661     /**
0662      * Retrieve context headers
0663      *
0664      * Returns all headers for a context as key/value pairs
0665      *
0666      * @param  string $context
0667      * @return array
0668      */
0669     public function getHeaders($context)
0670     {
0671         $this->hasContext($context, true);
0672         $context = (string) $context;
0673         return $this->_contexts[$context]['headers'];
0674     }
0675 
0676     /**
0677      * Remove a single header from a context
0678      *
0679      * @param  string $context
0680      * @param  string $header
0681      * @return boolean
0682      */
0683     public function removeHeader($context, $header)
0684     {
0685         $this->hasContext($context, true);
0686         $context = (string) $context;
0687         $header  = (string) $header;
0688         if (isset($this->_contexts[$context]['headers'][$header])) {
0689             unset($this->_contexts[$context]['headers'][$header]);
0690             return true;
0691         }
0692 
0693         return false;
0694     }
0695 
0696     /**
0697      * Clear all headers for a given context
0698      *
0699      * @param  string $context
0700      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0701      */
0702     public function clearHeaders($context)
0703     {
0704         $this->hasContext($context, true);
0705         $context = (string) $context;
0706         $this->_contexts[$context]['headers'] = array();
0707         return $this;
0708     }
0709 
0710     /**
0711      * Validate trigger and return in normalized form
0712      *
0713      * @param  string $trigger
0714      * @throws Zend_Controller_Action_Exception
0715      * @return string
0716      */
0717     protected function _validateTrigger($trigger)
0718     {
0719         $trigger = strtoupper($trigger);
0720         if ('TRIGGER_' !== substr($trigger, 0, 8)) {
0721             $trigger = 'TRIGGER_' . $trigger;
0722         }
0723 
0724         if (!in_array($trigger, array(self::TRIGGER_INIT, self::TRIGGER_POST))) {
0725             /**
0726              * @see Zend_Controller_Action_Exception
0727              */
0728             // require_once 'Zend/Controller/Action/Exception.php';
0729             throw new Zend_Controller_Action_Exception(sprintf('Invalid trigger "%s"', $trigger));
0730         }
0731 
0732         return $trigger;
0733     }
0734 
0735     /**
0736      * Set a callback for a given context and trigger
0737      *
0738      * @param  string       $context
0739      * @param  string       $trigger
0740      * @param  string|array $callback
0741      * @throws Zend_Controller_Action_Exception
0742      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0743      */
0744     public function setCallback($context, $trigger, $callback)
0745     {
0746         $this->hasContext($context, true);
0747         $trigger = $this->_validateTrigger($trigger);
0748 
0749         if (!is_string($callback)) {
0750             if (!is_array($callback) || (2 != count($callback))) {
0751                 /**
0752                  * @see Zend_Controller_Action_Exception
0753                  */
0754                 // require_once 'Zend/Controller/Action/Exception.php';
0755                 throw new Zend_Controller_Action_Exception('Invalid callback specified');
0756             }
0757         }
0758 
0759         $this->_contexts[$context]['callbacks'][$trigger] = $callback;
0760         return $this;
0761     }
0762 
0763     /**
0764      * Set callbacks from array of context => callbacks pairs
0765      *
0766      * @param  array $options
0767      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0768      */
0769     protected function _setCallbacks(array $options)
0770     {
0771         foreach ($options as $context => $callbacks) {
0772             if (!is_array($callbacks)) {
0773                 continue;
0774             }
0775 
0776             $this->setCallbacks($context, $callbacks);
0777         }
0778         return $this;
0779     }
0780 
0781     /**
0782      * Set callbacks for a given context
0783      *
0784      * Callbacks should be in trigger/callback pairs.
0785      *
0786      * @param  string $context
0787      * @param  array  $callbacks
0788      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0789      */
0790     public function setCallbacks($context, array $callbacks)
0791     {
0792         $this->hasContext($context, true);
0793         $context = (string) $context;
0794         if (!isset($this->_contexts[$context]['callbacks'])) {
0795             $this->_contexts[$context]['callbacks'] = array();
0796         }
0797 
0798         foreach ($callbacks as $trigger => $callback) {
0799             $this->setCallback($context, $trigger, $callback);
0800         }
0801         return $this;
0802     }
0803 
0804     /**
0805      * Get a single callback for a given context and trigger
0806      *
0807      * @param  string $context
0808      * @param  string $trigger
0809      * @return string|array|null
0810      */
0811     public function getCallback($context, $trigger)
0812     {
0813         $this->hasContext($context, true);
0814         $trigger = $this->_validateTrigger($trigger);
0815         if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
0816             return $this->_contexts[$context]['callbacks'][$trigger];
0817         }
0818 
0819         return null;
0820     }
0821 
0822     /**
0823      * Get all callbacks for a given context
0824      *
0825      * @param  string $context
0826      * @return array
0827      */
0828     public function getCallbacks($context)
0829     {
0830         $this->hasContext($context, true);
0831         return $this->_contexts[$context]['callbacks'];
0832     }
0833 
0834     /**
0835      * Clear a callback for a given context and trigger
0836      *
0837      * @param  string $context
0838      * @param  string $trigger
0839      * @return boolean
0840      */
0841     public function removeCallback($context, $trigger)
0842     {
0843         $this->hasContext($context, true);
0844         $trigger = $this->_validateTrigger($trigger);
0845         if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
0846             unset($this->_contexts[$context]['callbacks'][$trigger]);
0847             return true;
0848         }
0849 
0850         return false;
0851     }
0852 
0853     /**
0854      * Clear all callbacks for a given context
0855      *
0856      * @param  string $context
0857      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0858      */
0859     public function clearCallbacks($context)
0860     {
0861         $this->hasContext($context, true);
0862         $this->_contexts[$context]['callbacks'] = array();
0863         return $this;
0864     }
0865 
0866     /**
0867      * Set name of parameter to use when determining context format
0868      *
0869      * @param  string $name
0870      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0871      */
0872     public function setContextParam($name)
0873     {
0874         $this->_contextParam = (string) $name;
0875         return $this;
0876     }
0877 
0878     /**
0879      * Return context format request parameter name
0880      *
0881      * @return string
0882      */
0883     public function getContextParam()
0884     {
0885         return $this->_contextParam;
0886     }
0887 
0888     /**
0889      * Indicate default context to use when no context format provided
0890      *
0891      * @param  string $type
0892      * @throws Zend_Controller_Action_Exception
0893      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0894      */
0895     public function setDefaultContext($type)
0896     {
0897         if (!isset($this->_contexts[$type])) {
0898             /**
0899              * @see Zend_Controller_Action_Exception
0900              */
0901             // require_once 'Zend/Controller/Action/Exception.php';
0902             throw new Zend_Controller_Action_Exception(sprintf('Cannot set default context; invalid context type "%s"', $type));
0903         }
0904 
0905         $this->_defaultContext = $type;
0906         return $this;
0907     }
0908 
0909     /**
0910      * Return default context
0911      *
0912      * @return string
0913      */
0914     public function getDefaultContext()
0915     {
0916         return $this->_defaultContext;
0917     }
0918 
0919     /**
0920      * Set flag indicating if layout should be disabled
0921      *
0922      * @param  boolean $flag
0923      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0924      */
0925     public function setAutoDisableLayout($flag)
0926     {
0927         $this->_disableLayout = ($flag) ? true : false;
0928         return $this;
0929     }
0930 
0931     /**
0932      * Retrieve auto layout disable flag
0933      *
0934      * @return boolean
0935      */
0936     public function getAutoDisableLayout()
0937     {
0938         return $this->_disableLayout;
0939     }
0940 
0941     /**
0942      * Add new context
0943      *
0944      * @param  string $context Context type
0945      * @param  array  $spec    Context specification
0946      * @throws Zend_Controller_Action_Exception
0947      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0948      */
0949     public function addContext($context, array $spec)
0950     {
0951         if ($this->hasContext($context)) {
0952             /**
0953              * @see Zend_Controller_Action_Exception
0954              */
0955             // require_once 'Zend/Controller/Action/Exception.php';
0956             throw new Zend_Controller_Action_Exception(sprintf('Cannot add context "%s"; already exists', $context));
0957         }
0958         $context = (string) $context;
0959 
0960         $this->_contexts[$context] = array();
0961 
0962         $this->setSuffix($context,    (isset($spec['suffix'])    ? $spec['suffix']    : ''))
0963              ->setHeaders($context,   (isset($spec['headers'])   ? $spec['headers']   : array()))
0964              ->setCallbacks($context, (isset($spec['callbacks']) ? $spec['callbacks'] : array()));
0965         return $this;
0966     }
0967 
0968     /**
0969      * Overwrite existing context
0970      *
0971      * @param  string $context Context type
0972      * @param  array  $spec    Context specification
0973      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0974      */
0975     public function setContext($context, array $spec)
0976     {
0977         $this->removeContext($context);
0978         return $this->addContext($context, $spec);
0979     }
0980 
0981     /**
0982      * Add multiple contexts
0983      *
0984      * @param  array $contexts
0985      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
0986      */
0987     public function addContexts(array $contexts)
0988     {
0989         foreach ($contexts as $context => $spec) {
0990             $this->addContext($context, $spec);
0991         }
0992         return $this;
0993     }
0994 
0995     /**
0996      * Set multiple contexts, after first removing all
0997      *
0998      * @param  array $contexts
0999      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1000      */
1001     public function setContexts(array $contexts)
1002     {
1003         $this->clearContexts();
1004         foreach ($contexts as $context => $spec) {
1005             $this->addContext($context, $spec);
1006         }
1007         return $this;
1008     }
1009 
1010     /**
1011      * Retrieve context specification
1012      *
1013      * @param  string $context
1014      * @return array|null
1015      */
1016     public function getContext($context)
1017     {
1018         if ($this->hasContext($context)) {
1019             return $this->_contexts[(string) $context];
1020         }
1021         return null;
1022     }
1023 
1024     /**
1025      * Retrieve context definitions
1026      *
1027      * @return array
1028      */
1029     public function getContexts()
1030     {
1031         return $this->_contexts;
1032     }
1033 
1034     /**
1035      * Remove a context
1036      *
1037      * @param  string $context
1038      * @return boolean
1039      */
1040     public function removeContext($context)
1041     {
1042         if ($this->hasContext($context)) {
1043             unset($this->_contexts[(string) $context]);
1044             return true;
1045         }
1046         return false;
1047     }
1048 
1049     /**
1050      * Remove all contexts
1051      *
1052      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1053      */
1054     public function clearContexts()
1055     {
1056         $this->_contexts = array();
1057         return $this;
1058     }
1059 
1060     /**
1061      * Return current context, if any
1062      *
1063      * @return null|string
1064      */
1065     public function getCurrentContext()
1066     {
1067         return $this->_currentContext;
1068     }
1069 
1070     /**
1071      * Post dispatch processing
1072      *
1073      * Execute postDispatch callback for current context, if available
1074      *
1075      * @throws Zend_Controller_Action_Exception
1076      * @return void
1077      */
1078     public function postDispatch()
1079     {
1080         $context = $this->getCurrentContext();
1081         if (null !== $context) {
1082             if (null !== ($callback = $this->getCallback($context, self::TRIGGER_POST))) {
1083                 if (is_string($callback) && method_exists($this, $callback)) {
1084                     $this->$callback();
1085                 } elseif (is_string($callback) && function_exists($callback)) {
1086                     $callback();
1087                 } elseif (is_array($callback)) {
1088                     call_user_func($callback);
1089                 } else {
1090                     /**
1091                      * @see Zend_Controller_Action_Exception
1092                      */
1093                     // require_once 'Zend/Controller/Action/Exception.php';
1094                     throw new Zend_Controller_Action_Exception(sprintf('Invalid postDispatch context callback registered for context "%s"', $context));
1095                 }
1096             }
1097         }
1098     }
1099 
1100     /**
1101      * JSON post processing
1102      *
1103      * JSON serialize view variables to response body
1104      *
1105      * @return void
1106      */
1107     public function postJsonContext()
1108     {
1109         if (!$this->getAutoJsonSerialization()) {
1110             return;
1111         }
1112 
1113         $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
1114         $view = $viewRenderer->view;
1115         if ($view instanceof Zend_View_Interface) {
1116             /**
1117              * @see Zend_Json
1118              */
1119             if(method_exists($view, 'getVars')) {
1120                 // require_once 'Zend/Json.php';
1121                 $vars = Zend_Json::encode($view->getVars());
1122                 $this->getResponse()->setBody($vars);
1123             } else {
1124                 // require_once 'Zend/Controller/Action/Exception.php';
1125                 throw new Zend_Controller_Action_Exception('View does not implement the getVars() method needed to encode the view into JSON');
1126             }
1127         }
1128     }
1129 
1130     /**
1131      * Add one or more contexts to an action
1132      *
1133      * @param  string       $action
1134      * @param  string|array $context
1135      * @return Zend_Controller_Action_Helper_ContextSwitch|void Provides a fluent interface
1136      */
1137     public function addActionContext($action, $context)
1138     {
1139         $this->hasContext($context, true);
1140         $controller = $this->getActionController();
1141         if (null === $controller) {
1142             return;
1143         }
1144         $action     = (string) $action;
1145         $contextKey = $this->_contextKey;
1146 
1147         if (!isset($controller->$contextKey)) {
1148             $controller->$contextKey = array();
1149         }
1150 
1151         if (true === $context) {
1152             $contexts = $this->getContexts();
1153             $controller->{$contextKey}[$action] = array_keys($contexts);
1154             return $this;
1155         }
1156 
1157         $context = (array) $context;
1158         if (!isset($controller->{$contextKey}[$action])) {
1159             $controller->{$contextKey}[$action] = $context;
1160         } else {
1161             $controller->{$contextKey}[$action] = array_merge(
1162                 $controller->{$contextKey}[$action],
1163                 $context
1164             );
1165         }
1166 
1167         return $this;
1168     }
1169 
1170     /**
1171      * Set a context as available for a given controller action
1172      *
1173      * @param  string       $action
1174      * @param  string|array $context
1175      * @return Zend_Controller_Action_Helper_ContextSwitch|void Provides a fluent interface
1176      */
1177     public function setActionContext($action, $context)
1178     {
1179         $this->hasContext($context, true);
1180         $controller = $this->getActionController();
1181         if (null === $controller) {
1182             return;
1183         }
1184         $action     = (string) $action;
1185         $contextKey = $this->_contextKey;
1186 
1187         if (!isset($controller->$contextKey)) {
1188             $controller->$contextKey = array();
1189         }
1190 
1191         if (true === $context) {
1192             $contexts = $this->getContexts();
1193             $controller->{$contextKey}[$action] = array_keys($contexts);
1194         } else {
1195             $controller->{$contextKey}[$action] = (array) $context;
1196         }
1197 
1198         return $this;
1199     }
1200 
1201     /**
1202      * Add multiple action/context pairs at once
1203      *
1204      * @param  array $contexts
1205      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1206      */
1207     public function addActionContexts(array $contexts)
1208     {
1209         foreach ($contexts as $action => $context) {
1210             $this->addActionContext($action, $context);
1211         }
1212         return $this;
1213     }
1214 
1215     /**
1216      * Overwrite and set multiple action contexts at once
1217      *
1218      * @param  array $contexts
1219      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1220      */
1221     public function setActionContexts(array $contexts)
1222     {
1223         foreach ($contexts as $action => $context) {
1224             $this->setActionContext($action, $context);
1225         }
1226         return $this;
1227     }
1228 
1229     /**
1230      * Does a particular controller action have the given context(s)?
1231      *
1232      * @param  string       $action
1233      * @param  string|array $context
1234      * @throws Zend_Controller_Action_Exception
1235      * @return boolean
1236      */
1237     public function hasActionContext($action, $context)
1238     {
1239         $this->hasContext($context, true);
1240         $controller = $this->getActionController();
1241         if (null === $controller) {
1242             return false;
1243         }
1244         $action     = (string) $action;
1245         $contextKey = $this->_contextKey;
1246 
1247         if (!isset($controller->{$contextKey})) {
1248             return false;
1249         }
1250 
1251         $allContexts = $controller->{$contextKey};
1252 
1253         if (!is_array($allContexts)) {
1254             /**
1255              * @see Zend_Controller_Action_Exception
1256              */
1257             // require_once 'Zend/Controller/Action/Exception.php';
1258             throw new Zend_Controller_Action_Exception("Invalid contexts found for controller");
1259         }
1260 
1261         if (!isset($allContexts[$action])) {
1262             return false;
1263         }
1264 
1265         if (true === $allContexts[$action]) {
1266             return true;
1267         }
1268 
1269         $contexts = $allContexts[$action];
1270 
1271         if (!is_array($contexts)) {
1272             /**
1273              * @see Zend_Controller_Action_Exception
1274              */
1275             // require_once 'Zend/Controller/Action/Exception.php';
1276             throw new Zend_Controller_Action_Exception(sprintf("Invalid contexts found for action '%s'", $action));
1277         }
1278 
1279         if (is_string($context) && in_array($context, $contexts)) {
1280             return true;
1281         } elseif (is_array($context)) {
1282             $found = true;
1283             foreach ($context as $test) {
1284                 if (!in_array($test, $contexts)) {
1285                     $found = false;
1286                     break;
1287                 }
1288             }
1289             return $found;
1290         }
1291 
1292         return false;
1293     }
1294 
1295     /**
1296      * Get contexts for a given action or all actions in the controller
1297      *
1298      * @param  string $action
1299      * @return array
1300      */
1301     public function getActionContexts($action = null)
1302     {
1303         $controller = $this->getActionController();
1304         if (null === $controller) {
1305             return array();
1306         }
1307         $contextKey = $this->_contextKey;
1308 
1309         if (!isset($controller->$contextKey)) {
1310             return array();
1311         }
1312 
1313         if (null !== $action) {
1314             $action = (string) $action;
1315             if (isset($controller->{$contextKey}[$action])) {
1316                 return $controller->{$contextKey}[$action];
1317             } else {
1318                 return array();
1319             }
1320         }
1321 
1322         return $controller->$contextKey;
1323     }
1324 
1325     /**
1326      * Remove one or more contexts for a given controller action
1327      *
1328      * @param  string       $action
1329      * @param  string|array $context
1330      * @return boolean
1331      */
1332     public function removeActionContext($action, $context)
1333     {
1334         if ($this->hasActionContext($action, $context)) {
1335             $controller     = $this->getActionController();
1336             $contextKey     = $this->_contextKey;
1337             $action         = (string) $action;
1338             $contexts       = $controller->$contextKey;
1339             $actionContexts = $contexts[$action];
1340             $contexts       = (array) $context;
1341             foreach ($contexts as $context) {
1342                 $index = array_search($context, $actionContexts);
1343                 if (false !== $index) {
1344                     unset($controller->{$contextKey}[$action][$index]);
1345                 }
1346             }
1347             return true;
1348         }
1349         return false;
1350     }
1351 
1352     /**
1353      * Clear all contexts for a given controller action or all actions
1354      *
1355      * @param  string $action
1356      * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1357      */
1358     public function clearActionContexts($action = null)
1359     {
1360         $controller = $this->getActionController();
1361         $contextKey = $this->_contextKey;
1362 
1363         if (!isset($controller->$contextKey) || empty($controller->$contextKey)) {
1364             return $this;
1365         }
1366 
1367         if (null === $action) {
1368             $controller->$contextKey = array();
1369             return $this;
1370         }
1371 
1372         $action = (string) $action;
1373         if (isset($controller->{$contextKey}[$action])) {
1374             unset($controller->{$contextKey}[$action]);
1375         }
1376 
1377         return $this;
1378     }
1379 
1380     /**
1381      * Retrieve ViewRenderer
1382      *
1383      * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
1384      */
1385     protected function _getViewRenderer()
1386     {
1387         if (null === $this->_viewRenderer) {
1388             $this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
1389         }
1390 
1391         return $this->_viewRenderer;
1392     }
1393 }
1394