HasHtml.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. namespace Dcat\Admin\Traits;
  3. use Dcat\Admin\Support\Helper;
  4. use DOMElement;
  5. use DOMDocument;
  6. trait HasHtml
  7. {
  8. protected static $shouldResolveTags = ['style', 'script', 'template'];
  9. /**
  10. * @param string|array $content
  11. *
  12. * @return null|string
  13. */
  14. public static function html($content = null)
  15. {
  16. $html = static::context()->html ?: [];
  17. if ($content === null) {
  18. return implode('', array_unique($html));
  19. }
  20. static::context()->html = array_merge(
  21. $html,
  22. array_map([Helper::class, 'render'], (array) $content)
  23. );
  24. }
  25. /**
  26. * @param string $view
  27. * @param array $data
  28. *
  29. * @return string
  30. *
  31. * @throws \Throwable
  32. */
  33. public static function view(string $view, array $data = [])
  34. {
  35. return static::resolveHtml(view($view, $data))['html'];
  36. }
  37. /**
  38. * @param string|\Illuminate\Contracts\Support\Renderable $content
  39. * @param array $data
  40. * @param array $options
  41. *
  42. * @throws \Throwable
  43. *
  44. * @return array ['html' => $html, 'script' => $script]
  45. */
  46. public static function resolveHtml($content, array $options = []): array
  47. {
  48. $dom = static::getDOMDocument(Helper::render($content));
  49. $head = $dom->getElementsByTagName('head')->item(0) ?: null;
  50. $body = $dom->getElementsByTagName('body')->item(0) ?: null;
  51. $head = static::resolveElement($head);
  52. $body = static::resolveElement($body);
  53. $script = $head['script'].$body['script'];
  54. $runScript = $options['runScript'] ?? true;
  55. if ($runScript) {
  56. static::script($script);
  57. $script = '';
  58. }
  59. return ['html' => $head['html'].$body['html'], 'script' => $script];
  60. }
  61. /**
  62. * @param string $html
  63. *
  64. * @throws \Throwable
  65. *
  66. * @return DOMDocument
  67. */
  68. protected static function getDOMDocument(string $html)
  69. {
  70. $dom = new DOMDocument();
  71. libxml_use_internal_errors(true);
  72. $dom->loadHTML('<?xml encoding="utf-8" ?>'.$html);
  73. libxml_use_internal_errors(false);
  74. return $dom;
  75. }
  76. /**
  77. * @param DOMElement $element
  78. *
  79. * @return void|string
  80. */
  81. protected static function resolve(DOMElement $element)
  82. {
  83. $method = 'resolve'.ucfirst($element->tagName);
  84. return static::{$method}($element);
  85. }
  86. /**
  87. * @param DOMElement $element
  88. *
  89. * @return string|void
  90. */
  91. protected static function resolveScript(DOMElement $element)
  92. {
  93. if ($element->hasAttribute('src')) {
  94. static::js($element->getAttribute('src'));
  95. return;
  96. }
  97. if (! empty($script = trim($element->nodeValue))) {
  98. if ($require = $element->getAttribute('require')) {
  99. static::asset()->require(explode(',', $require));
  100. }
  101. $script = '(function () {'.$script.'})();';
  102. if ($element->hasAttribute('once')) {
  103. return static::script($script);
  104. }
  105. return $script;
  106. }
  107. }
  108. /**
  109. * @param DOMElement $element
  110. *
  111. * @return void
  112. */
  113. protected static function resolveStyle(DOMElement $element)
  114. {
  115. if (! empty(trim($element->nodeValue))) {
  116. static::style($element->nodeValue);
  117. }
  118. }
  119. /**
  120. * @param DOMElement $element
  121. *
  122. * @return void
  123. */
  124. protected static function resolveTemplate(DOMElement $element)
  125. {
  126. $html = '';
  127. foreach ($element->childNodes as $childNode) {
  128. $html .= $element->ownerDocument->saveHTML($childNode);
  129. }
  130. $html && static::html($html);
  131. }
  132. protected static function resolveElement(?DOMElement $element)
  133. {
  134. $html = $script = '';
  135. if (! $element) {
  136. return ['html' => $html, 'script' => $script];
  137. }
  138. foreach ($element->childNodes as $child) {
  139. if (
  140. $child instanceof DOMElement
  141. && in_array($child->tagName, static::$shouldResolveTags, true)
  142. ) {
  143. $script .= static::resolve($child);
  144. continue;
  145. }
  146. $html .= trim($element->ownerDocument->saveHTML($child));
  147. }
  148. return ['html' => $html, 'script' => $script];
  149. }
  150. }