File indexing completed on 2025-03-02 05:29:35

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Markup
0017  * @subpackage Renderer
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_config
0025  */
0026 // require_once 'Zend/Config.php';
0027 /**
0028  * @see Zend_Filter
0029  */
0030 // require_once 'Zend/Filter.php';
0031 /**
0032  * @see Zend_Markup_Renderer_TokenConverterInterface
0033  */
0034 // require_once 'Zend/Markup/Renderer/TokenConverterInterface.php';
0035 
0036 /**
0037  * Defines the basic rendering functionality
0038  *
0039  * @category   Zend
0040  * @package    Zend_Markup
0041  * @subpackage Renderer
0042  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0043  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0044  */
0045 abstract class Zend_Markup_Renderer_RendererAbstract
0046 {
0047     const TYPE_CALLBACK = 4;
0048     const TYPE_REPLACE  = 8;
0049     const TYPE_ALIAS    = 16;
0050 
0051     /**
0052      * Tag info
0053      *
0054      * @var array
0055      */
0056     protected $_markups = array();
0057 
0058     /**
0059      * Parser
0060      *
0061      * @var Zend_Markup_Parser_ParserInterface
0062      */
0063     protected $_parser;
0064 
0065     /**
0066      * What filter to use
0067      *
0068      * @var bool
0069      */
0070     protected $_filter;
0071 
0072     /**
0073      * Filter chain
0074      *
0075      * @var Zend_Filter
0076      */
0077     protected $_defaultFilter;
0078 
0079     /**
0080      * The current group
0081      *
0082      * @var string
0083      */
0084     protected $_group;
0085 
0086     /**
0087      * Groups definition
0088      *
0089      * @var array
0090      */
0091     protected $_groups = array();
0092 
0093     /**
0094      * Plugin loader for tags
0095      *
0096      * @var Zend_Loader_PluginLoader
0097      */
0098     protected $_pluginLoader;
0099 
0100     /**
0101      * The current token
0102      *
0103      * @var Zend_Markup_Token
0104      */
0105     protected $_token;
0106 
0107     /**
0108      * Encoding
0109      *
0110      * @var string
0111      */
0112     protected static $_encoding = 'UTF-8';
0113 
0114 
0115     /**
0116      * Constructor
0117      *
0118      * @param array|Zend_Config $options
0119      *
0120      * @return void
0121      */
0122     public function __construct($options = array())
0123     {
0124         if ($options instanceof Zend_Config) {
0125             $options = $options->toArray();
0126         }
0127 
0128         if (isset($options['encoding'])) {
0129             $this->setEncoding($options['encoding']);
0130         }
0131         if (isset($options['parser'])) {
0132             $this->setParser($options['parser']);
0133         }
0134         if (!isset($options['useDefaultFilters']) || ($options['useDefaultFilters'] === true)) {
0135             $this->addDefaultFilters();
0136         }
0137         if (isset($options['defaultFilter'])) {
0138             $this->addDefaultFilter($options['defaultFilter']);
0139         }
0140     }
0141 
0142     /**
0143      * Set the parser
0144      *
0145      * @param  Zend_Markup_Parser_ParserInterface $parser
0146      * @return Zend_Markup_Renderer_RendererAbstract
0147      */
0148     public function setParser(Zend_Markup_Parser_ParserInterface $parser)
0149     {
0150         $this->_parser = $parser;
0151         return $this;
0152     }
0153 
0154     /**
0155      * Get the parser
0156      *
0157      * @return Zend_Markup_Parser_ParserInterface
0158      */
0159     public function getParser()
0160     {
0161         return $this->_parser;
0162     }
0163 
0164     /**
0165      * Get the plugin loader
0166      *
0167      * @return Zend_Loader_PluginLoader
0168      */
0169     public function getPluginLoader()
0170     {
0171         return $this->_pluginLoader;
0172     }
0173 
0174     /**
0175      * Set the renderer's encoding
0176      *
0177      * @param string $encoding
0178      *
0179      * @return void
0180      */
0181     public static function setEncoding($encoding)
0182     {
0183         self::$_encoding = $encoding;
0184     }
0185 
0186     /**
0187      * Get the renderer's encoding
0188      *
0189      * @return string
0190      */
0191     public static function getEncoding()
0192     {
0193         return self::$_encoding;
0194     }
0195 
0196     /**
0197      * Add a new markup
0198      *
0199      * @param string $name
0200      * @param string $type
0201      * @param array $options
0202      *
0203      * @return Zend_Markup_Renderer_RendererAbstract
0204      */
0205     public function addMarkup($name, $type, array $options)
0206     {
0207         if (!isset($options['group']) && ($type ^ self::TYPE_ALIAS)) {
0208             // require_once 'Zend/Markup/Renderer/Exception.php';
0209             throw new Zend_Markup_Renderer_Exception("There is no render group defined.");
0210         }
0211 
0212         // add the filter
0213         if (isset($options['filter'])) {
0214             if ($options['filter'] instanceof Zend_Filter_Interface) {
0215                 $filter = $options['filter'];
0216             } elseif ($options['filter'] === true) {
0217                 $filter = $this->getDefaultFilter();
0218             } else {
0219                 $filter = false;
0220             }
0221         } else {
0222             $filter = $this->getDefaultFilter();
0223         }
0224 
0225         // check the type
0226         if ($type & self::TYPE_CALLBACK) {
0227             // add a callback tag
0228             if (isset($options['callback'])) {
0229                 if (!($options['callback'] instanceof Zend_Markup_Renderer_TokenConverterInterface)) {
0230                     // require_once 'Zend/Markup/Renderer/Exception.php';
0231                     throw new Zend_Markup_Renderer_Exception("Not a valid tag callback.");
0232                 }
0233                 if (method_exists($options['callback'], 'setRenderer')) {
0234                     $options['callback']->setRenderer($this);
0235                 }
0236             } else {
0237                 $options['callback'] = null;
0238             }
0239 
0240             $options['type'] = $type;
0241             $options['filter'] = $filter;
0242 
0243             $this->_markups[$name] = $options;
0244         } elseif ($type & self::TYPE_ALIAS) {
0245             // add an alias
0246             if (empty($options['name'])) {
0247                 // require_once 'Zend/Markup/Renderer/Exception.php';
0248                 throw new Zend_Markup_Renderer_Exception(
0249                         'No alias was provided but tag was defined as such');
0250             }
0251 
0252             $this->_markups[$name] = array(
0253                 'type' => self::TYPE_ALIAS,
0254                 'name' => $options['name']
0255             );
0256         } else {
0257             if ($type && array_key_exists('empty', $options) && $options['empty']) {
0258                 // add a single replace markup
0259                 $options['type']   = $type;
0260                 $options['filter'] = $filter;
0261 
0262                 $this->_markups[$name] = $options;
0263             } else {
0264                 // add a replace markup
0265                 $options['type']   = $type;
0266                 $options['filter'] = $filter;
0267 
0268                 $this->_markups[$name] = $options;
0269             }
0270         }
0271         return $this;
0272     }
0273 
0274     /**
0275      * Remove a markup
0276      *
0277      * @param string $name
0278      *
0279      * @return void
0280      */
0281     public function removeMarkup($name)
0282     {
0283         unset($this->_markups[$name]);
0284     }
0285 
0286     /**
0287      * Remove the default tags
0288      *
0289      * @return void
0290      */
0291     public function clearMarkups()
0292     {
0293         $this->_markups = array();
0294     }
0295 
0296     /**
0297      * Render function
0298      *
0299      * @param  Zend_Markup_TokenList|string $tokenList
0300      * @return string
0301      */
0302     public function render($value)
0303     {
0304         if ($value instanceof Zend_Markup_TokenList) {
0305             $tokenList = $value;
0306         } else {
0307             $tokenList = $this->getParser()->parse($value);
0308         }
0309 
0310         $root = $tokenList->current();
0311 
0312         $this->_filter = $this->getDefaultFilter();
0313 
0314         return $this->_render($root);
0315     }
0316 
0317     /**
0318      * Render a single token
0319      *
0320      * @param  Zend_Markup_Token $token
0321      * @return string
0322      */
0323     protected function _render(Zend_Markup_Token $token)
0324     {
0325         $return    = '';
0326 
0327         $this->_token = $token;
0328 
0329         // if this tag has children, execute them
0330         if ($token->hasChildren()) {
0331             foreach ($token->getChildren() as $child) {
0332                 $return .= $this->_execute($child);
0333             }
0334         }
0335 
0336         return $return;
0337     }
0338 
0339     /**
0340      * Get the group of a token
0341      *
0342      * @param  Zend_Markup_Token $token
0343      * @return string|bool
0344      */
0345     protected function _getGroup(Zend_Markup_Token $token)
0346     {
0347         if (!isset($this->_markups[$token->getName()])) {
0348             return false;
0349         }
0350 
0351         $tag = $this->_markups[$token->getName()];
0352 
0353         // alias processing
0354         while ($tag['type'] & self::TYPE_ALIAS) {
0355             $tag = $this->_markups[$tag['name']];
0356         }
0357 
0358         return isset($tag['group']) ? $tag['group'] : false;
0359     }
0360 
0361     /**
0362      * Execute the token
0363      *
0364      * @param  Zend_Markup_Token $token
0365      * @return string
0366      */
0367     protected function _execute(Zend_Markup_Token $token)
0368     {
0369         // first return the normal text tags
0370         if ($token->getType() == Zend_Markup_Token::TYPE_NONE) {
0371             return $this->_filter($token->getTag());
0372         }
0373 
0374         // if the token doesn't have a notation, return the plain text
0375         if (!isset($this->_markups[$token->getName()])) {
0376             $oldToken  = $this->_token;
0377             $return = $this->_filter($token->getTag()) . $this->_render($token) . $token->getStopper();
0378             $this->_token = $oldToken;
0379             return $return;
0380         }
0381 
0382         $name   = $this->_getMarkupName($token);
0383         $markup = (!$name) ? false : $this->_markups[$name];
0384         $empty  = (is_array($markup) && array_key_exists('empty', $markup) && $markup['empty']);
0385 
0386         // check if the tag has content
0387         if (!$empty && !$token->hasChildren()) {
0388             return '';
0389         }
0390 
0391         // check for the context
0392         if (is_array($markup) && !in_array($markup['group'], $this->_groups[$this->_group])) {
0393             $oldToken = $this->_token;
0394             $return   = $this->_filter($token->getTag()) . $this->_render($token) . $token->getStopper();
0395             $this->_token = $oldToken;
0396             return $return;
0397         }
0398 
0399         // check for the filter
0400         if (!isset($markup['filter'])
0401             || (!($markup['filter'] instanceof Zend_Filter_Interface) && ($markup['filter'] !== false))) {
0402             $this->_markups[$name]['filter'] = $this->getDefaultFilter();
0403         }
0404 
0405         // save old values to reset them after the work is done
0406         $oldFilter = $this->_filter;
0407         $oldGroup  = $this->_group;
0408 
0409         $return = '';
0410 
0411         // set the filter and the group
0412         $this->_filter = $this->getFilter($name);
0413 
0414         if ($group = $this->_getGroup($token)) {
0415             $this->_group = $group;
0416         }
0417 
0418         // callback
0419         if (is_array($markup) && ($markup['type'] & self::TYPE_CALLBACK)) {
0420             // load the callback if the tag doesn't exist
0421             if (!($markup['callback'] instanceof Zend_Markup_Renderer_TokenConverterInterface)) {
0422                 $class = $this->getPluginLoader()->load($name);
0423 
0424                 $markup['callback'] = new $class;
0425 
0426                 if (!($markup['callback'] instanceof Zend_Markup_Renderer_TokenConverterInterface)) {
0427                     // require_once 'Zend/Markup/Renderer/Exception.php';
0428                     throw new Zend_Markup_Renderer_Exception("Callback for tag '$name' found, but it isn't valid.");
0429                 }
0430 
0431                 if (method_exists($markup['callback'], 'setRenderer')) {
0432                     $markup['callback']->setRenderer($this);
0433                 }
0434             }
0435             if ($markup['type'] && !$empty) {
0436                 $return = $markup['callback']->convert($token, $this->_render($token));
0437             } else {
0438                 $return = $markup['callback']->convert($token, null);
0439             }
0440         } else {
0441             // replace
0442             if ($markup['type'] && !$empty) {
0443                 $return = $this->_executeReplace($token, $markup);
0444             } else {
0445                 $return = $this->_executeSingleReplace($token, $markup);
0446             }
0447         }
0448 
0449         // reset to the old values
0450         $this->_filter = $oldFilter;
0451         $this->_group  = $oldGroup;
0452 
0453         return $return;
0454     }
0455 
0456     /**
0457      * Filter method
0458      *
0459      * @param string $value
0460      *
0461      * @return string
0462      */
0463     protected function _filter($value)
0464     {
0465         if ($this->_filter instanceof Zend_Filter_Interface) {
0466             return $this->_filter->filter($value);
0467         }
0468         return $value;
0469     }
0470 
0471     /**
0472      * Get the markup name
0473      *
0474      * @param Zend_Markup_Token
0475      *
0476      * @return string
0477      */
0478     protected function _getMarkupName(Zend_Markup_Token $token)
0479     {
0480         $name = $token->getName();
0481         if (empty($name)) {
0482             return false;
0483         }
0484 
0485         return $this->_resolveMarkupName($name);
0486     }
0487 
0488     /**
0489      * Resolve aliases for a markup name
0490      *
0491      * @param string $name
0492      *
0493      * @return string
0494      */
0495     protected function _resolveMarkupName($name)
0496     {
0497         while (($type = $this->_getMarkupType($name))
0498                && ($type & self::TYPE_ALIAS)
0499         ) {
0500             $name = $this->_markups[$name]['name'];
0501         }
0502 
0503         return $name;
0504     }
0505 
0506     /**
0507      * Retrieve markup type
0508      *
0509      * @param  string $name
0510      * @return false|int
0511      */
0512     protected function _getMarkupType($name)
0513     {
0514         if (!isset($this->_markups[$name])) {
0515             return false;
0516         }
0517         if (!isset($this->_markups[$name]['type'])) {
0518             return false;
0519         }
0520         return $this->_markups[$name]['type'];
0521     }
0522 
0523     /**
0524      * Execute a replace token
0525      *
0526      * @param  Zend_Markup_Token $token
0527      * @param  array $tag
0528      * @return string
0529      */
0530     protected function _executeReplace(Zend_Markup_Token $token, $tag)
0531     {
0532         return $tag['start'] . $this->_render($token) . $tag['end'];
0533     }
0534 
0535     /**
0536      * Execute a single replace token
0537      *
0538      * @param  Zend_Markup_Token $token
0539      * @param  array $tag
0540      * @return string
0541      */
0542     protected function _executeSingleReplace(Zend_Markup_Token $token, $tag)
0543     {
0544         return $tag['replace'];
0545     }
0546 
0547     /**
0548      * Get the default filter
0549      *
0550      * @return void
0551      */
0552     public function getDefaultFilter()
0553     {
0554         if (null === $this->_defaultFilter) {
0555             $this->addDefaultFilters();
0556         }
0557 
0558         return $this->_defaultFilter;
0559     }
0560 
0561     /**
0562      * Add a default filter
0563      *
0564      * @param string $filter
0565      *
0566      * @return void
0567      */
0568     public function addDefaultFilter(Zend_Filter_Interface $filter, $placement = Zend_Filter::CHAIN_APPEND)
0569     {
0570         if (!($this->_defaultFilter instanceof Zend_Filter)) {
0571             $defaultFilter = new Zend_Filter();
0572             $defaultFilter->addFilter($filter);
0573             $this->_defaultFilter = $defaultFilter;
0574         }
0575 
0576         $this->_defaultFilter->addFilter($filter, $placement);
0577     }
0578 
0579     /**
0580      * Set the default filter
0581      *
0582      * @param Zend_Filter_Interface $filter
0583      *
0584      * @return void
0585      */
0586     public function setDefaultFilter(Zend_Filter_Interface $filter)
0587     {
0588         $this->_defaultFilter = $filter;
0589     }
0590 
0591     /**
0592      * Get the filter for an existing markup
0593      *
0594      * @param string $markup
0595      *
0596      * @return Zend_Filter_Interface
0597      */
0598     public function getFilter($markup)
0599     {
0600         $markup = $this->_resolveMarkupName($markup);
0601 
0602         if (!isset($this->_markups[$markup]['filter'])
0603             || !($this->_markups[$markup]['filter'] instanceof Zend_Filter_Interface)
0604         ) {
0605             if (isset($this->_markups[$markup]['filter']) && $this->_markups[$markup]['filter']) {
0606                 $this->_markups[$markup]['filter'] = $this->getDefaultFilter();
0607             } else {
0608                 return false;
0609             }
0610         }
0611 
0612         return $this->_markups[$markup]['filter'];
0613     }
0614 
0615     /**
0616      * Add a filter for an existing markup
0617      *
0618      * @param Zend_Filter_Interface $filter
0619      * @param string $markup
0620      * @param string $placement
0621      *
0622      * @return Zend_Markup_Renderer_RendererAbstract
0623      */
0624     public function addFilter(Zend_Filter_Interface $filter, $markup, $placement = Zend_Filter::CHAIN_APPEND)
0625     {
0626         $markup = $this->_resolveMarkupName($markup);
0627 
0628         $oldFilter = $this->getFilter($markup);
0629 
0630         // if this filter is the default filter, clone it first
0631         if ($oldFilter === $this->getDefaultFilter()) {
0632             $oldFilter = clone $oldFilter;
0633         }
0634 
0635         if (!($oldFilter instanceof Zend_Filter)) {
0636             $this->_markups[$markup]['filter'] = new Zend_Filter();
0637 
0638             if ($oldFilter instanceof Zend_Filter_Interface) {
0639                 $this->_markups[$markup]['filter']->addFilter($oldFilter);
0640             }
0641         } else {
0642             $this->_markups[$markup]['filter'] = $oldFilter;
0643         }
0644 
0645         $this->_markups[$markup]['filter']->addFilter($filter, $placement);
0646 
0647         return $this;
0648     }
0649 
0650     /**
0651      * Set the filter for an existing
0652      *
0653      * @param Zend_Filter_Interface $filter
0654      * @param string $markup
0655      *
0656      * @return Zend_Markup_Renderer_RendererAbstract
0657      */
0658     public function setFilter(Zend_Filter_Interface $filter, $markup)
0659     {
0660         $markup = $this->_resolveMarkupName($markup);
0661 
0662         $this->_markups[$markup]['filter'] = $filter;
0663 
0664         return $this;
0665     }
0666 
0667     /**
0668      * Add a render group
0669      *
0670      * @param string $name
0671      * @param array $allowedInside
0672      * @param array $allowsInside
0673      *
0674      * @return void
0675      */
0676     public function addGroup($name, array $allowedInside = array(), array $allowsInside = array())
0677     {
0678         $this->_groups[$name] = $allowsInside;
0679 
0680         foreach ($allowedInside as $group) {
0681             $this->_groups[$group][] = $name;
0682         }
0683     }
0684 
0685     /**
0686      * Get group definitions
0687      *
0688      * @return array
0689      */
0690     public function getGroups()
0691     {
0692         return $this->_groups;
0693     }
0694 
0695     /**
0696      * Set the default filters
0697      *
0698      * @return void
0699      */
0700     abstract public function addDefaultFilters();
0701 
0702 }