[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE 8 */ 9 10 namespace Joomla\CMS\Document\Renderer\Html; 11 12 use Joomla\CMS\Document\DocumentRenderer; 13 use Joomla\CMS\WebAsset\WebAssetItemInterface; 14 15 // phpcs:disable PSR1.Files.SideEffects 16 \defined('JPATH_PLATFORM') or die; 17 // phpcs:enable PSR1.Files.SideEffects 18 19 /** 20 * JDocument head renderer 21 * 22 * @since 4.0.0 23 */ 24 class ScriptsRenderer extends DocumentRenderer 25 { 26 /** 27 * List of already rendered src 28 * 29 * @var array 30 * 31 * @since 4.0.0 32 */ 33 private $renderedSrc = []; 34 35 /** 36 * Renders the document script tags and returns the results as a string 37 * 38 * @param string $head (unused) 39 * @param array $params Associative array of values 40 * @param string $content The script 41 * 42 * @return string The output of the script 43 * 44 * @since 4.0.0 45 */ 46 public function render($head, $params = array(), $content = null) 47 { 48 // Get line endings 49 $lnEnd = $this->_doc->_getLineEnd(); 50 $tab = $this->_doc->_getTab(); 51 $buffer = ''; 52 $wam = $this->_doc->getWebAssetManager(); 53 $assets = $wam->getAssets('script', true); 54 55 // Get a list of inline assets and their relation with regular assets 56 $inlineAssets = $wam->filterOutInlineAssets($assets); 57 $inlineRelation = $wam->getInlineRelation($inlineAssets); 58 59 // Merge with existing scripts, for rendering 60 $assets = array_merge(array_values($assets), $this->_doc->_scripts); 61 62 // Generate script file links 63 foreach ($assets as $key => $item) { 64 // Check whether we have an Asset instance, or old array with attributes 65 $asset = $item instanceof WebAssetItemInterface ? $item : null; 66 67 // Add src attribute for non Asset item 68 if (!$asset) { 69 $item['src'] = $key; 70 } 71 72 // Check for inline content "before" 73 if ($asset && !empty($inlineRelation[$asset->getName()]['before'])) { 74 foreach ($inlineRelation[$asset->getName()]['before'] as $itemBefore) { 75 $buffer .= $this->renderInlineElement($itemBefore); 76 77 // Remove this item from inline queue 78 unset($inlineAssets[$itemBefore->getName()]); 79 } 80 } 81 82 $buffer .= $this->renderElement($item); 83 84 // Check for inline content "after" 85 if ($asset && !empty($inlineRelation[$asset->getName()]['after'])) { 86 foreach ($inlineRelation[$asset->getName()]['after'] as $itemBefore) { 87 $buffer .= $this->renderInlineElement($itemBefore); 88 89 // Remove this item from inline queue 90 unset($inlineAssets[$itemBefore->getName()]); 91 } 92 } 93 } 94 95 // Generate script declarations for assets 96 foreach ($inlineAssets as $item) { 97 $buffer .= $this->renderInlineElement($item); 98 } 99 100 // Generate script declarations for old scripts 101 foreach ($this->_doc->_script as $type => $contents) { 102 // Test for B.C. in case someone still store script declarations as single string 103 if (\is_string($contents)) { 104 $contents = [$contents]; 105 } 106 107 foreach ($contents as $content) { 108 $buffer .= $this->renderInlineElement( 109 [ 110 'type' => $type, 111 'content' => $content, 112 ] 113 ); 114 } 115 } 116 117 // Output the custom tags - array_unique makes sure that we don't output the same tags twice 118 foreach (array_unique($this->_doc->_custom) as $custom) { 119 $buffer .= $tab . $custom . $lnEnd; 120 } 121 122 return ltrim($buffer, $tab); 123 } 124 125 /** 126 * Renders the element 127 * 128 * @param WebAssetItemInterface|array $item The element 129 * 130 * @return string The resulting string 131 * 132 * @since 4.0.0 133 */ 134 private function renderElement($item): string 135 { 136 $buffer = ''; 137 $asset = $item instanceof WebAssetItemInterface ? $item : null; 138 $src = $asset ? $asset->getUri() : ($item['src'] ?? ''); 139 140 // Make sure we have a src, and it not already rendered 141 if (!$src || !empty($this->renderedSrc[$src]) || ($asset && $asset->getOption('webcomponent'))) { 142 return ''; 143 } 144 145 $lnEnd = $this->_doc->_getLineEnd(); 146 $tab = $this->_doc->_getTab(); 147 $mediaVersion = $this->_doc->getMediaVersion(); 148 149 // Get the attributes and other options 150 if ($asset) { 151 $attribs = $asset->getAttributes(); 152 $version = $asset->getVersion(); 153 $conditional = $asset->getOption('conditional'); 154 155 // Add an asset info for debugging 156 if (JDEBUG) { 157 $attribs['data-asset-name'] = $asset->getName(); 158 159 if ($asset->getDependencies()) { 160 $attribs['data-asset-dependencies'] = implode(',', $asset->getDependencies()); 161 } 162 } 163 } else { 164 $attribs = $item; 165 $version = isset($attribs['options']['version']) ? $attribs['options']['version'] : ''; 166 $conditional = !empty($attribs['options']['conditional']) ? $attribs['options']['conditional'] : null; 167 } 168 169 // Add "nonce" attribute if exist 170 if ($this->_doc->cspNonce && !is_null($this->_doc->cspNonce)) { 171 $attribs['nonce'] = $this->_doc->cspNonce; 172 } 173 174 // To prevent double rendering 175 $this->renderedSrc[$src] = true; 176 177 // Check if script uses media version. 178 if ($version && strpos($src, '?') === false && ($mediaVersion || $version !== 'auto')) { 179 $src .= '?' . ($version === 'auto' ? $mediaVersion : $version); 180 } 181 182 $buffer .= $tab; 183 184 // This is for IE conditional statements support. 185 if (!\is_null($conditional)) { 186 $buffer .= '<!--[if ' . $conditional . ']>'; 187 } 188 189 // Render the element with attributes 190 $buffer .= '<script src="' . htmlspecialchars($src) . '"'; 191 $buffer .= $this->renderAttributes($attribs); 192 $buffer .= '></script>'; 193 194 // This is for IE conditional statements support. 195 if (!\is_null($conditional)) { 196 $buffer .= '<![endif]-->'; 197 } 198 199 $buffer .= $lnEnd; 200 201 return $buffer; 202 } 203 204 /** 205 * Renders the inline element 206 * 207 * @param WebAssetItemInterface|array $item The element 208 * 209 * @return string The resulting string 210 * 211 * @since 4.0.0 212 */ 213 private function renderInlineElement($item): string 214 { 215 $buffer = ''; 216 $lnEnd = $this->_doc->_getLineEnd(); 217 $tab = $this->_doc->_getTab(); 218 219 if ($item instanceof WebAssetItemInterface) { 220 $attribs = $item->getAttributes(); 221 $content = $item->getOption('content'); 222 } else { 223 $attribs = $item; 224 $content = $item['content'] ?? ''; 225 226 unset($attribs['content']); 227 } 228 229 // Do not produce empty elements 230 if (!$content) { 231 return ''; 232 } 233 234 // Add "nonce" attribute if exist 235 if ($this->_doc->cspNonce && !is_null($this->_doc->cspNonce)) { 236 $attribs['nonce'] = $this->_doc->cspNonce; 237 } 238 239 $buffer .= $tab . '<script'; 240 $buffer .= $this->renderAttributes($attribs); 241 $buffer .= '>'; 242 243 // This is for full XHTML support. 244 if ($this->_doc->_mime !== 'text/html') { 245 $buffer .= $tab . $tab . '//<![CDATA[' . $lnEnd; 246 } 247 248 $buffer .= $content; 249 250 // See above note 251 if ($this->_doc->_mime !== 'text/html') { 252 $buffer .= $tab . $tab . '//]]>' . $lnEnd; 253 } 254 255 $buffer .= '</script>' . $lnEnd; 256 257 return $buffer; 258 } 259 260 /** 261 * Renders the element attributes 262 * 263 * @param array $attributes The element attributes 264 * 265 * @return string The attributes string 266 * 267 * @since 4.0.0 268 */ 269 private function renderAttributes(array $attributes): string 270 { 271 $buffer = ''; 272 273 $defaultJsMimes = array('text/javascript', 'application/javascript', 'text/x-javascript', 'application/x-javascript'); 274 $html5NoValueAttributes = array('defer', 'async', 'nomodule'); 275 276 foreach ($attributes as $attrib => $value) { 277 // Don't add the 'options' attribute. This attribute is for internal use (version, conditional, etc). 278 if ($attrib === 'options' || $attrib === 'src') { 279 continue; 280 } 281 282 // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C. 283 if (\in_array($attrib, array('type', 'mime')) && $this->_doc->isHtml5() && \in_array($value, $defaultJsMimes)) { 284 continue; 285 } 286 287 // B/C: If defer and async is false or empty don't render the attribute. Also skip if value is bool:false. 288 if (\in_array($attrib, array('defer', 'async')) && !$value || $value === false) { 289 continue; 290 } 291 292 // NoValue attribute, if it have bool:true 293 $isNoValueAttrib = $value === true || \in_array($attrib, $html5NoValueAttributes); 294 295 // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C. 296 if ($attrib === 'mime') { 297 $attrib = 'type'; 298 } elseif ($isNoValueAttrib) { 299 // NoValue attribute in non HTML5 should contain a value, set it equal to attribute name. 300 $value = $attrib; 301 } 302 303 // Add attribute to script tag output. 304 $buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT, 'UTF-8'); 305 306 if (!($this->_doc->isHtml5() && $isNoValueAttrib)) { 307 // Json encode value if it's an array. 308 $value = !is_scalar($value) ? json_encode($value) : $value; 309 310 $buffer .= '="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"'; 311 } 312 } 313 314 return $buffer; 315 } 316 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |