HasHtml.php 4.4 KB

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