PageRenderTime 49ms CodeModel.GetById 18ms app.highlight 23ms RepoModel.GetById 0ms app.codeStats 1ms

/library/Zend/Controller/Action/Helper/ContextSwitch.php

https://bitbucket.org/hamidrezas/melobit
PHP | 1394 lines | 739 code | 147 blank | 508 comment | 113 complexity | 3cc0438edf9c8b6b4b8cc1c761032e93 MD5 | raw file
Possible License(s): AGPL-1.0
   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 * @subpackage Zend_Controller_Action_Helper
  18 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
  20 * @version    $Id: ContextSwitch.php 24594 2012-01-05 21:27:01Z matthew $
  21 */
  22
  23/**
  24 * @see Zend_Controller_Action_Helper_Abstract
  25 */
  26require_once 'Zend/Controller/Action/Helper/Abstract.php';
  27
  28/**
  29 * Simplify context switching based on requested format
  30 *
  31 * @uses       Zend_Controller_Action_Helper_Abstract
  32 * @category   Zend
  33 * @package    Zend_Controller
  34 * @subpackage Zend_Controller_Action_Helper
  35 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  36 * @license    http://framework.zend.com/license/new-bsd     New BSD License
  37 */
  38class Zend_Controller_Action_Helper_ContextSwitch extends Zend_Controller_Action_Helper_Abstract
  39{
  40    /**
  41     * Trigger type constants
  42     */
  43    const TRIGGER_INIT = 'TRIGGER_INIT';
  44    const TRIGGER_POST = 'TRIGGER_POST';
  45
  46    /**
  47     * Supported contexts
  48     * @var array
  49     */
  50    protected $_contexts = array();
  51
  52    /**
  53     * JSON auto-serialization flag
  54     * @var boolean
  55     */
  56    protected $_autoJsonSerialization = true;
  57
  58    /**
  59     * Controller property key to utilize for context switching
  60     * @var string
  61     */
  62    protected $_contextKey = 'contexts';
  63
  64    /**
  65     * Request parameter containing requested context
  66     * @var string
  67     */
  68    protected $_contextParam = 'format';
  69
  70    /**
  71     * Current context
  72     * @var string
  73     */
  74    protected $_currentContext;
  75
  76    /**
  77     * Default context (xml)
  78     * @var string
  79     */
  80    protected $_defaultContext = 'xml';
  81
  82    /**
  83     * Whether or not to disable layouts when switching contexts
  84     * @var boolean
  85     */
  86    protected $_disableLayout = true;
  87
  88    /**
  89     * Methods that require special configuration
  90     * @var array
  91     */
  92    protected $_specialConfig = array(
  93        'setSuffix',
  94        'setHeaders',
  95        'setCallbacks',
  96    );
  97
  98    /**
  99     * Methods that are not configurable via setOptions and setConfig
 100     * @var array
 101     */
 102    protected $_unconfigurable = array(
 103        'setOptions',
 104        'setConfig',
 105        'setHeader',
 106        'setCallback',
 107        'setContext',
 108        'setActionContext',
 109        'setActionContexts',
 110    );
 111
 112    /**
 113     * @var Zend_Controller_Action_Helper_ViewRenderer
 114     */
 115    protected $_viewRenderer;
 116
 117    /**
 118     * Original view suffix prior to detecting context switch
 119     * @var string
 120     */
 121    protected $_viewSuffixOrig;
 122
 123    /**
 124     * Constructor
 125     *
 126     * @param  array|Zend_Config $options
 127     * @return void
 128     */
 129    public function __construct($options = null)
 130    {
 131        if ($options instanceof Zend_Config) {
 132            $this->setConfig($options);
 133        } elseif (is_array($options)) {
 134            $this->setOptions($options);
 135        }
 136
 137        if (empty($this->_contexts)) {
 138            $this->addContexts(array(
 139                'json' => array(
 140                    'suffix'    => 'json',
 141                    'headers'   => array('Content-Type' => 'application/json'),
 142                    'callbacks' => array(
 143                        'init' => 'initJsonContext',
 144                        'post' => 'postJsonContext'
 145                    )
 146                ),
 147                'xml'  => array(
 148                    'suffix'    => 'xml',
 149                    'headers'   => array('Content-Type' => 'application/xml'),
 150                )
 151            ));
 152        }
 153
 154        $this->init();
 155    }
 156
 157    /**
 158     * Initialize at start of action controller
 159     *
 160     * Reset the view script suffix to the original state, or store the
 161     * original state.
 162     *
 163     * @return void
 164     */
 165    public function init()
 166    {
 167        if (null === $this->_viewSuffixOrig) {
 168            $this->_viewSuffixOrig = $this->_getViewRenderer()->getViewSuffix();
 169        } else {
 170            $this->_getViewRenderer()->setViewSuffix($this->_viewSuffixOrig);
 171        }
 172    }
 173
 174    /**
 175     * Configure object from array of options
 176     *
 177     * @param  array $options
 178     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 179     */
 180    public function setOptions(array $options)
 181    {
 182        if (isset($options['contexts'])) {
 183            $this->setContexts($options['contexts']);
 184            unset($options['contexts']);
 185        }
 186
 187        foreach ($options as $key => $value) {
 188            $method = 'set' . ucfirst($key);
 189            if (in_array($method, $this->_unconfigurable)) {
 190                continue;
 191            }
 192
 193            if (in_array($method, $this->_specialConfig)) {
 194                $method = '_' . $method;
 195            }
 196
 197            if (method_exists($this, $method)) {
 198                $this->$method($value);
 199            }
 200        }
 201        return $this;
 202    }
 203
 204    /**
 205     * Set object state from config object
 206     *
 207     * @param  Zend_Config $config
 208     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 209     */
 210    public function setConfig(Zend_Config $config)
 211    {
 212        return $this->setOptions($config->toArray());
 213    }
 214
 215    /**
 216     * Strategy pattern: return object
 217     *
 218     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 219     */
 220    public function direct()
 221    {
 222        return $this;
 223    }
 224
 225    /**
 226     * Initialize context detection and switching
 227     *
 228     * @param  mixed $format
 229     * @throws Zend_Controller_Action_Exception
 230     * @return void
 231     */
 232    public function initContext($format = null)
 233    {
 234        $this->_currentContext = null;
 235
 236        $controller = $this->getActionController();
 237        $request    = $this->getRequest();
 238        $action     = $request->getActionName();
 239
 240        // Return if no context switching enabled, or no context switching
 241        // enabled for this action
 242        $contexts = $this->getActionContexts($action);
 243        if (empty($contexts)) {
 244            return;
 245        }
 246
 247        // Return if no context parameter provided
 248        if (!$context = $request->getParam($this->getContextParam())) {
 249            if ($format === null) {
 250                return;
 251            }
 252            $context = $format;
 253            $format  = null;
 254        }
 255
 256        // Check if context allowed by action controller
 257        if (!$this->hasActionContext($action, $context)) {
 258            return;
 259        }
 260
 261        // Return if invalid context parameter provided and no format or invalid
 262        // format provided
 263        if (!$this->hasContext($context)) {
 264            if (empty($format) || !$this->hasContext($format)) {
 265
 266                return;
 267            }
 268        }
 269
 270        // Use provided format if passed
 271        if (!empty($format) && $this->hasContext($format)) {
 272            $context = $format;
 273        }
 274
 275        $suffix = $this->getSuffix($context);
 276
 277        $this->_getViewRenderer()->setViewSuffix($suffix);
 278
 279        $headers = $this->getHeaders($context);
 280        if (!empty($headers)) {
 281            $response = $this->getResponse();
 282            foreach ($headers as $header => $content) {
 283                $response->setHeader($header, $content);
 284            }
 285        }
 286
 287        if ($this->getAutoDisableLayout()) {
 288            /**
 289             * @see Zend_Layout
 290             */
 291            require_once 'Zend/Layout.php';
 292            $layout = Zend_Layout::getMvcInstance();
 293            if (null !== $layout) {
 294                $layout->disableLayout();
 295            }
 296        }
 297
 298        if (null !== ($callback = $this->getCallback($context, self::TRIGGER_INIT))) {
 299            if (is_string($callback) && method_exists($this, $callback)) {
 300                $this->$callback();
 301            } elseif (is_string($callback) && function_exists($callback)) {
 302                $callback();
 303            } elseif (is_array($callback)) {
 304                call_user_func($callback);
 305            } else {
 306                /**
 307                 * @see Zend_Controller_Action_Exception
 308                 */
 309                require_once 'Zend/Controller/Action/Exception.php';
 310                throw new Zend_Controller_Action_Exception(sprintf('Invalid context callback registered for context "%s"', $context));
 311            }
 312        }
 313
 314        $this->_currentContext = $context;
 315    }
 316
 317    /**
 318     * JSON context extra initialization
 319     *
 320     * Turns off viewRenderer auto-rendering
 321     *
 322     * @return void
 323     */
 324    public function initJsonContext()
 325    {
 326        if (!$this->getAutoJsonSerialization()) {
 327            return;
 328        }
 329
 330        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
 331        $view = $viewRenderer->view;
 332        if ($view instanceof Zend_View_Interface) {
 333            $viewRenderer->setNoRender(true);
 334        }
 335    }
 336
 337    /**
 338     * Should JSON contexts auto-serialize?
 339     *
 340     * @param  boolean $flag
 341     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 342     */
 343    public function setAutoJsonSerialization($flag)
 344    {
 345        $this->_autoJsonSerialization = (bool) $flag;
 346        return $this;
 347    }
 348
 349    /**
 350     * Get JSON context auto-serialization flag
 351     *
 352     * @return boolean
 353     */
 354    public function getAutoJsonSerialization()
 355    {
 356        return $this->_autoJsonSerialization;
 357    }
 358
 359    /**
 360     * Set suffix from array
 361     *
 362     * @param  array $spec
 363     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 364     */
 365    protected function _setSuffix(array $spec)
 366    {
 367        foreach ($spec as $context => $suffixInfo) {
 368            if (!is_string($context)) {
 369                $context = null;
 370            }
 371
 372            if (is_string($suffixInfo)) {
 373                $this->setSuffix($context, $suffixInfo);
 374                continue;
 375            } elseif (is_array($suffixInfo)) {
 376                if (isset($suffixInfo['suffix'])) {
 377                    $suffix                    = $suffixInfo['suffix'];
 378                    $prependViewRendererSuffix = true;
 379
 380                    if ((null === $context) && isset($suffixInfo['context'])) {
 381                        $context = $suffixInfo['context'];
 382                    }
 383
 384                    if (isset($suffixInfo['prependViewRendererSuffix'])) {
 385                        $prependViewRendererSuffix = $suffixInfo['prependViewRendererSuffix'];
 386                    }
 387
 388                    $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
 389                    continue;
 390                }
 391
 392                $count = count($suffixInfo);
 393                switch (true) {
 394                    case (($count < 2) && (null === $context)):
 395                        /**
 396                         * @see Zend_Controller_Action_Exception
 397                         */
 398                        require_once 'Zend/Controller/Action/Exception.php';
 399                        throw new Zend_Controller_Action_Exception('Invalid suffix information provided in config');
 400                    case ($count < 2):
 401                        $suffix = array_shift($suffixInfo);
 402                        $this->setSuffix($context, $suffix);
 403                        break;
 404                    case (($count < 3) && (null === $context)):
 405                        $context = array_shift($suffixInfo);
 406                        $suffix  = array_shift($suffixInfo);
 407                        $this->setSuffix($context, $suffix);
 408                        break;
 409                    case (($count == 3) && (null === $context)):
 410                        $context = array_shift($suffixInfo);
 411                        $suffix  = array_shift($suffixInfo);
 412                        $prependViewRendererSuffix = array_shift($suffixInfo);
 413                        $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
 414                        break;
 415                    case ($count >= 2):
 416                        $suffix  = array_shift($suffixInfo);
 417                        $prependViewRendererSuffix = array_shift($suffixInfo);
 418                        $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
 419                        break;
 420                }
 421            }
 422        }
 423        return $this;
 424    }
 425
 426    /**
 427     * Customize view script suffix to use when switching context.
 428     *
 429     * Passing an empty suffix value to the setters disables the view script
 430     * suffix change.
 431     *
 432     * @param  string  $context                   Context type for which to set suffix
 433     * @param  string  $suffix                    Suffix to use
 434     * @param  boolean $prependViewRendererSuffix Whether or not to prepend the new suffix to the viewrenderer suffix
 435     * @throws Zend_Controller_Action_Exception
 436     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 437     */
 438    public function setSuffix($context, $suffix, $prependViewRendererSuffix = true)
 439    {
 440        if (!isset($this->_contexts[$context])) {
 441            /**
 442             * @see Zend_Controller_Action_Exception
 443             */
 444            require_once 'Zend/Controller/Action/Exception.php';
 445            throw new Zend_Controller_Action_Exception(sprintf('Cannot set suffix; invalid context type "%s"', $context));
 446        }
 447
 448        if (empty($suffix)) {
 449            $suffix = '';
 450        }
 451
 452        if (is_array($suffix)) {
 453            if (isset($suffix['prependViewRendererSuffix'])) {
 454                $prependViewRendererSuffix = $suffix['prependViewRendererSuffix'];
 455            }
 456            if (isset($suffix['suffix'])) {
 457                $suffix = $suffix['suffix'];
 458            } else {
 459                $suffix = '';
 460            }
 461        }
 462
 463        $suffix = (string) $suffix;
 464
 465        if ($prependViewRendererSuffix) {
 466            if (empty($suffix)) {
 467                $suffix = $this->_getViewRenderer()->getViewSuffix();
 468            } else {
 469                $suffix .= '.' . $this->_getViewRenderer()->getViewSuffix();
 470            }
 471        }
 472
 473        $this->_contexts[$context]['suffix'] = $suffix;
 474        return $this;
 475    }
 476
 477    /**
 478     * Retrieve suffix for given context type
 479     *
 480     * @param  string $type Context type
 481     * @throws Zend_Controller_Action_Exception
 482     * @return string
 483     */
 484    public function getSuffix($type)
 485    {
 486        if (!isset($this->_contexts[$type])) {
 487            /**
 488             * @see Zend_Controller_Action_Exception
 489             */
 490            require_once 'Zend/Controller/Action/Exception.php';
 491            throw new Zend_Controller_Action_Exception(sprintf('Cannot retrieve suffix; invalid context type "%s"', $type));
 492        }
 493
 494        return $this->_contexts[$type]['suffix'];
 495    }
 496
 497    /**
 498     * Does the given context exist?
 499     *
 500     * @param  string  $context
 501     * @param  boolean $throwException
 502     * @throws Zend_Controller_Action_Exception if context does not exist and throwException is true
 503     * @return bool
 504     */
 505    public function hasContext($context, $throwException = false)
 506    {
 507        if (is_string($context)) {
 508            if (isset($this->_contexts[$context])) {
 509                return true;
 510            }
 511        } elseif (is_array($context)) {
 512            $error = false;
 513            foreach ($context as $test) {
 514                if (!isset($this->_contexts[$test])) {
 515                    $error = (string) $test;
 516                    break;
 517                }
 518            }
 519            if (false === $error) {
 520                return true;
 521            }
 522            $context = $error;
 523        } elseif (true === $context) {
 524            return true;
 525        }
 526
 527        if ($throwException) {
 528            /**
 529             * @see Zend_Controller_Action_Exception
 530             */
 531            require_once 'Zend/Controller/Action/Exception.php';
 532            throw new Zend_Controller_Action_Exception(sprintf('Context "%s" does not exist', $context));
 533        }
 534
 535        return false;
 536    }
 537
 538    /**
 539     * Add header to context
 540     *
 541     * @param  string $context
 542     * @param  string $header
 543     * @param  string $content
 544     * @throws Zend_Controller_Action_Exception
 545     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 546     */
 547    public function addHeader($context, $header, $content)
 548    {
 549        $context = (string) $context;
 550        $this->hasContext($context, true);
 551
 552        $header  = (string) $header;
 553        $content = (string) $content;
 554
 555        if (isset($this->_contexts[$context]['headers'][$header])) {
 556            /**
 557             * @see Zend_Controller_Action_Exception
 558             */
 559            require_once 'Zend/Controller/Action/Exception.php';
 560            throw new Zend_Controller_Action_Exception(sprintf('Cannot add "%s" header to context "%s": already exists', $header, $context));
 561        }
 562
 563        $this->_contexts[$context]['headers'][$header] = $content;
 564        return $this;
 565    }
 566
 567    /**
 568     * Customize response header to use when switching context
 569     *
 570     * Passing an empty header value to the setters disables the response
 571     * header.
 572     *
 573     * @param  string $type   Context type for which to set suffix
 574     * @param  string $header Header to set
 575     * @param  string $content Header content
 576     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 577     */
 578    public function setHeader($context, $header, $content)
 579    {
 580        $this->hasContext($context, true);
 581        $context = (string) $context;
 582        $header  = (string) $header;
 583        $content = (string) $content;
 584
 585        $this->_contexts[$context]['headers'][$header] = $content;
 586        return $this;
 587    }
 588
 589    /**
 590     * Add multiple headers at once for a given context
 591     *
 592     * @param  string $context
 593     * @param  array  $headers
 594     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 595     */
 596    public function addHeaders($context, array $headers)
 597    {
 598        foreach ($headers as $header => $content) {
 599            $this->addHeader($context, $header, $content);
 600        }
 601
 602        return $this;
 603    }
 604
 605    /**
 606     * Set headers from context => headers pairs
 607     *
 608     * @param  array $options
 609     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 610     */
 611    protected function _setHeaders(array $options)
 612    {
 613        foreach ($options as $context => $headers) {
 614            if (!is_array($headers)) {
 615                continue;
 616            }
 617            $this->setHeaders($context, $headers);
 618        }
 619
 620        return $this;
 621    }
 622
 623    /**
 624     * Set multiple headers at once for a given context
 625     *
 626     * @param  string $context
 627     * @param  array  $headers
 628     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 629     */
 630    public function setHeaders($context, array $headers)
 631    {
 632        $this->clearHeaders($context);
 633        foreach ($headers as $header => $content) {
 634            $this->setHeader($context, $header, $content);
 635        }
 636
 637        return $this;
 638    }
 639
 640    /**
 641     * Retrieve context header
 642     *
 643     * Returns the value of a given header for a given context type
 644     *
 645     * @param  string $context
 646     * @param  string $header
 647     * @return string|null
 648     */
 649    public function getHeader($context, $header)
 650    {
 651        $this->hasContext($context, true);
 652        $context = (string) $context;
 653        $header  = (string) $header;
 654        if (isset($this->_contexts[$context]['headers'][$header])) {
 655            return $this->_contexts[$context]['headers'][$header];
 656        }
 657
 658        return null;
 659    }
 660
 661    /**
 662     * Retrieve context headers
 663     *
 664     * Returns all headers for a context as key/value pairs
 665     *
 666     * @param  string $context
 667     * @return array
 668     */
 669    public function getHeaders($context)
 670    {
 671        $this->hasContext($context, true);
 672        $context = (string) $context;
 673        return $this->_contexts[$context]['headers'];
 674    }
 675
 676    /**
 677     * Remove a single header from a context
 678     *
 679     * @param  string $context
 680     * @param  string $header
 681     * @return boolean
 682     */
 683    public function removeHeader($context, $header)
 684    {
 685        $this->hasContext($context, true);
 686        $context = (string) $context;
 687        $header  = (string) $header;
 688        if (isset($this->_contexts[$context]['headers'][$header])) {
 689            unset($this->_contexts[$context]['headers'][$header]);
 690            return true;
 691        }
 692
 693        return false;
 694    }
 695
 696    /**
 697     * Clear all headers for a given context
 698     *
 699     * @param  string $context
 700     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 701     */
 702    public function clearHeaders($context)
 703    {
 704        $this->hasContext($context, true);
 705        $context = (string) $context;
 706        $this->_contexts[$context]['headers'] = array();
 707        return $this;
 708    }
 709
 710    /**
 711     * Validate trigger and return in normalized form
 712     *
 713     * @param  string $trigger
 714     * @throws Zend_Controller_Action_Exception
 715     * @return string
 716     */
 717    protected function _validateTrigger($trigger)
 718    {
 719        $trigger = strtoupper($trigger);
 720        if ('TRIGGER_' !== substr($trigger, 0, 8)) {
 721            $trigger = 'TRIGGER_' . $trigger;
 722        }
 723
 724        if (!in_array($trigger, array(self::TRIGGER_INIT, self::TRIGGER_POST))) {
 725            /**
 726             * @see Zend_Controller_Action_Exception
 727             */
 728            require_once 'Zend/Controller/Action/Exception.php';
 729            throw new Zend_Controller_Action_Exception(sprintf('Invalid trigger "%s"', $trigger));
 730        }
 731
 732        return $trigger;
 733    }
 734
 735    /**
 736     * Set a callback for a given context and trigger
 737     *
 738     * @param  string       $context
 739     * @param  string       $trigger
 740     * @param  string|array $callback
 741     * @throws Zend_Controller_Action_Exception
 742     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 743     */
 744    public function setCallback($context, $trigger, $callback)
 745    {
 746        $this->hasContext($context, true);
 747        $trigger = $this->_validateTrigger($trigger);
 748
 749        if (!is_string($callback)) {
 750            if (!is_array($callback) || (2 != count($callback))) {
 751                /**
 752                 * @see Zend_Controller_Action_Exception
 753                 */
 754                require_once 'Zend/Controller/Action/Exception.php';
 755                throw new Zend_Controller_Action_Exception('Invalid callback specified');
 756            }
 757        }
 758
 759        $this->_contexts[$context]['callbacks'][$trigger] = $callback;
 760        return $this;
 761    }
 762
 763    /**
 764     * Set callbacks from array of context => callbacks pairs
 765     *
 766     * @param  array $options
 767     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 768     */
 769    protected function _setCallbacks(array $options)
 770    {
 771        foreach ($options as $context => $callbacks) {
 772            if (!is_array($callbacks)) {
 773                continue;
 774            }
 775
 776            $this->setCallbacks($context, $callbacks);
 777        }
 778        return $this;
 779    }
 780
 781    /**
 782     * Set callbacks for a given context
 783     *
 784     * Callbacks should be in trigger/callback pairs.
 785     *
 786     * @param  string $context
 787     * @param  array  $callbacks
 788     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 789     */
 790    public function setCallbacks($context, array $callbacks)
 791    {
 792        $this->hasContext($context, true);
 793        $context = (string) $context;
 794        if (!isset($this->_contexts[$context]['callbacks'])) {
 795            $this->_contexts[$context]['callbacks'] = array();
 796        }
 797
 798        foreach ($callbacks as $trigger => $callback) {
 799            $this->setCallback($context, $trigger, $callback);
 800        }
 801        return $this;
 802    }
 803
 804    /**
 805     * Get a single callback for a given context and trigger
 806     *
 807     * @param  string $context
 808     * @param  string $trigger
 809     * @return string|array|null
 810     */
 811    public function getCallback($context, $trigger)
 812    {
 813        $this->hasContext($context, true);
 814        $trigger = $this->_validateTrigger($trigger);
 815        if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
 816            return $this->_contexts[$context]['callbacks'][$trigger];
 817        }
 818
 819        return null;
 820    }
 821
 822    /**
 823     * Get all callbacks for a given context
 824     *
 825     * @param  string $context
 826     * @return array
 827     */
 828    public function getCallbacks($context)
 829    {
 830        $this->hasContext($context, true);
 831        return $this->_contexts[$context]['callbacks'];
 832    }
 833
 834    /**
 835     * Clear a callback for a given context and trigger
 836     *
 837     * @param  string $context
 838     * @param  string $trigger
 839     * @return boolean
 840     */
 841    public function removeCallback($context, $trigger)
 842    {
 843        $this->hasContext($context, true);
 844        $trigger = $this->_validateTrigger($trigger);
 845        if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
 846            unset($this->_contexts[$context]['callbacks'][$trigger]);
 847            return true;
 848        }
 849
 850        return false;
 851    }
 852
 853    /**
 854     * Clear all callbacks for a given context
 855     *
 856     * @param  string $context
 857     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 858     */
 859    public function clearCallbacks($context)
 860    {
 861        $this->hasContext($context, true);
 862        $this->_contexts[$context]['callbacks'] = array();
 863        return $this;
 864    }
 865
 866    /**
 867     * Set name of parameter to use when determining context format
 868     *
 869     * @param  string $name
 870     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 871     */
 872    public function setContextParam($name)
 873    {
 874        $this->_contextParam = (string) $name;
 875        return $this;
 876    }
 877
 878    /**
 879     * Return context format request parameter name
 880     *
 881     * @return string
 882     */
 883    public function getContextParam()
 884    {
 885        return $this->_contextParam;
 886    }
 887
 888    /**
 889     * Indicate default context to use when no context format provided
 890     *
 891     * @param  string $type
 892     * @throws Zend_Controller_Action_Exception
 893     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 894     */
 895    public function setDefaultContext($type)
 896    {
 897        if (!isset($this->_contexts[$type])) {
 898            /**
 899             * @see Zend_Controller_Action_Exception
 900             */
 901            require_once 'Zend/Controller/Action/Exception.php';
 902            throw new Zend_Controller_Action_Exception(sprintf('Cannot set default context; invalid context type "%s"', $type));
 903        }
 904
 905        $this->_defaultContext = $type;
 906        return $this;
 907    }
 908
 909    /**
 910     * Return default context
 911     *
 912     * @return string
 913     */
 914    public function getDefaultContext()
 915    {
 916        return $this->_defaultContext;
 917    }
 918
 919    /**
 920     * Set flag indicating if layout should be disabled
 921     *
 922     * @param  boolean $flag
 923     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 924     */
 925    public function setAutoDisableLayout($flag)
 926    {
 927        $this->_disableLayout = ($flag) ? true : false;
 928        return $this;
 929    }
 930
 931    /**
 932     * Retrieve auto layout disable flag
 933     *
 934     * @return boolean
 935     */
 936    public function getAutoDisableLayout()
 937    {
 938        return $this->_disableLayout;
 939    }
 940
 941    /**
 942     * Add new context
 943     *
 944     * @param  string $context Context type
 945     * @param  array  $spec    Context specification
 946     * @throws Zend_Controller_Action_Exception
 947     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 948     */
 949    public function addContext($context, array $spec)
 950    {
 951        if ($this->hasContext($context)) {
 952            /**
 953             * @see Zend_Controller_Action_Exception
 954             */
 955            require_once 'Zend/Controller/Action/Exception.php';
 956            throw new Zend_Controller_Action_Exception(sprintf('Cannot add context "%s"; already exists', $context));
 957        }
 958        $context = (string) $context;
 959
 960        $this->_contexts[$context] = array();
 961
 962        $this->setSuffix($context,    (isset($spec['suffix'])    ? $spec['suffix']    : ''))
 963             ->setHeaders($context,   (isset($spec['headers'])   ? $spec['headers']   : array()))
 964             ->setCallbacks($context, (isset($spec['callbacks']) ? $spec['callbacks'] : array()));
 965        return $this;
 966    }
 967
 968    /**
 969     * Overwrite existing context
 970     *
 971     * @param  string $context Context type
 972     * @param  array  $spec    Context specification
 973     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 974     */
 975    public function setContext($context, array $spec)
 976    {
 977        $this->removeContext($context);
 978        return $this->addContext($context, $spec);
 979    }
 980
 981    /**
 982     * Add multiple contexts
 983     *
 984     * @param  array $contexts
 985     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 986     */
 987    public function addContexts(array $contexts)
 988    {
 989        foreach ($contexts as $context => $spec) {
 990            $this->addContext($context, $spec);
 991        }
 992        return $this;
 993    }
 994
 995    /**
 996     * Set multiple contexts, after first removing all
 997     *
 998     * @param  array $contexts
 999     * @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        $action     = (string) $action;
1308        $contextKey = $this->_contextKey;
1309
1310        if (!isset($controller->$contextKey)) {
1311            return array();
1312        }
1313
1314        if (null !== $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