HtmlNode.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. namespace PHPHtmlParser\Dom;
  3. use PHPHtmlParser\Exceptions\UnknownChildTypeException;
  4. use PHPHtmlParser\Exceptions\ChildNotFoundException;
  5. /**
  6. * Class HtmlNode
  7. *
  8. * @package PHPHtmlParser\Dom
  9. */
  10. class HtmlNode extends InnerNode
  11. {
  12. /**
  13. * Remembers what the innerHtml was if it was scanned previously.
  14. *
  15. * @var string
  16. */
  17. protected $innerHtml = null;
  18. /**
  19. * Remembers what the outerHtml was if it was scanned previously.
  20. *
  21. * @var string
  22. */
  23. protected $outerHtml = null;
  24. /**
  25. * Remembers what the text was if it was scanned previously.
  26. *
  27. * @var string
  28. */
  29. protected $text = null;
  30. /**
  31. * Remembers what the text was when we looked into all our
  32. * children nodes.
  33. *
  34. * @var string
  35. */
  36. protected $textWithChildren = null;
  37. /**
  38. * Sets up the tag of this node.
  39. *
  40. * @param string|Tag $tag
  41. */
  42. public function __construct($tag)
  43. {
  44. if ( ! $tag instanceof Tag) {
  45. $tag = new Tag($tag);
  46. }
  47. $this->tag = $tag;
  48. parent::__construct();
  49. }
  50. /**
  51. * Gets the inner html of this node.
  52. *
  53. * @return string
  54. * @throws UnknownChildTypeException
  55. */
  56. public function innerHtml(): string
  57. {
  58. if ( ! $this->hasChildren()) {
  59. // no children
  60. return '';
  61. }
  62. if ( ! is_null($this->innerHtml)) {
  63. // we already know the result.
  64. return $this->innerHtml;
  65. }
  66. $child = $this->firstChild();
  67. $string = '';
  68. // continue to loop until we are out of children
  69. while ( ! is_null($child)) {
  70. if ($child instanceof TextNode) {
  71. $string .= $child->text();
  72. } elseif ($child instanceof HtmlNode) {
  73. $string .= $child->outerHtml();
  74. } else {
  75. throw new UnknownChildTypeException('Unknown child type "'.get_class($child).'" found in node');
  76. }
  77. try {
  78. $child = $this->nextChild($child->id());
  79. } catch (ChildNotFoundException $e) {
  80. // no more children
  81. $child = null;
  82. }
  83. }
  84. // remember the results
  85. $this->innerHtml = $string;
  86. return $string;
  87. }
  88. /**
  89. * Gets the html of this node, including it's own
  90. * tag.
  91. *
  92. * @return string
  93. */
  94. public function outerHtml(): string
  95. {
  96. // special handling for root
  97. if ($this->tag->name() == 'root') {
  98. return $this->innerHtml();
  99. }
  100. if ( ! is_null($this->outerHtml)) {
  101. // we already know the results.
  102. return $this->outerHtml;
  103. }
  104. $return = $this->tag->makeOpeningTag();
  105. if ($this->tag->isSelfClosing()) {
  106. // ignore any children... there should not be any though
  107. return $return;
  108. }
  109. // get the inner html
  110. $return .= $this->innerHtml();
  111. // add closing tag
  112. $return .= $this->tag->makeClosingTag();
  113. // remember the results
  114. $this->outerHtml = $return;
  115. return $return;
  116. }
  117. /**
  118. * Gets the text of this node (if there is any text). Or get all the text
  119. * in this node, including children.
  120. *
  121. * @param bool $lookInChildren
  122. * @return string
  123. */
  124. public function text(bool $lookInChildren = false): string
  125. {
  126. if ($lookInChildren) {
  127. if ( ! is_null($this->textWithChildren)) {
  128. // we already know the results.
  129. return $this->textWithChildren;
  130. }
  131. } elseif ( ! is_null($this->text)) {
  132. // we already know the results.
  133. return $this->text;
  134. }
  135. // find out if this node has any text children
  136. $text = '';
  137. foreach ($this->children as $child) {
  138. /** @var AbstractNode $node */
  139. $node = $child['node'];
  140. if ($node instanceof TextNode) {
  141. $text .= $child['node']->text;
  142. } elseif ($lookInChildren &&
  143. $node instanceof HtmlNode
  144. ) {
  145. $text .= $node->text($lookInChildren);
  146. }
  147. }
  148. // remember our result
  149. if ($lookInChildren) {
  150. $this->textWithChildren = $text;
  151. } else {
  152. $this->text = $text;
  153. }
  154. return $text;
  155. }
  156. /**
  157. * Call this when something in the node tree has changed. Like a child has been added
  158. * or a parent has been changed.
  159. */
  160. protected function clear(): void
  161. {
  162. $this->innerHtml = null;
  163. $this->outerHtml = null;
  164. $this->text = null;
  165. $this->textWithChildren = null;
  166. if (is_null($this->parent) === false) {
  167. $this->parent->clear();
  168. }
  169. }
  170. /**
  171. * Returns all children of this html node.
  172. *
  173. * @return array
  174. */
  175. protected function getIteratorArray(): array
  176. {
  177. return $this->getChildren();
  178. }
  179. }