PageRenderTime 62ms CodeModel.GetById 17ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://firephp.googlecode.com/
PHP | 1368 lines | 729 code | 143 blank | 496 comment | 111 complexity | b13e7f442243d2c8aa999e24b237bd4e MD5 | raw file
   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-2008 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 12316 2008-11-05 22:33:18Z sidhighwind $
  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-2008 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     * Constructor
 119     *
 120     * @param  array|Zend_Config $options
 121     * @return void
 122     */
 123    public function __construct($options = null)
 124    {
 125        if ($options instanceof Zend_Config) {
 126            $this->setConfig($options);
 127        } elseif (is_array($options)) {
 128            $this->setOptions($options);
 129        }
 130
 131        if (empty($this->_contexts)) {
 132            $this->addContexts(array(
 133                'json' => array(
 134                    'suffix'    => 'json',
 135                    'headers'   => array('Content-Type' => 'application/json'),
 136                    'callbacks' => array(
 137                        'init' => 'initJsonContext',
 138                        'post' => 'postJsonContext'
 139                    )
 140                ),
 141                'xml'  => array(
 142                    'suffix'    => 'xml',
 143                    'headers'   => array('Content-Type' => 'application/xml'),
 144                )
 145            ));
 146        }
 147    }
 148
 149    /**
 150     * Configure object from array of options
 151     *
 152     * @param  array $options
 153     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 154     */
 155    public function setOptions(array $options)
 156    {
 157        if (isset($options['contexts'])) {
 158            $this->setContexts($options['contexts']);
 159            unset($options['contexts']);
 160        }
 161
 162        foreach ($options as $key => $value) {
 163            $method = 'set' . ucfirst($key);
 164            if (in_array($method, $this->_unconfigurable)) {
 165                continue;
 166            }
 167
 168            if (in_array($method, $this->_specialConfig)) {
 169                $method = '_' . $method;
 170            }
 171
 172            if (method_exists($this, $method)) {
 173                $this->$method($value);
 174            }
 175        }
 176        return $this;
 177    }
 178
 179    /**
 180     * Set object state from config object
 181     *
 182     * @param  Zend_Config $config
 183     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 184     */
 185    public function setConfig(Zend_Config $config)
 186    {
 187        return $this->setOptions($config->toArray());
 188    }
 189
 190    /**
 191     * Strategy pattern: return object
 192     *
 193     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 194     */
 195    public function direct()
 196    {
 197        return $this;
 198    }
 199
 200    /**
 201     * Initialize context detection and switching
 202     *
 203     * @param  mixed $format
 204     * @throws Zend_Controller_Action_Exception
 205     * @return void
 206     */
 207    public function initContext($format = null)
 208    {
 209        $this->_currentContext = null;
 210
 211        $controller = $this->getActionController();
 212        $request    = $this->getRequest();
 213        $action     = $request->getActionName();
 214
 215        // Return if no context switching enabled, or no context switching
 216        // enabled for this action
 217        $contexts = $this->getActionContexts($action);
 218        if (empty($contexts)) {
 219            return;
 220        }
 221
 222        // Return if no context parameter provided
 223        if (!$context = $request->getParam($this->getContextParam())) {
 224            if ($format === null) {
 225                return;
 226            }
 227            $context = $format;
 228            $format  = null;
 229        }
 230
 231        // Check if context allowed by action controller
 232        if (!$this->hasActionContext($action, $context)) {
 233            return;
 234        }
 235
 236        // Return if invalid context parameter provided and no format or invalid
 237        // format provided
 238        if (!$this->hasContext($context)) {
 239            if (empty($format) || !$this->hasContext($format)) {
 240
 241                return;
 242            }
 243        }
 244
 245        // Use provided format if passed
 246        if (!empty($format) && $this->hasContext($format)) {
 247            $context = $format;
 248        }
 249
 250        $suffix = $this->getSuffix($context);
 251        $this->_getViewRenderer()->setViewSuffix($suffix);
 252
 253        $headers = $this->getHeaders($context);
 254        if (!empty($headers)) {
 255            $response = $this->getResponse();
 256            foreach ($headers as $header => $content) {
 257                $response->setHeader($header, $content);
 258            }
 259        }
 260
 261        if ($this->getAutoDisableLayout()) {
 262            /**
 263             * @see Zend_Layout
 264             */
 265            require_once 'Zend/Layout.php';
 266            $layout = Zend_Layout::getMvcInstance();
 267            if (null !== $layout) {
 268                $layout->disableLayout();
 269            }
 270        }
 271
 272        if (null !== ($callback = $this->getCallback($context, self::TRIGGER_INIT))) {
 273            if (is_string($callback) && method_exists($this, $callback)) {
 274                $this->$callback();
 275            } elseif (is_string($callback) && function_exists($callback)) {
 276                $callback();
 277            } elseif (is_array($callback)) {
 278                call_user_func($callback);
 279            } else {
 280                /**
 281                 * @see Zend_Controller_Action_Exception
 282                 */
 283                require_once 'Zend/Controller/Action/Exception.php';
 284                throw new Zend_Controller_Action_Exception(sprintf('Invalid context callback registered for context "%s"', $context));
 285            }
 286        }
 287
 288        $this->_currentContext = $context;
 289    }
 290
 291    /**
 292     * JSON context extra initialization
 293     *
 294     * Turns off viewRenderer auto-rendering
 295     *
 296     * @return void
 297     */
 298    public function initJsonContext()
 299    {
 300        if (!$this->getAutoJsonSerialization()) {
 301            return;
 302        }
 303
 304        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
 305        $view = $viewRenderer->view;
 306        if ($view instanceof Zend_View_Interface) {
 307            $viewRenderer->setNoRender(true);
 308        }
 309    }
 310
 311    /**
 312     * Should JSON contexts auto-serialize?
 313     *
 314     * @param  boolean $flag
 315     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 316     */
 317    public function setAutoJsonSerialization($flag)
 318    {
 319        $this->_autoJsonSerialization = (bool) $flag;
 320        return $this;
 321    }
 322
 323    /**
 324     * Get JSON context auto-serialization flag
 325     *
 326     * @return boolean
 327     */
 328    public function getAutoJsonSerialization()
 329    {
 330        return $this->_autoJsonSerialization;
 331    }
 332
 333    /**
 334     * Set suffix from array
 335     *
 336     * @param  array $spec
 337     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 338     */
 339    protected function _setSuffix(array $spec)
 340    {
 341        foreach ($spec as $context => $suffixInfo) {
 342            if (!is_string($context)) {
 343                $context = null;
 344            }
 345
 346            if (is_string($suffixInfo)) {
 347                $this->setSuffix($context, $suffixInfo);
 348                continue;
 349            } elseif (is_array($suffixInfo)) {
 350                if (isset($suffixInfo['suffix'])) {
 351                    $suffix                    = $suffixInfo['suffix'];
 352                    $prependViewRendererSuffix = true;
 353
 354                    if ((null === $context) && isset($suffixInfo['context'])) {
 355                        $context = $suffixInfo['context'];
 356                    }
 357
 358                    if (isset($suffixInfo['prependViewRendererSuffix'])) {
 359                        $prependViewRendererSuffix = $suffixInfo['prependViewRendererSuffix'];
 360                    }
 361
 362                    $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
 363                    continue;
 364                }
 365
 366                $count = count($suffixInfo);
 367                switch (true) {
 368                    case (($count < 2) && (null === $context)):
 369                        /**
 370                         * @see Zend_Controller_Action_Exception
 371                         */
 372                        require_once 'Zend/Controller/Action/Exception.php';
 373                        throw new Zend_Controller_Action_Exception('Invalid suffix information provided in config');
 374                    case ($count < 2):
 375                        $suffix = array_shift($suffixInfo);
 376                        $this->setSuffix($context, $suffix);
 377                        break;
 378                    case (($count < 3) && (null === $context)):
 379                        $context = array_shift($suffixInfo);
 380                        $suffix  = array_shift($suffixInfo);
 381                        $this->setSuffix($context, $suffix);
 382                        break;
 383                    case (($count == 3) && (null === $context)):
 384                        $context = array_shift($suffixInfo);
 385                        $suffix  = array_shift($suffixInfo);
 386                        $prependViewRendererSuffix = array_shift($suffixInfo);
 387                        $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
 388                        break;
 389                    case ($count >= 2):
 390                        $suffix  = array_shift($suffixInfo);
 391                        $prependViewRendererSuffix = array_shift($suffixInfo);
 392                        $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
 393                        break;
 394                }
 395            }
 396        }
 397        return $this;
 398    }
 399
 400    /**
 401     * Customize view script suffix to use when switching context.
 402     *
 403     * Passing an empty suffix value to the setters disables the view script
 404     * suffix change.
 405     *
 406     * @param  string  $context                   Context type for which to set suffix
 407     * @param  string  $suffix                    Suffix to use
 408     * @param  boolean $prependViewRendererSuffix Whether or not to prepend the new suffix to the viewrenderer suffix
 409     * @throws Zend_Controller_Action_Exception
 410     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 411     */
 412    public function setSuffix($context, $suffix, $prependViewRendererSuffix = true)
 413    {
 414        if (!isset($this->_contexts[$context])) {
 415            /**
 416             * @see Zend_Controller_Action_Exception
 417             */
 418            require_once 'Zend/Controller/Action/Exception.php';
 419            throw new Zend_Controller_Action_Exception(sprintf('Cannot set suffix; invalid context type "%s"', $context));
 420        }
 421
 422        if (empty($suffix)) {
 423            $suffix = '';
 424        }
 425
 426        if (is_array($suffix)) {
 427            if (isset($suffix['prependViewRendererSuffix'])) {
 428                $prependViewRendererSuffix = $suffix['prependViewRendererSuffix'];
 429            }
 430            if (isset($suffix['suffix'])) {
 431                $suffix = $suffix['suffix'];
 432            } else {
 433                $suffix = '';
 434            }
 435        }
 436
 437        $suffix = (string) $suffix;
 438
 439        if ($prependViewRendererSuffix) {
 440            if (empty($suffix)) {
 441                $suffix = $this->_getViewRenderer()->getViewSuffix();
 442            } else {
 443                $suffix .= '.' . $this->_getViewRenderer()->getViewSuffix();
 444            }
 445        }
 446
 447        $this->_contexts[$context]['suffix'] = $suffix;
 448        return $this;
 449    }
 450
 451    /**
 452     * Retrieve suffix for given context type
 453     *
 454     * @param  string $type Context type
 455     * @throws Zend_Controller_Action_Exception
 456     * @return string
 457     */
 458    public function getSuffix($type)
 459    {
 460        if (!isset($this->_contexts[$type])) {
 461            /**
 462             * @see Zend_Controller_Action_Exception
 463             */
 464            require_once 'Zend/Controller/Action/Exception.php';
 465            throw new Zend_Controller_Action_Exception(sprintf('Cannot retrieve suffix; invalid context type "%s"', $type));
 466        }
 467
 468        return $this->_contexts[$type]['suffix'];
 469    }
 470
 471    /**
 472     * Does the given context exist?
 473     *
 474     * @param  string  $context
 475     * @param  boolean $throwException
 476     * @throws Zend_Controller_Action_Exception if context does not exist and throwException is true
 477     * @return bool
 478     */
 479    public function hasContext($context, $throwException = false)
 480    {
 481        if (is_string($context)) {
 482            if (isset($this->_contexts[$context])) {
 483                return true;
 484            }
 485        } elseif (is_array($context)) {
 486            $error = false;
 487            foreach ($context as $test) {
 488                if (!isset($this->_contexts[$test])) {
 489                    $error = (string) $test;
 490                    break;
 491                }
 492            }
 493            if (false === $error) {
 494                return true;
 495            }
 496            $context = $error;
 497        } elseif (true === $context) {
 498            return true;
 499        }
 500
 501        if ($throwException) {
 502            /**
 503             * @see Zend_Controller_Action_Exception
 504             */
 505            require_once 'Zend/Controller/Action/Exception.php';
 506            throw new Zend_Controller_Action_Exception(sprintf('Context "%s" does not exist', $context));
 507        }
 508
 509        return false;
 510    }
 511
 512    /**
 513     * Add header to context
 514     *
 515     * @param  string $context
 516     * @param  string $header
 517     * @param  string $content
 518     * @throws Zend_Controller_Action_Exception
 519     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 520     */
 521    public function addHeader($context, $header, $content)
 522    {
 523        $context = (string) $context;
 524        $this->hasContext($context, true);
 525
 526        $header  = (string) $header;
 527        $content = (string) $content;
 528
 529        if (isset($this->_contexts[$context]['headers'][$header])) {
 530            /**
 531             * @see Zend_Controller_Action_Exception
 532             */
 533            require_once 'Zend/Controller/Action/Exception.php';
 534            throw new Zend_Controller_Action_Exception(sprintf('Cannot add "%s" header to context "%s": already exists', $header, $context));
 535        }
 536
 537        $this->_contexts[$context]['headers'][$header] = $content;
 538        return $this;
 539    }
 540
 541    /**
 542     * Customize response header to use when switching context
 543     *
 544     * Passing an empty header value to the setters disables the response
 545     * header.
 546     *
 547     * @param  string $type   Context type for which to set suffix
 548     * @param  string $header Header to set
 549     * @param  string $content Header content
 550     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 551     */
 552    public function setHeader($context, $header, $content)
 553    {
 554        $this->hasContext($context, true);
 555        $context = (string) $context;
 556        $header  = (string) $header;
 557        $content = (string) $content;
 558
 559        $this->_contexts[$context]['headers'][$header] = $content;
 560        return $this;
 561    }
 562
 563    /**
 564     * Add multiple headers at once for a given context
 565     *
 566     * @param  string $context
 567     * @param  array  $headers
 568     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 569     */
 570    public function addHeaders($context, array $headers)
 571    {
 572        foreach ($headers as $header => $content) {
 573            $this->addHeader($context, $header, $content);
 574        }
 575
 576        return $this;
 577    }
 578
 579    /**
 580     * Set headers from context => headers pairs
 581     *
 582     * @param  array $options
 583     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 584     */
 585    protected function _setHeaders(array $options)
 586    {
 587        foreach ($options as $context => $headers) {
 588            if (!is_array($headers)) {
 589                continue;
 590            }
 591            $this->setHeaders($context, $headers);
 592        }
 593
 594        return $this;
 595    }
 596
 597    /**
 598     * Set multiple headers at once for a given context
 599     *
 600     * @param  string $context
 601     * @param  array  $headers
 602     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 603     */
 604    public function setHeaders($context, array $headers)
 605    {
 606        $this->clearHeaders($context);
 607        foreach ($headers as $header => $content) {
 608            $this->setHeader($context, $header, $content);
 609        }
 610
 611        return $this;
 612    }
 613
 614    /**
 615     * Retrieve context header
 616     *
 617     * Returns the value of a given header for a given context type
 618     *
 619     * @param  string $context
 620     * @param  string $header
 621     * @return string|null
 622     */
 623    public function getHeader($context, $header)
 624    {
 625        $this->hasContext($context, true);
 626        $context = (string) $context;
 627        $header  = (string) $header;
 628        if (isset($this->_contexts[$context]['headers'][$header])) {
 629            return $this->_contexts[$context]['headers'][$header];
 630        }
 631
 632        return null;
 633    }
 634
 635    /**
 636     * Retrieve context headers
 637     *
 638     * Returns all headers for a context as key/value pairs
 639     *
 640     * @param  string $context
 641     * @return array
 642     */
 643    public function getHeaders($context)
 644    {
 645        $this->hasContext($context, true);
 646        $context = (string) $context;
 647        return $this->_contexts[$context]['headers'];
 648    }
 649
 650    /**
 651     * Remove a single header from a context
 652     *
 653     * @param  string $context
 654     * @param  string $header
 655     * @return boolean
 656     */
 657    public function removeHeader($context, $header)
 658    {
 659        $this->hasContext($context, true);
 660        $context = (string) $context;
 661        $header  = (string) $header;
 662        if (isset($this->_contexts[$context]['headers'][$header])) {
 663            unset($this->_contexts[$context]['headers'][$header]);
 664            return true;
 665        }
 666
 667        return false;
 668    }
 669
 670    /**
 671     * Clear all headers for a given context
 672     *
 673     * @param  string $context
 674     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 675     */
 676    public function clearHeaders($context)
 677    {
 678        $this->hasContext($context, true);
 679        $context = (string) $context;
 680        $this->_contexts[$context]['headers'] = array();
 681        return $this;
 682    }
 683
 684    /**
 685     * Validate trigger and return in normalized form
 686     *
 687     * @param  string $trigger
 688     * @throws Zend_Controller_Action_Exception
 689     * @return string
 690     */
 691    protected function _validateTrigger($trigger)
 692    {
 693        $trigger = strtoupper($trigger);
 694        if ('TRIGGER_' !== substr($trigger, 0, 8)) {
 695            $trigger = 'TRIGGER_' . $trigger;
 696        }
 697
 698        if (!in_array($trigger, array(self::TRIGGER_INIT, self::TRIGGER_POST))) {
 699            /**
 700             * @see Zend_Controller_Action_Exception
 701             */
 702            require_once 'Zend/Controller/Action/Exception.php';
 703            throw new Zend_Controller_Action_Exception(sprintf('Invalid trigger "%s"', $trigger));
 704        }
 705
 706        return $trigger;
 707    }
 708
 709    /**
 710     * Set a callback for a given context and trigger
 711     *
 712     * @param  string       $context
 713     * @param  string       $trigger
 714     * @param  string|array $callback
 715     * @throws Zend_Controller_Action_Exception
 716     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 717     */
 718    public function setCallback($context, $trigger, $callback)
 719    {
 720        $this->hasContext($context, true);
 721        $trigger = $this->_validateTrigger($trigger);
 722
 723        if (!is_string($callback)) {
 724            if (!is_array($callback) || (2 != count($callback))) {
 725                /**
 726                 * @see Zend_Controller_Action_Exception
 727                 */
 728                require_once 'Zend/Controller/Action/Exception.php';
 729                throw new Zend_Controller_Action_Exception('Invalid callback specified');
 730            }
 731        }
 732
 733        $this->_contexts[$context]['callbacks'][$trigger] = $callback;
 734        return $this;
 735    }
 736
 737    /**
 738     * Set callbacks from array of context => callbacks pairs
 739     *
 740     * @param  array $options
 741     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 742     */
 743    protected function _setCallbacks(array $options)
 744    {
 745        foreach ($options as $context => $callbacks) {
 746            if (!is_array($callbacks)) {
 747                continue;
 748            }
 749
 750            $this->setCallbacks($context, $callbacks);
 751        }
 752        return $this;
 753    }
 754
 755    /**
 756     * Set callbacks for a given context
 757     *
 758     * Callbacks should be in trigger/callback pairs.
 759     *
 760     * @param  string $context
 761     * @param  array  $callbacks
 762     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 763     */
 764    public function setCallbacks($context, array $callbacks)
 765    {
 766        $this->hasContext($context, true);
 767        $context = (string) $context;
 768        if (!isset($this->_contexts[$context]['callbacks'])) {
 769            $this->_contexts[$context]['callbacks'] = array();
 770        }
 771
 772        foreach ($callbacks as $trigger => $callback) {
 773            $this->setCallback($context, $trigger, $callback);
 774        }
 775        return $this;
 776    }
 777
 778    /**
 779     * Get a single callback for a given context and trigger
 780     *
 781     * @param  string $context
 782     * @param  string $trigger
 783     * @return string|array|null
 784     */
 785    public function getCallback($context, $trigger)
 786    {
 787        $this->hasContext($context, true);
 788        $trigger = $this->_validateTrigger($trigger);
 789        if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
 790            return $this->_contexts[$context]['callbacks'][$trigger];
 791        }
 792
 793        return null;
 794    }
 795
 796    /**
 797     * Get all callbacks for a given context
 798     *
 799     * @param  string $context
 800     * @return array
 801     */
 802    public function getCallbacks($context)
 803    {
 804        $this->hasContext($context, true);
 805        return $this->_contexts[$context]['callbacks'];
 806    }
 807
 808    /**
 809     * Clear a callback for a given context and trigger
 810     *
 811     * @param  string $context
 812     * @param  string $trigger
 813     * @return boolean
 814     */
 815    public function removeCallback($context, $trigger)
 816    {
 817        $this->hasContext($context, true);
 818        $trigger = $this->_validateTrigger($trigger);
 819        if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
 820            unset($this->_contexts[$context]['callbacks'][$trigger]);
 821            return true;
 822        }
 823
 824        return false;
 825    }
 826
 827    /**
 828     * Clear all callbacks for a given context
 829     *
 830     * @param  string $context
 831     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 832     */
 833    public function clearCallbacks($context)
 834    {
 835        $this->hasContext($context, true);
 836        $this->_contexts[$context]['callbacks'] = array();
 837        return $this;
 838    }
 839
 840    /**
 841     * Set name of parameter to use when determining context format
 842     *
 843     * @param  string $name
 844     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 845     */
 846    public function setContextParam($name)
 847    {
 848        $this->_contextParam = (string) $name;
 849        return $this;
 850    }
 851
 852    /**
 853     * Return context format request parameter name
 854     *
 855     * @return string
 856     */
 857    public function getContextParam()
 858    {
 859        return $this->_contextParam;
 860    }
 861
 862    /**
 863     * Indicate default context to use when no context format provided
 864     *
 865     * @param  string $type
 866     * @throws Zend_Controller_Action_Exception
 867     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 868     */
 869    public function setDefaultContext($type)
 870    {
 871        if (!isset($this->_contexts[$type])) {
 872            /**
 873             * @see Zend_Controller_Action_Exception
 874             */
 875            require_once 'Zend/Controller/Action/Exception.php';
 876            throw new Zend_Controller_Action_Exception(sprintf('Cannot set default context; invalid context type "%s"', $type));
 877        }
 878
 879        $this->_defaultContext = $type;
 880        return $this;
 881    }
 882
 883    /**
 884     * Return default context
 885     *
 886     * @return string
 887     */
 888    public function getDefaultContext()
 889    {
 890        return $this->_defaultContext;
 891    }
 892
 893    /**
 894     * Set flag indicating if layout should be disabled
 895     *
 896     * @param  boolean $flag
 897     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 898     */
 899    public function setAutoDisableLayout($flag)
 900    {
 901        $this->_disableLayout = ($flag) ? true : false;
 902        return $this;
 903    }
 904
 905    /**
 906     * Retrieve auto layout disable flag
 907     *
 908     * @return boolean
 909     */
 910    public function getAutoDisableLayout()
 911    {
 912        return $this->_disableLayout;
 913    }
 914
 915    /**
 916     * Add new context
 917     *
 918     * @param  string $context Context type
 919     * @param  array  $spec    Context specification
 920     * @throws Zend_Controller_Action_Exception
 921     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 922     */
 923    public function addContext($context, array $spec)
 924    {
 925        if ($this->hasContext($context)) {
 926            /**
 927             * @see Zend_Controller_Action_Exception
 928             */
 929            require_once 'Zend/Controller/Action/Exception.php';
 930            throw new Zend_Controller_Action_Exception(sprintf('Cannot add context "%s"; already exists', $context));
 931        }
 932        $context = (string) $context;
 933
 934        $this->_contexts[$context] = array();
 935
 936        $this->setSuffix($context,    (isset($spec['suffix'])    ? $spec['suffix']    : ''))
 937             ->setHeaders($context,   (isset($spec['headers'])   ? $spec['headers']   : array()))
 938             ->setCallbacks($context, (isset($spec['callbacks']) ? $spec['callbacks'] : array()));
 939        return $this;
 940    }
 941
 942    /**
 943     * Overwrite existing context
 944     *
 945     * @param  string $context Context type
 946     * @param  array  $spec    Context specification
 947     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 948     */
 949    public function setContext($context, array $spec)
 950    {
 951        $this->removeContext($context);
 952        return $this->addContext($context, $spec);
 953    }
 954
 955    /**
 956     * Add multiple contexts
 957     *
 958     * @param  array $contexts
 959     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 960     */
 961    public function addContexts(array $contexts)
 962    {
 963        foreach ($contexts as $context => $spec) {
 964            $this->addContext($context, $spec);
 965        }
 966        return $this;
 967    }
 968
 969    /**
 970     * Set multiple contexts, after first removing all
 971     *
 972     * @param  array $contexts
 973     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
 974     */
 975    public function setContexts(array $contexts)
 976    {
 977        $this->clearContexts();
 978        foreach ($contexts as $context => $spec) {
 979            $this->addContext($context, $spec);
 980        }
 981        return $this;
 982    }
 983
 984    /**
 985     * Retrieve context specification
 986     *
 987     * @param  string $context
 988     * @return array|null
 989     */
 990    public function getContext($context)
 991    {
 992        if ($this->hasContext($context)) {
 993            return $this->_contexts[(string) $context];
 994        }
 995        return null;
 996    }
 997
 998    /**
 999     * Retrieve context definitions
1000     *
1001     * @return array
1002     */
1003    public function getContexts()
1004    {
1005        return $this->_contexts;
1006    }
1007
1008    /**
1009     * Remove a context
1010     *
1011     * @param  string $context
1012     * @return boolean
1013     */
1014    public function removeContext($context)
1015    {
1016        if ($this->hasContext($context)) {
1017            unset($this->_contexts[(string) $context]);
1018            return true;
1019        }
1020        return false;
1021    }
1022
1023    /**
1024     * Remove all contexts
1025     *
1026     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1027     */
1028    public function clearContexts()
1029    {
1030        $this->_contexts = array();
1031        return $this;
1032    }
1033
1034    /**
1035     * Return current context, if any
1036     *
1037     * @return null|string
1038     */
1039    public function getCurrentContext()
1040    {
1041        return $this->_currentContext;
1042    }
1043
1044    /**
1045     * Post dispatch processing
1046     *
1047     * Execute postDispatch callback for current context, if available
1048     *
1049     * @throws Zend_Controller_Action_Exception
1050     * @return void
1051     */
1052    public function postDispatch()
1053    {
1054        $context = $this->getCurrentContext();
1055        if (null !== $context) {
1056            if (null !== ($callback = $this->getCallback($context, self::TRIGGER_POST))) {
1057                if (is_string($callback) && method_exists($this, $callback)) {
1058                    $this->$callback();
1059                } elseif (is_string($callback) && function_exists($callback)) {
1060                    $callback();
1061                } elseif (is_array($callback)) {
1062                    call_user_func($callback);
1063                } else {
1064                    /**
1065                     * @see Zend_Controller_Action_Exception
1066                     */
1067                    require_once 'Zend/Controller/Action/Exception.php';
1068                    throw new Zend_Controller_Action_Exception(sprintf('Invalid postDispatch context callback registered for context "%s"', $context));
1069                }
1070            }
1071        }
1072    }
1073
1074    /**
1075     * JSON post processing
1076     *
1077     * JSON serialize view variables to response body
1078     *
1079     * @return void
1080     */
1081    public function postJsonContext()
1082    {
1083        if (!$this->getAutoJsonSerialization()) {
1084            return;
1085        }
1086
1087        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
1088        $view = $viewRenderer->view;
1089        if ($view instanceof Zend_View_Interface) {
1090            /**
1091             * @see Zend_Json
1092             */
1093            if(method_exists($view, 'getVars')) {
1094                require_once 'Zend/Json.php';
1095                $vars = Zend_Json::encode($view->getVars());
1096                $this->getResponse()->setBody($vars);
1097            } else {
1098                require_once 'Zend/Controller/Action/Exception.php';
1099                throw new Zend_Controller_Action_Exception('View does not implement the getVars() method needed to encode the view into JSON');
1100            }
1101        }
1102    }
1103
1104    /**
1105     * Add one or more contexts to an action
1106     *
1107     * @param  string       $action
1108     * @param  string|array $context
1109     * @return Zend_Controller_Action_Helper_ContextSwitch|void Provides a fluent interface
1110     */
1111    public function addActionContext($action, $context)
1112    {
1113        $this->hasContext($context, true);
1114        $controller = $this->getActionController();
1115        if (null === $controller) {
1116            return;
1117        }
1118        $action     = (string) $action;
1119        $contextKey = $this->_contextKey;
1120
1121        if (!isset($controller->$contextKey)) {
1122            $controller->$contextKey = array();
1123        }
1124
1125        if (true === $context) {
1126            $contexts = $this->getContexts();
1127            $controller->{$contextKey}[$action] = array_keys($contexts);
1128            return $this;
1129        }
1130
1131        $context = (array) $context;
1132        if (!isset($controller->{$contextKey}[$action])) {
1133            $controller->{$contextKey}[$action] = $context;
1134        } else {
1135            $controller->{$contextKey}[$action] = array_merge(
1136                $controller->{$contextKey}[$action],
1137                $context
1138            );
1139        }
1140
1141        return $this;
1142    }
1143
1144    /**
1145     * Set a context as available for a given controller action
1146     *
1147     * @param  string       $action
1148     * @param  string|array $context
1149     * @return Zend_Controller_Action_Helper_ContextSwitch|void Provides a fluent interface
1150     */
1151    public function setActionContext($action, $context)
1152    {
1153        $this->hasContext($context, true);
1154        $controller = $this->getActionController();
1155        if (null === $controller) {
1156            return;
1157        }
1158        $action     = (string) $action;
1159        $contextKey = $this->_contextKey;
1160
1161        if (!isset($controller->$contextKey)) {
1162            $controller->$contextKey = array();
1163        }
1164
1165        if (true === $context) {
1166            $contexts = $this->getContexts();
1167            $controller->{$contextKey}[$action] = array_keys($contexts);
1168        } else {
1169            $controller->{$contextKey}[$action] = (array) $context;
1170        }
1171
1172        return $this;
1173    }
1174
1175    /**
1176     * Add multiple action/context pairs at once
1177     *
1178     * @param  array $contexts
1179     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1180     */
1181    public function addActionContexts(array $contexts)
1182    {
1183        foreach ($contexts as $action => $context) {
1184            $this->addActionContext($action, $context);
1185        }
1186        return $this;
1187    }
1188
1189    /**
1190     * Overwrite and set multiple action contexts at once
1191     *
1192     * @param  array $contexts
1193     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1194     */
1195    public function setActionContexts(array $contexts)
1196    {
1197        foreach ($contexts as $action => $context) {
1198            $this->setActionContext($action, $context);
1199        }
1200        return $this;
1201    }
1202
1203    /**
1204     * Does a particular controller action have the given context(s)?
1205     *
1206     * @param  string       $action
1207     * @param  string|array $context
1208     * @throws Zend_Controller_Action_Exception
1209     * @return boolean
1210     */
1211    public function hasActionContext($action, $context)
1212    {
1213        $this->hasContext($context, true);
1214        $controller = $this->getActionController();
1215        if (null === $controller) {
1216            return false;
1217        }
1218        $action     = (string) $action;
1219        $contextKey = $this->_contextKey;
1220
1221        if (!isset($controller->{$contextKey})) {
1222            return false;
1223        }
1224
1225        $allContexts = $controller->{$contextKey};
1226
1227        if (!is_array($allContexts)) {
1228            /**
1229             * @see Zend_Controller_Action_Exception
1230             */
1231            require_once 'Zend/Controller/Action/Exception.php';
1232            throw new Zend_Controller_Action_Exception("Invalid contexts found for controller");
1233        }
1234
1235        if (!isset($allContexts[$action])) {
1236            return false;
1237        }
1238
1239        if (true === $allContexts[$action]) {
1240            return true;
1241        }
1242
1243        $contexts = $allContexts[$action];
1244
1245        if (!is_array($contexts)) {
1246            /**
1247             * @see Zend_Controller_Action_Exception
1248             */
1249            require_once 'Zend/Controller/Action/Exception.php';
1250            throw new Zend_Controller_Action_Exception(sprintf("Invalid contexts found for action '%s'", $action));
1251        }
1252
1253        if (is_string($context) && in_array($context, $contexts)) {
1254            return true;
1255        } elseif (is_array($context)) {
1256            $found = true;
1257            foreach ($context as $test) {
1258                if (!in_array($test, $contexts)) {
1259                    $found = false;
1260                    break;
1261                }
1262            }
1263            return $found;
1264        }
1265
1266        return false;
1267    }
1268
1269    /**
1270     * Get contexts for a given action or all actions in the controller
1271     *
1272     * @param  string $action
1273     * @return array
1274     */
1275    public function getActionContexts($action = null)
1276    {
1277        $controller = $this->getActionController();
1278        if (null === $controller) {
1279            return array();
1280        }
1281        $action     = (string) $action;
1282        $contextKey = $this->_contextKey;
1283
1284        if (!isset($controller->$contextKey)) {
1285            return array();
1286        }
1287
1288        if (null !== $action) {
1289            if (isset($controller->{$contextKey}[$action])) {
1290                return $controller->{$contextKey}[$action];
1291            } else {
1292                return array();
1293            }
1294        }
1295
1296        return $controller->$contextKey;
1297    }
1298
1299    /**
1300     * Remove one or more contexts for a given controller action
1301     *
1302     * @param  string       $action
1303     * @param  string|array $context
1304     * @return boolean
1305     */
1306    public function removeActionContext($action, $context)
1307    {
1308        if ($this->hasActionContext($action, $context)) {
1309            $controller     = $this->getActionController();
1310            $contextKey     = $this->_contextKey;
1311            $action         = (string) $action;
1312            $contexts       = $controller->$contextKey;
1313            $actionContexts = $contexts[$action];
1314            $contexts       = (array) $context;
1315            foreach ($contexts as $context) {
1316                $index = array_search($context, $actionContexts);
1317                if (false !== $index) {
1318                    unset($controller->{$contextKey}[$action][$index]);
1319                }
1320            }
1321            return true;
1322        }
1323        return false;
1324    }
1325
1326    /**
1327     * Clear all contexts for a given controller action or all actions
1328     *
1329     * @param  string $action
1330     * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
1331     */
1332    public function clearActionContexts($action = null)
1333    {
1334        $controller = $this->getActionController();
1335        $contextKey = $this->_contextKey;
1336
1337        if (!isset($controller->$contextKey) || empty($controller->$contextKey)) {
1338            return $this;
1339        }
1340
1341        if (null === $action) {
1342            $controller->$contextKey = array();
1343            return $this;
1344        }
1345
1346        $action = (string) $action;
1347        if (isset($controller->{$contextKey}[$action])) {
1348            unset($controller->{$contextKey}[$action]);
1349        }
1350
1351        return $this;
1352    }
1353
1354    /**
1355     * Retrieve ViewRenderer
1356     *
1357     * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
1358     */
1359    protected function _getViewRenderer()
1360    {
1361        if (null === $this->_viewRenderer) {
1362            $this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
1363        }
1364
1365        return $this->_viewRenderer;
1366    }
1367}
1368