PageRenderTime 999ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/src/DocBlox/Transformer/Behaviour/Inherit/Node/Abstract.php

https://github.com/androa/Docblox
PHP | 264 lines | 123 code | 29 blank | 112 comment | 16 complexity | 99e0fda3a6f0d812a8c9279d256b833c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * DocBlox
  4. *
  5. * PHP Version 5
  6. *
  7. * @category DocBlox
  8. * @package Transformer
  9. * @subpackage Behaviour
  10. * @author Mike van Riel <mike.vanriel@naenius.com>
  11. * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT
  13. * @link http://docblox-project.org
  14. */
  15. /**
  16. * Base class for adding inheritance to an element.
  17. *
  18. * @category DocBlox
  19. * @package Transformer
  20. * @subpackage Behaviour
  21. * @author Mike van Riel <mike.vanriel@naenius.com>
  22. * @license http://www.opensource.org/licenses/mit-license.php MIT
  23. * @link http://docblox-project.org
  24. */
  25. abstract class DocBlox_Transformer_Behaviour_Inherit_Node_Abstract
  26. {
  27. /** @var DOMElement */
  28. protected $node = null;
  29. /** @var string[] All class tags that are inherited when none are defined */
  30. protected $inherited_tags = array(
  31. 'version',
  32. 'copyright',
  33. 'author'
  34. );
  35. /**
  36. * Initialize the inheritance for this node.
  37. *
  38. * @param DOMElement $node
  39. */
  40. public function __construct(DOMElement $node)
  41. {
  42. $this->node = $node;
  43. }
  44. /**
  45. * Returns the name of the given method or property node.
  46. *
  47. * @return string
  48. */
  49. protected function getNodeName()
  50. {
  51. return current($this->getDirectElementsByTagName($this->node, 'name'))
  52. ->nodeValue;
  53. }
  54. /**
  55. * Returns the docblock element for the given node; if none exists it will
  56. * be added.
  57. *
  58. * @return DOMElement
  59. */
  60. protected function getDocBlockElement()
  61. {
  62. $docblocks = $this->getDirectElementsByTagName($this->node, 'docblock');
  63. // if this method does not yet have a docblock; add one; even
  64. // though DocBlox throws a warning about a missing DocBlock!
  65. if (count($docblocks) < 1) {
  66. $docblock = new DOMElement('docblock');
  67. $this->node->appendChild($docblock);
  68. } else {
  69. /** @var DOMElement $docblock */
  70. $docblock = reset($docblocks);
  71. }
  72. return $docblock;
  73. }
  74. /**
  75. * Returns the elements with the given tag name that can be found
  76. * as direct children of $node.
  77. *
  78. * getElementsByTagName returns all DOMElements with the given tag name
  79. * regardless where in the DOM subtree they are. This method checks whether
  80. * the parent node matches the given node and thus determines whether it is
  81. * a direct child.
  82. *
  83. * @param DOMElement $node
  84. * @param string $element_name
  85. *
  86. * @return DOMElement[]
  87. */
  88. protected function getDirectElementsByTagName(DOMElement $node, $element_name)
  89. {
  90. $result = array();
  91. $elements = $node->getElementsByTagName($element_name);
  92. for($i = 0; $i < $elements->length; $i++)
  93. {
  94. if ($elements->item($i)->parentNode !== $node)
  95. {
  96. continue;
  97. }
  98. $result[] = $elements->item($i);
  99. }
  100. return $result;
  101. }
  102. /**
  103. * Copies the short description from the Super element's DocBlock to the
  104. * Sub element's DocBlock if the sub element has none.
  105. *
  106. * @param DOMElement $super_docblock
  107. * @param DOMElement $docblock
  108. *
  109. * @return void
  110. */
  111. public function copyShortDescription(DOMElement $super_docblock,
  112. DOMElement $docblock)
  113. {
  114. /** @var DOMElement $desc */
  115. $desc = current($this->getDirectElementsByTagName($docblock, 'description'));
  116. $super_desc = current(
  117. $this->getDirectElementsByTagName($super_docblock, 'description')
  118. );
  119. if ((($desc === false) || (!trim($desc->nodeValue)))
  120. && ($super_desc !== false)
  121. ) {
  122. if ($desc !== false) {
  123. $docblock->removeChild($desc);
  124. }
  125. $docblock->appendChild(clone $super_desc);
  126. }
  127. }
  128. /**
  129. * Copies the long description from the Super element's DocBlock to the
  130. * Sub element's DocBlock if the sub element has none.
  131. *
  132. * @param DOMElement $super_docblock
  133. * @param DOMElement $docblock
  134. *
  135. * @return void
  136. */
  137. public function copyLongDescription(DOMElement $super_docblock,
  138. DOMElement $docblock)
  139. {
  140. /** @var DOMElement $desc */
  141. $desc = current(
  142. $this->getDirectElementsByTagName($docblock, 'long-description')
  143. );
  144. $super_desc = current(
  145. $this->getDirectElementsByTagName($super_docblock, 'long-description')
  146. );
  147. if ((($desc === false) || (!trim($desc->nodeValue)))
  148. && ($super_desc !== false)
  149. ) {
  150. if ($desc !== false) {
  151. $docblock->removeChild($desc);
  152. }
  153. $docblock->appendChild(clone $super_desc);
  154. }
  155. }
  156. /**
  157. * Copies the tags from the super docblock to this one if it matches
  158. * the criteria.
  159. *
  160. * Criteria for copying are:
  161. *
  162. * * Tag name must be in the list of to-be-copied tag names
  163. * * No tag with that name may be in the sub element
  164. *
  165. * @param string[] $tag_types array of to-be-copied tag names.
  166. * @param DOMElement $super_docblock DocBlock of the super element.
  167. * @param DOMElement $docblock DocBlock of the sub element.
  168. *
  169. * @return void
  170. */
  171. protected function copyTags(array $tag_types, DOMElement $super_docblock,
  172. DOMElement $docblock)
  173. {
  174. // get the names of all existing tags because we should only add
  175. // parent tags if there are none in the existing docblock
  176. $existing_tag_names = array();
  177. foreach ($this->getDirectElementsByTagName($docblock, 'tag') as $tag)
  178. {
  179. $existing_tag_names[] = $tag->getAttribute('name');
  180. }
  181. $existing_tag_names = array_unique($existing_tag_names);
  182. /** @var DOMElement $tag */
  183. foreach ($this->getDirectElementsByTagName($super_docblock, 'tag') as $tag) {
  184. $tag_name = $tag->getAttribute('name');
  185. if (in_array($tag_name, $tag_types)
  186. && (!in_array($tag_name, $existing_tag_names))
  187. ) {
  188. $child = clone $tag;
  189. $child->setAttribute('line', $this->node->getAttribute('line'));
  190. $docblock->appendChild($child);
  191. }
  192. }
  193. }
  194. /**
  195. * Combines the docblock of an overridden method with this one if applicable.
  196. *
  197. * @param mixed[] $super Array containing a flat list of methods for
  198. * a tree of inherited classes.
  199. * @param string $class_name Name of the current class.
  200. *
  201. * @return void
  202. */
  203. public function apply(array &$super, $class_name)
  204. {
  205. // the name is always the first encountered child element with
  206. // tag name 'name'
  207. $node_name = $this->getNodeName();
  208. // only process if the super has a node with this name
  209. if (isset($super[$node_name])) {
  210. $docblock = $this->getDocBlockElement();
  211. /** @var DOMElement $super_object */
  212. $super_object = $super[$node_name]['object'];
  213. /** @var DOMElement $super_docblock */
  214. $super_docblock = current($this->getDirectElementsByTagName(
  215. $super_object, 'docblock'
  216. ));
  217. $super_class = current($this->getDirectElementsByTagName(
  218. $super_object->parentNode, 'full_name'
  219. ))->nodeValue;
  220. // add an element which defines which class' element you override
  221. $this->node->appendChild(new DOMElement('overrides-from', $super_class));
  222. $this->copyShortDescription($super_docblock, $docblock);
  223. $this->copyLongDescription($super_docblock, $docblock);
  224. $this->copyTags($this->inherited_tags, $super_docblock, $docblock);
  225. }
  226. // only add if this has a docblock; otherwise it is useless
  227. $docblocks = $this->getDirectElementsByTagName($this->node, 'docblock');
  228. if (count($docblocks) > 0) {
  229. $super[$node_name] = array(
  230. 'class' => $class_name,
  231. 'object' => $this->node
  232. );
  233. }
  234. }
  235. }