RendererAbstract.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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_Markup
  17. * @subpackage Renderer
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_config
  24. */
  25. require_once 'Zend/Config.php';
  26. /**
  27. * @see Zend_Filter
  28. */
  29. require_once 'Zend/Filter.php';
  30. /**
  31. * @see Zend_Markup_Renderer_TokenConverterInterface
  32. */
  33. require_once 'Zend/Markup/Renderer/TokenConverterInterface.php';
  34. /**
  35. * Defines the basic rendering functionality
  36. *
  37. * @category Zend
  38. * @package Zend_Markup
  39. * @subpackage Renderer
  40. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  41. * @license http://framework.zend.com/license/new-bsd New BSD License
  42. */
  43. abstract class Zend_Markup_Renderer_RendererAbstract
  44. {
  45. const TAG_SINGLE = 1;
  46. const TAG_NORMAL = 2;
  47. const TYPE_CALLBACK = 4;
  48. const TYPE_REPLACE = 8;
  49. const TYPE_ALIAS = 16;
  50. /**
  51. * Tag info
  52. *
  53. * @var array
  54. */
  55. protected $_tags = array();
  56. /**
  57. * Parser
  58. *
  59. * @var Zend_Markup_Parser_ParserInterface
  60. */
  61. protected $_parser;
  62. /**
  63. * Use the filter or not
  64. *
  65. * @var bool
  66. */
  67. protected $_filter = true;
  68. /**
  69. * Filter chain
  70. *
  71. * @var Zend_Filter
  72. */
  73. protected $_filterChain;
  74. /**
  75. * The current group
  76. *
  77. * @var string
  78. */
  79. protected $_group;
  80. /**
  81. * Groups definition
  82. *
  83. * @var array
  84. */
  85. protected $_groups = array();
  86. /**
  87. * Plugin loader for tags
  88. *
  89. * @var Zend_Loader_PluginLoader
  90. */
  91. protected $_pluginLoader;
  92. /**
  93. * The current token
  94. *
  95. * @var Zend_Markup_Token
  96. */
  97. protected $_token;
  98. /**
  99. * Constructor
  100. *
  101. * @param array|Zend_Config $options
  102. *
  103. * @return void
  104. */
  105. public function __construct($options = array())
  106. {
  107. if ($options instanceof Zend_Config) {
  108. $options = $options->toArray();
  109. }
  110. if (isset($options['parser'])) {
  111. $this->setParser($options['parser']);
  112. }
  113. if (isset($options['prependFilter'])) {
  114. $this->addFilter($options['prependFilter']);
  115. }
  116. if (isset($options['useDefaultTags']) && ($options['useDefaultTags'] === false)) {
  117. $this->removeDefaultTags();
  118. }
  119. if (!isset($options['useDefaultFilters']) || ($options['useDefaultFilters'] === true)) {
  120. $this->addDefaultFilters();
  121. }
  122. if (isset($options['filter'])) {
  123. $this->addFilter($options['filter']);
  124. }
  125. if (isset($options['appendFilter'])) {
  126. $this->addFilter($options['appendFilter']);
  127. }
  128. }
  129. /**
  130. * Set the parser
  131. *
  132. * @param Zend_Markup_Parser_ParserInterface $parser
  133. * @return Zend_Markup_Renderer_RendererAbstract
  134. */
  135. public function setParser(Zend_Markup_Parser_ParserInterface $parser)
  136. {
  137. $this->_parser = $parser;
  138. return $this;
  139. }
  140. /**
  141. * Get the parser
  142. *
  143. * @return Zend_Markup_Parser_ParserInterface
  144. */
  145. public function getParser()
  146. {
  147. return $this->_parser;
  148. }
  149. /**
  150. * Get the plugin loader
  151. *
  152. * @return Zend_Loader_PluginLoader
  153. */
  154. public function getPluginLoader()
  155. {
  156. return $this->_pluginLoader;
  157. }
  158. /**
  159. * Get the filter chain
  160. *
  161. * @return Zend_Filter
  162. */
  163. public function getFilterChain()
  164. {
  165. if (null === $this->_filterChain) {
  166. $this->_filterChain = new Zend_Filter();
  167. }
  168. return $this->_filterChain;
  169. }
  170. /**
  171. * Add a filter
  172. *
  173. * @param Zend_Filter_Interface $filter
  174. * @param string $filter
  175. *
  176. * @return void
  177. */
  178. public function addFilter(Zend_Filter_Interface $filter, $placement = Zend_Filter::CHAIN_APPEND)
  179. {
  180. $this->getFilterChain()->addFilter($filter, $placement);
  181. }
  182. /**
  183. * Add a new tag
  184. *
  185. * @param string $name
  186. * @param string $type
  187. * @param array $info
  188. *
  189. * @return Zend_Markup_Renderer_RendererAbstract
  190. */
  191. public function addTag($name, $type, array $info)
  192. {
  193. if (!isset($info['group']) && ($type ^ self::TYPE_ALIAS)) {
  194. require_once 'Zend/Markup/Renderer/Exception.php';
  195. throw new Zend_Markup_Renderer_Exception("There is no render group defined.");
  196. }
  197. if (isset($info['filter'])) {
  198. $filter = (boolean) $info['filter'];
  199. } else {
  200. $filter = true;
  201. }
  202. // check the type
  203. if ($type & self::TYPE_CALLBACK) {
  204. // add a callback tag
  205. if (isset($info['callback'])) {
  206. if (!($info['callback'] instanceof Zend_Markup_Renderer_TokenConverterInterface)) {
  207. require_once 'Zend/Markup/Renderer/Exception.php';
  208. throw new Zend_Markup_Renderer_Exception("Not a valid tag callback.");
  209. }
  210. if (method_exists($info['callback'], 'setRenderer')) {
  211. $info['callback']->setRenderer($this);
  212. }
  213. } else {
  214. $info['callback'] = null;
  215. }
  216. $info['type'] = $type;
  217. $info['filter'] = $filter;
  218. $this->_tags[$name] = $info;
  219. } elseif ($type & self::TYPE_ALIAS) {
  220. // add an alias
  221. if (empty($info['name'])) {
  222. require_once 'Zend/Markup/Renderer/Exception.php';
  223. throw new Zend_Markup_Renderer_Exception(
  224. 'No alias was provided but tag was defined as such');
  225. }
  226. $this->_tags[$name] = array(
  227. 'type' => self::TYPE_ALIAS,
  228. 'name' => $info['name']
  229. );
  230. } else {
  231. if ($type & self::TAG_SINGLE) {
  232. // add a single replace tag
  233. $info['type'] = $type;
  234. $info['filter'] = $filter;
  235. $this->_tags[$name] = $info;
  236. } else {
  237. // add a replace tag
  238. $info['type'] = $type;
  239. $info['filter'] = $filter;
  240. $this->_tags[$name] = $info;
  241. }
  242. }
  243. return $this;
  244. }
  245. /**
  246. * Remove a tag
  247. *
  248. * @param string $name
  249. *
  250. * @return void
  251. */
  252. public function removeTag($name)
  253. {
  254. unset($this->_tags[$name]);
  255. }
  256. /**
  257. * Remove the default tags
  258. *
  259. * @return void
  260. */
  261. public function clearTags()
  262. {
  263. $this->_tags = array();
  264. }
  265. /**
  266. * Render function
  267. *
  268. * @param Zend_Markup_TokenList|string $tokenList
  269. * @return string
  270. */
  271. public function render($value)
  272. {
  273. if ($value instanceof Zend_Markup_TokenList) {
  274. $tokenList = $value;
  275. } else {
  276. $tokenList = $this->getParser()->parse($value);
  277. }
  278. $root = $tokenList->current();
  279. return $this->_render($root);
  280. }
  281. /**
  282. * Render a single token
  283. *
  284. * @param Zend_Markup_Token $token
  285. * @return string
  286. */
  287. protected function _render(Zend_Markup_Token $token)
  288. {
  289. $return = '';
  290. // save old values to reset them after the work is done
  291. $oldFilter = $this->_filter;
  292. $oldGroup = $this->_group;
  293. $oldToken = $this->_token;
  294. // check filter and group usage in this tag
  295. if (isset($this->_tags[$token->getName()])) {
  296. if (isset($this->_tags[$token->getName()]['filter'])
  297. && $this->_tags[$token->getName()]['filter']
  298. ) {
  299. $this->_filter = true;
  300. } else {
  301. $this->_filter = false;
  302. }
  303. if ($group = $this->_getGroup($token)) {
  304. $this->_group = $group;
  305. }
  306. }
  307. $this->_token = $token;
  308. // if this tag has children, execute them
  309. if ($token->hasChildren()) {
  310. foreach ($token->getChildren() as $child) {
  311. $return .= $this->_execute($child);
  312. }
  313. }
  314. // reset to the old values
  315. $this->_token = $oldToken;
  316. $this->_filter = $oldFilter;
  317. $this->_group = $oldGroup;
  318. return $return;
  319. }
  320. /**
  321. * Get the group of a token
  322. *
  323. * @param Zend_Markup_Token $token
  324. * @return string|bool
  325. */
  326. protected function _getGroup(Zend_Markup_Token $token)
  327. {
  328. if (!isset($this->_tags[$token->getName()])) {
  329. return false;
  330. }
  331. $tag = $this->_tags[$token->getName()];
  332. // alias processing
  333. while ($tag['type'] & self::TYPE_ALIAS) {
  334. $tag = $this->_tags[$tag['name']];
  335. }
  336. return isset($tag['group']) ? $tag['group'] : false;
  337. }
  338. /**
  339. * Execute the token
  340. *
  341. * @param Zend_Markup_Token $token
  342. * @return string
  343. */
  344. protected function _execute(Zend_Markup_Token $token)
  345. {
  346. // first return the normal text tags
  347. if ($token->getType() == Zend_Markup_Token::TYPE_NONE) {
  348. return $this->_filter($token->getTag());
  349. }
  350. // if the token doesn't have a notation, return the plain text
  351. if (!isset($this->_tags[$token->getName()])) {
  352. return $this->_filter($token->getTag()) . $this->_render($token) . $token->getStopper();
  353. }
  354. $name = $this->_getTagName($token);
  355. $tag = $this->_tags[$name];
  356. // check if the tag has content
  357. if (($tag['type'] & self::TAG_NORMAL) && !$token->hasChildren()) {
  358. return '';
  359. }
  360. // check for the context
  361. if (!in_array($tag['group'], $this->_groups[$this->_group])) {
  362. return $this->_filter($token->getTag()) . $this->_render($token) . $token->getStopper();
  363. }
  364. // callback
  365. if ($tag['type'] & self::TYPE_CALLBACK) {
  366. // load the callback if the tag doesn't exist
  367. if (!($tag['callback'] instanceof Zend_Markup_Renderer_TokenConverterInterface)) {
  368. $class = $this->getPluginLoader()->load($name);
  369. $tag['callback'] = new $class;
  370. if (!($tag['callback'] instanceof Zend_Markup_Renderer_TokenConverterInterface)) {
  371. require_once 'Zend/Markup/Renderer/Exception.php';
  372. throw new Zend_Markup_Renderer_Exception("Callback for tag '$name' found, but it isn't valid.");
  373. }
  374. if (method_exists($tag['callback'], 'setRenderer')) {
  375. $tag['callback']->setRenderer($this);
  376. }
  377. }
  378. if ($tag['type'] & self::TAG_NORMAL) {
  379. return $tag['callback']->convert($token, $this->_render($token));
  380. }
  381. return $tag['callback']->convert($token, null);
  382. }
  383. // replace
  384. if ($tag['type'] & self::TAG_NORMAL) {
  385. return $this->_executeReplace($token, $tag);
  386. }
  387. return $this->_executeSingleReplace($token, $tag);
  388. }
  389. /**
  390. * Get the tag name
  391. *
  392. * @param Zend_Markup_Token
  393. *
  394. * @return string
  395. */
  396. protected function _getTagName(Zend_Markup_Token $token)
  397. {
  398. $name = $token->getName();
  399. // process the aliases
  400. while ($this->_tags[$name]['type'] & self::TYPE_ALIAS) {
  401. $name = $this->_tags[$name]['name'];
  402. }
  403. return $name;
  404. }
  405. /**
  406. * Filter method
  407. *
  408. * @param string $value
  409. *
  410. * @return string
  411. */
  412. protected function _filter($value)
  413. {
  414. if ($this->_filter) {
  415. return $this->getFilterChain()->filter($value);
  416. }
  417. return $value;
  418. }
  419. /**
  420. * Execute a replace token
  421. *
  422. * @param Zend_Markup_Token $token
  423. * @param array $tag
  424. * @return string
  425. */
  426. protected function _executeReplace(Zend_Markup_Token $token, $tag)
  427. {
  428. return $tag['start'] . $this->_render($token) . $tag['end'];
  429. }
  430. /**
  431. * Execute a single replace token
  432. *
  433. * @param Zend_Markup_Token $token
  434. * @param array $tag
  435. * @return string
  436. */
  437. protected function _executeSingleReplace(Zend_Markup_Token $token, $tag)
  438. {
  439. return $tag['replace'];
  440. }
  441. /**
  442. * Initialize the default filters
  443. *
  444. * @return void
  445. */
  446. abstract public function addDefaultFilters();
  447. }