PageRenderTime 25ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/test/distributions/opt/2.0.6/lib/Opt/Instruction/Attribute.php

https://github.com/rodneyrehm/php-template-engines
PHP | 324 lines | 235 code | 24 blank | 65 comment | 42 complexity | 4c27ff71dd79f5a2a7b25902a6012be8 MD5 | raw file
  1. <?php
  2. /*
  3. * OPEN POWER LIBS <http://www.invenzzia.org>
  4. *
  5. * This file is subject to the new BSD license that is bundled
  6. * with this package in the file LICENSE. It is also available through
  7. * WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
  8. *
  9. * Copyright (c) Invenzzia Group <http://www.invenzzia.org>
  10. * and other contributors. See website for details.
  11. *
  12. */
  13. class Opt_Instruction_Attribute extends Opt_Compiler_Processor
  14. {
  15. // Display the attribute values
  16. const ATTR_DISPLAY = 0;
  17. // Keep raw expressions, because they will be processed later.
  18. const ATTR_RAW = 1;
  19. protected $_name = 'attribute';
  20. // A counter to create unique fake attribute names
  21. static private $_cnt = 0;
  22. /**
  23. * Registers opt:attribute tag and opt:single attribute.
  24. */
  25. public function configure()
  26. {
  27. $this->_addInstructions(array('opt:attribute'));
  28. $this->_addAttributes(array('opt:attributes-build', 'opt:attributes-ignore'));
  29. } // end configure();
  30. /**
  31. * Processes the opt:attribute instruction tag.
  32. *
  33. * @param Opt_Xml_Node $node XML node.
  34. */
  35. public function processNode(Opt_Xml_Node $node)
  36. {
  37. $params = array(
  38. 'name' => array(0 => self::REQUIRED, self::EXPRESSION),
  39. 'value' => array(0 => self::OPTIONAL, self::EXPRESSION, null),
  40. 'ns' => array(0 => self::OPTIONAL, self::EXPRESSION, null),
  41. );
  42. $this->_extractAttributes($node, $params);
  43. self::$_cnt++;
  44. $parent = $node->getParent();
  45. $returnStyle = $node->get('attributeValueStyle');
  46. $returnStyle = (is_null($returnStyle) ? self::ATTR_DISPLAY : $returnStyle);
  47. if($returnStyle == self::ATTR_DISPLAY)
  48. {
  49. if(!$node->getParent() instanceof Opt_Xml_Element)
  50. {
  51. throw new Opt_InstructionInvalidParent_Exception('opt:attribute', 'printable tag');
  52. }
  53. $parentName = $node->getParent()->getXmlName();
  54. if(($this->_compiler->isInstruction($parentName) || $this->_compiler->isComponent($parentName) || $this->_compiler->isBlock($parentName)) && $node->getParent()->get('call:attribute-friendly') === null)
  55. {
  56. throw new Opt_InstructionInvalidParent_Exception('opt:attribute', 'printable tag');
  57. }
  58. // This is a bit tricky optimization. If the name is constant, there is no need to process it as a variable name.
  59. // If the name is constant, the result must contain only a string
  60. if($params['ns'] !== null)
  61. {
  62. $trNamespace = trim($params['ns'], '\' ');
  63. if(!(substr_count($params['ns'], '\'') == 2 && substr_count($trNamespace, '\'') == 0 && $this->_compiler->isIdentifier($trNamespace)))
  64. {
  65. unset($trNamespace);
  66. }
  67. }
  68. // Using the same tricky optimization for names
  69. $trName = trim($params['name'], '\' ');
  70. if(!(substr_count($params['name'], '\'') == 2 && substr_count($trName, '\'') == 0 && $this->_compiler->isIdentifier($trName)))
  71. {
  72. unset($trName);
  73. }
  74. if((isset($trName) && $params['ns'] === null) || (isset($trName) && isset($trNamespace)))
  75. {
  76. $attribute = new Opt_Xml_Attribute($trName, $params['value']);
  77. if(isset($trNamespace))
  78. {
  79. $attribute->setNamespace($trNamespace);
  80. }
  81. }
  82. else
  83. {
  84. $attribute = new Opt_Xml_Attribute('__xattr_'.self::$_cnt, $params['value']);
  85. if(isset($trNamespace))
  86. {
  87. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_NAME, 'echo \''.$trNamespace.':\'.'.$params['name'].'; ');
  88. }
  89. elseif($params['ns'] !== null)
  90. {
  91. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_NAME, ' $_ns = '.$params['ns'].'; echo (!empty($_ns) ? $_ns.\':\' : \'\').'.$params['name'].'; ');
  92. }
  93. else
  94. {
  95. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_NAME, 'echo '.$params['name'].'; ');
  96. }
  97. }
  98. // Construct the value for the attribute.
  99. if($node->hasChildren())
  100. {
  101. // The more complex statement with opt:value nodes...
  102. list($pairs, $else) = $this->_getValuePairs($node, $params);
  103. // Now, create the IF...ELSEIF statement
  104. // We perform here a small optimization. If the "ELSE" statement is set
  105. // the value will always appear, so we can put this code directly in ATTRIBUTE_VALUE
  106. // and do not bother with temporary variables.
  107. if($else !== null)
  108. {
  109. $destination = 'echo';
  110. $code = '';
  111. }
  112. else
  113. {
  114. $destination = '$_attr'.self::$_cnt.'_val = ';
  115. $code = '$_attr'.self::$_cnt.'_val = null; ';
  116. }
  117. $start = true;
  118. foreach($pairs as $pair)
  119. {
  120. if($start)
  121. {
  122. $code = ' if('.$pair[0].'){ '.$destination.' '.$pair[1].'; }';
  123. $start = false;
  124. }
  125. else
  126. {
  127. $code .= 'elseif('.$pair[0].'){ '.$destination.' '.$pair[1].'; }';
  128. }
  129. }
  130. if($else !== null)
  131. {
  132. $code .= 'else{ '.$destination.' '.$else.'; } ';
  133. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_VALUE, $code);
  134. }
  135. else
  136. {
  137. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_BEGIN, $code.' if($_attr'.self::$_cnt.'_val !== null){ ');
  138. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_VALUE, ' echo $_attr'.self::$_cnt.'_val; ');
  139. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_END, ' } ');
  140. }
  141. }
  142. else
  143. {
  144. // The ordinary behaviour
  145. if($params['value'] === null)
  146. {
  147. throw new Opt_AttributeNotDefined_Exception('value', $node->getXmlName());
  148. }
  149. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_VALUE, 'echo '.$params['value'].'; ');
  150. }
  151. }
  152. else
  153. {
  154. // In the raw mode, we simply put the raw expressions, because they will be processed
  155. // later by another instruction processor.
  156. $attribute = new Opt_Xml_Attribute('__xattr_'.self::$_cnt++, $params['value']);
  157. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_NAME, $params['name']);
  158. // Construct the value for the attribute.
  159. if($node->hasChildren())
  160. {
  161. // The more complex statement with opt:value nodes...
  162. $attribute->set('call:values', $this->_getValuePairs($node, $params));
  163. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_VALUE, '');
  164. }
  165. else
  166. {
  167. // The ordinary behaviour
  168. $attribute->addAfter(Opt_Xml_Buffer::ATTRIBUTE_VALUE, $params['value']);
  169. }
  170. if($params['ns'] !== null)
  171. {
  172. $attribute->set('priv:namespace', $params['ns']);
  173. }
  174. }
  175. $node->set('priv:attr', $attribute);
  176. $node->set('postprocess', true);
  177. // Add the newly created attribute to the list of dynamic attributes in the parent tag.
  178. // If the list does not exist, then create it.
  179. if(!is_null($list = $parent->get('call:attribute')))
  180. {
  181. array_push($list, $attribute);
  182. $parent->set('call:attribute', $list);
  183. }
  184. else
  185. {
  186. $parent->set('call:attribute', array(0 => $attribute));
  187. }
  188. // Check, if such attribute does not exist...
  189. if($parent->getAttribute($attribute->getXmlName()) !== null)
  190. {
  191. throw new Opt_XmlDuplicatedAttribute_Exception($attribute->getXmlName(), $parent->getXmlName());
  192. }
  193. $parent->addAttribute($attribute);
  194. $parent->removeChild($node);
  195. } // end processNode();
  196. /**
  197. * Postprocesses the opt:attribute instruction tag.
  198. *
  199. * @param Opt_Xml_Node $node XML node.
  200. */
  201. public function postprocessNode(Opt_Xml_Node $node)
  202. {
  203. // We must copy the buffers here, because the instruction might have some attributes
  204. // which may also use "postprocess" to generate their code. Here we are sure they've completed
  205. // their work.
  206. $attribute = $node->get('priv:attr');
  207. $attribute->copyBuffer($node, Opt_Xml_Buffer::TAG_BEFORE, Opt_Xml_Buffer::ATTRIBUTE_BEGIN);
  208. $attribute->copyBuffer($node, Opt_Xml_Buffer::TAG_AFTER, Opt_Xml_Buffer::ATTRIBUTE_END);
  209. $node->set('priv:attr', null);
  210. } // end postprocessNode();
  211. /**
  212. * Processes the opt:attributes-build and opt:attributes-ignore attributes.
  213. * @param Opt_Xml_Element $node The node
  214. * @param Opt_Xml_Attribute $attr The attribute to process
  215. */
  216. public function processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
  217. {
  218. if($attr->getName() == 'attributes-build')
  219. {
  220. $ignoreList = $node->getAttribute('opt:attributes-ignore');
  221. if($ignoreList instanceof Opt_Xml_Attribute)
  222. {
  223. $ignore = $this->_compiler->compileExpression($ignoreList->getValue(), false, Opt_Compiler_Class::ESCAPE_OFF);
  224. $ignore = $ignore[0];
  225. }
  226. else
  227. {
  228. $ignore = 'array()';
  229. }
  230. $expression = $this->_compiler->compileExpression($attr->getValue(), false, Opt_Compiler_Class::ESCAPE_OFF);
  231. $node->addAfter(Opt_Xml_Buffer::TAG_ENDING_ATTRIBUTES, 'echo Opt_Function::buildAttributes('.$expression[0].', '.$ignore.', \' \'); ');
  232. }
  233. } // end processAttribute();
  234. /**
  235. * Returns the concatenated elements of opt:value
  236. *
  237. * @internal
  238. * @param Opt_Xml_Element $node The node to scan.
  239. * @param array $params The node parameters.
  240. * @return array
  241. */
  242. private function _getValuePairs(Opt_Xml_Element $node, array $params)
  243. {
  244. // The more sophisticated behaviour.
  245. $tags = $node->getElementsByTagNameNS('opt', 'value', false);
  246. $pairs = new SplQueue;
  247. $else = null;
  248. if(isset($params['value']))
  249. {
  250. $else = $params['value'];
  251. }
  252. // Pack the tags into the PHP code.
  253. foreach($tags as $tag)
  254. {
  255. if($tag->countChildren() > 1)
  256. {
  257. throw new Opt_InvalidValue_Exception('opt:value');
  258. }
  259. if(!($content = $tag->getLastChild()) instanceof Opt_Xml_Text)
  260. {
  261. throw new Opt_InvalidValue_Exception('opt:value');
  262. }
  263. // Concatenate the tag content into an expression
  264. $code = array();
  265. foreach($content as $items)
  266. {
  267. if($items instanceof Opt_Xml_Cdata)
  268. {
  269. $code[] = '\''.(string)$items.'\'';
  270. }
  271. elseif($items instanceof Opt_Xml_Expression)
  272. {
  273. $result = $this->_compiler->compileExpression($items->getExpression(), false, Opt_Compiler_Class::ESCAPE_OFF);
  274. $code[] = $result[0];
  275. }
  276. }
  277. $code = $this->_compiler->escape(implode('.', $code));
  278. // Decide, what to do (final alternative or not...)
  279. if(($condition = $tag->getAttribute('test')) === null)
  280. {
  281. if($else !== null)
  282. {
  283. throw new Opt_AttributeNotDefined_Exception('test', 'opt:value');
  284. }
  285. $else = $code;
  286. }
  287. else
  288. {
  289. $result = $this->_compiler->compileExpression($condition, true, Opt_Compiler_Class::ESCAPE_OFF);
  290. $pairs->enqueue(
  291. array(
  292. $result[0],
  293. $code
  294. )
  295. );
  296. }
  297. }
  298. return array($pairs, $else);
  299. } // end _getValuePairs();
  300. } // end Opt_Instruction_Attribute;