Passed
Pull Request — master (#20001)
by Philippe
13:20
created

View::renderBodyBeginHtml()   A

Complexity

Conditions 6
Paths 32

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6.73

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
c 1
b 0
f 0
nc 32
nop 0
dl 0
loc 17
ccs 8
cts 11
cp 0.7272
crap 6.73
rs 9.2222
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
use yii\helpers\ArrayHelper;
13
use yii\helpers\Html;
14
use yii\helpers\Json;
15
use yii\helpers\Url;
16
17
/**
18
 * View represents a view object in the MVC pattern.
19
 *
20
 * View provides a set of methods (e.g. [[render()]]) for rendering purpose.
21
 *
22
 * View is configured as an application component in [[\yii\base\Application]] by default.
23
 * You can access that instance via `Yii::$app->view`.
24
 *
25
 * You can modify its configuration by adding an array to your application config under `components`
26
 * as it is shown in the following example:
27
 *
28
 * ```php
29
 * 'view' => [
30
 *     'theme' => 'app\themes\MyTheme',
31
 *     'renderers' => [
32
 *         // you may add Smarty or Twig renderer here
33
 *     ]
34
 *     // ...
35
 * ]
36
 * ```
37
 *
38
 * For more details and usage information on View, see the [guide article on views](guide:structure-views).
39
 *
40
 * @property \yii\web\AssetManager $assetManager The asset manager. Defaults to the "assetManager" application
41
 * component.
42
 *
43
 * @author Qiang Xue <[email protected]>
44
 * @since 2.0
45
 */
46
class View extends \yii\base\View
47
{
48
    /**
49
     * @event Event an event that is triggered by [[beginBody()]].
50
     */
51
    const EVENT_BEGIN_BODY = 'beginBody';
52
    /**
53
     * @event Event an event that is triggered by [[endBody()]].
54
     */
55
    const EVENT_END_BODY = 'endBody';
56
    /**
57
     * The location of registered JavaScript code block or files.
58
     * This means the location is in the head section.
59
     */
60
    const POS_HEAD = 1;
61
    /**
62
     * The location of registered JavaScript code block or files.
63
     * This means the location is at the beginning of the body section.
64
     */
65
    const POS_BEGIN = 2;
66
    /**
67
     * The location of registered JavaScript code block or files.
68
     * This means the location is at the end of the body section.
69
     */
70
    const POS_END = 3;
71
    /**
72
     * The location of registered JavaScript code block.
73
     * This means the JavaScript code block will be enclosed within `jQuery(document).ready()`.
74
     */
75
    const POS_READY = 4;
76
    /**
77
     * The location of registered JavaScript code block.
78
     * This means the JavaScript code block will be enclosed within `jQuery(window).load()`.
79
     */
80
    const POS_LOAD = 5;
81
    /**
82
     * This is internally used as the placeholder for receiving the content registered for the head section.
83
     */
84
    const PH_HEAD = '<![CDATA[YII-BLOCK-HEAD]]>';
85
    /**
86
     * This is internally used as the placeholder for receiving the content registered for the beginning of the body section.
87
     */
88
    const PH_BODY_BEGIN = '<![CDATA[YII-BLOCK-BODY-BEGIN]]>';
89
    /**
90
     * This is internally used as the placeholder for receiving the content registered for the end of the body section.
91
     */
92
    const PH_BODY_END = '<![CDATA[YII-BLOCK-BODY-END]]>';
93
94
    /**
95
     * @var AssetBundle[] list of the registered asset bundles. The keys are the bundle names, and the values
96
     * are the registered [[AssetBundle]] objects.
97
     * @see registerAssetBundle()
98
     */
99
    public $assetBundles = [];
100
    /**
101
     * @var string the page title
102
     */
103
    public $title;
104
    /**
105
     * @var array the registered meta tags.
106
     * @see registerMetaTag()
107
     */
108
    public $metaTags = [];
109
    /**
110
     * @var array the registered link tags.
111
     * @see registerLinkTag()
112
     */
113
    public $linkTags = [];
114
    /**
115
     * @var array the registered CSS code blocks.
116
     * @see registerCss()
117
     */
118
    public $css = [];
119
    /**
120
     * @var array the registered CSS files.
121
     * @see registerCssFile()
122
     */
123
    public $cssFiles = [];
124
    /**
125
     * @var array the registered JS code blocks
126
     * @see registerJs()
127
     */
128
    public $js = [];
129
    /**
130
     * @var array the registered JS files.
131
     * @see registerJsFile()
132
     */
133
    public $jsFiles = [];
134
    /**
135
     * @var array list of registered json+ld blocks
136
     * @see registerLdJson()
137
     */
138
    public $ldJson = [];
139
    /**
140
     * @var array list of registered noscript tags
141
     * @see registerNoscriptTag()
142
     */
143
    public $noscriptTags = [];
144
145
    private $_assetManager;
146
147
148
    /**
149
     * Whether [[endPage()]] has been called and all files have been registered
150
     * @var bool
151
     * @since 2.0.44
152
     */
153
    protected $isPageEnded = false;
154
155
    /**
156
     * Marks the position of an HTML head section.
157
     */
158 52
    public function head()
159
    {
160 52
        echo self::PH_HEAD;
161 52
    }
162
163
    /**
164
     * Marks the beginning of an HTML body section.
165
     */
166 52
    public function beginBody()
167
    {
168 52
        echo self::PH_BODY_BEGIN;
169 52
        $this->trigger(self::EVENT_BEGIN_BODY);
170 52
    }
171
172
    /**
173
     * Marks the ending of an HTML body section.
174
     */
175 56
    public function endBody()
176
    {
177 56
        $this->trigger(self::EVENT_END_BODY);
178 56
        echo self::PH_BODY_END;
179
180 56
        foreach (array_keys($this->assetBundles) as $bundle) {
181 15
            $this->registerAssetFiles($bundle);
182
        }
183 56
    }
184
185
    /**
186
     * Marks the ending of an HTML page.
187
     * @param bool $ajaxMode whether the view is rendering in AJAX mode.
188
     * If true, the JS scripts registered at [[POS_READY]] and [[POS_LOAD]] positions
189
     * will be rendered at the end of the view like normal scripts.
190
     */
191 56
    public function endPage($ajaxMode = false)
192
    {
193 56
        $this->trigger(self::EVENT_END_PAGE);
194
195 56
        $this->isPageEnded = true;
196
197 56
        $content = ob_get_clean();
198
199 56
        echo strtr($content, [
200 56
            self::PH_HEAD => $this->renderHeadHtml(),
201 56
            self::PH_BODY_BEGIN => $this->renderBodyBeginHtml(),
202 56
            self::PH_BODY_END => $this->renderBodyEndHtml($ajaxMode),
203
        ]);
204
205 56
        $this->clear();
206 56
    }
207
208
    /**
209
     * Renders a view in response to an AJAX request.
210
     *
211
     * This method is similar to [[render()]] except that it will surround the view being rendered
212
     * with the calls of [[beginPage()]], [[head()]], [[beginBody()]], [[endBody()]] and [[endPage()]].
213
     * By doing so, the method is able to inject into the rendering result with JS/CSS scripts and files
214
     * that are registered with the view.
215
     *
216
     * @param string $view the view name. Please refer to [[render()]] on how to specify this parameter.
217
     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
218
     * @param object|null $context the context that the view should use for rendering the view. If null,
219
     * existing [[context]] will be used.
220
     * @return string the rendering result
221
     * @see render()
222
     */
223
    public function renderAjax($view, $params = [], $context = null)
224
    {
225
        $viewFile = $this->findViewFile($view, $context);
226
227
        ob_start();
228
        ob_implicit_flush(false);
229
230
        $this->beginPage();
231
        $this->head();
232
        $this->beginBody();
233
        echo $this->renderFile($viewFile, $params, $context);
234
        $this->endBody();
235
        $this->endPage(true);
236
237
        return ob_get_clean();
238
    }
239
240
    /**
241
     * Registers the asset manager being used by this view object.
242
     * @return \yii\web\AssetManager the asset manager. Defaults to the "assetManager" application component.
243
     */
244 59
    public function getAssetManager()
245
    {
246 59
        return $this->_assetManager ?: Yii::$app->getAssetManager();
247
    }
248
249
    /**
250
     * Sets the asset manager.
251
     * @param \yii\web\AssetManager $value the asset manager
252
     */
253 82
    public function setAssetManager($value)
254
    {
255 82
        $this->_assetManager = $value;
256 82
    }
257
258
    /**
259
     * Clears up the registered meta tags, link tags, css/js scripts and files.
260
     */
261 58
    public function clear()
262
    {
263 58
        $this->metaTags = [];
264 58
        $this->linkTags = [];
265 58
        $this->css = [];
266 58
        $this->cssFiles = [];
267 58
        $this->js = [];
268 58
        $this->jsFiles = [];
269 58
        $this->assetBundles = [];
270 58
        $this->ldJson = [];
271 58
        $this->noscriptTags = [];
272 58
    }
273
274
    /**
275
     * Registers all files provided by an asset bundle including depending bundles files.
276
     * Removes a bundle from [[assetBundles]] once files are registered.
277
     * @param string $name name of the bundle to register
278
     */
279 15
    protected function registerAssetFiles($name)
280
    {
281 15
        if (!isset($this->assetBundles[$name])) {
282 12
            return;
283
        }
284 15
        $bundle = $this->assetBundles[$name];
285 15
        if ($bundle) {
0 ignored issues
show
introduced by
$bundle is of type yii\web\AssetBundle, thus it always evaluated to true.
Loading history...
286 15
            foreach ($bundle->depends as $dep) {
287 12
                $this->registerAssetFiles($dep);
288
            }
289 15
            $bundle->registerAssetFiles($this);
290
        }
291 15
        unset($this->assetBundles[$name]);
292 15
    }
293
294
    /**
295
     * Registers the named asset bundle.
296
     * All dependent asset bundles will be registered.
297
     * @param string $name the class name of the asset bundle (without the leading backslash)
298
     * @param int|null $position if set, this forces a minimum position for javascript files.
299
     * This will adjust depending assets javascript file position or fail if requirement can not be met.
300
     * If this is null, asset bundles position settings will not be changed.
301
     * See [[registerJsFile]] for more details on javascript position.
302
     * @return AssetBundle the registered asset bundle instance
303
     * @throws InvalidConfigException if the asset bundle does not exist or a circular dependency is detected
304
     */
305 35
    public function registerAssetBundle($name, $position = null)
306
    {
307 35
        if (!isset($this->assetBundles[$name])) {
308 34
            $am = $this->getAssetManager();
309 34
            $bundle = $am->getBundle($name);
310 34
            $this->assetBundles[$name] = false;
311
            // register dependencies
312 34
            $pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;
313 34
            foreach ($bundle->depends as $dep) {
314 24
                $this->registerAssetBundle($dep, $pos);
315
            }
316 33
            $this->assetBundles[$name] = $bundle;
317 19
        } elseif ($this->assetBundles[$name] === false) {
0 ignored issues
show
introduced by
The condition $this->assetBundles[$name] === false is always false.
Loading history...
318 1
            throw new InvalidConfigException("A circular dependency is detected for bundle '$name'.");
319
        } else {
320 18
            $bundle = $this->assetBundles[$name];
321
        }
322
323 34
        if ($position !== null) {
324 12
            $pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;
325 12
            if ($pos === null) {
326 12
                $bundle->jsOptions['position'] = $pos = $position;
327 6
            } elseif ($pos > $position) {
328 6
                throw new InvalidConfigException("An asset bundle that depends on '$name' has a higher javascript file position configured than '$name'.");
329
            }
330
            // update position for all dependencies
331 12
            foreach ($bundle->depends as $dep) {
332 6
                $this->registerAssetBundle($dep, $pos);
333
            }
334
        }
335
336 34
        return $bundle;
337
    }
338
339
    /**
340
     * Registers a meta tag.
341
     *
342
     * For example, a description meta tag can be added like the following:
343
     *
344
     * ```php
345
     * $view->registerMetaTag([
346
     *     'name' => 'description',
347
     *     'content' => 'This website is about funny raccoons.'
348
     * ]);
349
     * ```
350
     *
351
     * will result in the meta tag `<meta name="description" content="This website is about funny raccoons.">`.
352
     *
353
     * @param array $options the HTML attributes for the meta tag.
354
     * @param string|null $key the key that identifies the meta tag. If two meta tags are registered
355
     * with the same key, the latter will overwrite the former. If this is null, the new meta tag
356
     * will be appended to the existing ones.
357
     */
358
    public function registerMetaTag($options, $key = null)
359
    {
360
        if ($key === null) {
361
            $this->metaTags[] = Html::tag('meta', '', $options);
362
        } else {
363
            $this->metaTags[$key] = Html::tag('meta', '', $options);
364
        }
365
    }
366
367
    /**
368
     * Registers CSRF meta tags.
369
     * They are rendered dynamically to retrieve a new CSRF token for each request.
370
     *
371
     * ```php
372
     * $view->registerCsrfMetaTags();
373
     * ```
374
     *
375
     * The above code will result in `<meta name="csrf-param" content="[yii\web\Request::$csrfParam]">`
376
     * and `<meta name="csrf-token" content="tTNpWKpdy-bx8ZmIq9R72...K1y8IP3XGkzZA==">` added to the page.
377
     *
378
     * Note: Hidden CSRF input of ActiveForm will be automatically refreshed by calling `window.yii.refreshCsrfToken()`
379
     * from `yii.js`.
380
     *
381
     * @since 2.0.13
382
     */
383 1
    public function registerCsrfMetaTags()
384
    {
385 1
        $this->metaTags['csrf_meta_tags'] = $this->renderDynamic('return yii\helpers\Html::csrfMetaTags();');
386 1
    }
387
388
    /**
389
     * Registers a link tag.
390
     *
391
     * For example, a link tag for a custom [favicon](https://www.w3.org/2005/10/howto-favicon)
392
     * can be added like the following:
393
     *
394
     * ```php
395
     * $view->registerLinkTag(['rel' => 'icon', 'type' => 'image/png', 'href' => '/myicon.png']);
396
     * ```
397
     *
398
     * which will result in the following HTML: `<link rel="icon" type="image/png" href="/myicon.png">`.
399
     *
400
     * **Note:** To register link tags for CSS stylesheets, use [[registerCssFile()]] instead, which
401
     * has more options for this kind of link tag.
402
     *
403
     * @param array $options the HTML attributes for the link tag.
404
     * @param string|null $key the key that identifies the link tag. If two link tags are registered
405
     * with the same key, the latter will overwrite the former. If this is null, the new link tag
406
     * will be appended to the existing ones.
407
     */
408
    public function registerLinkTag($options, $key = null)
409
    {
410
        if ($key === null) {
411
            $this->linkTags[] = Html::tag('link', '', $options);
412
        } else {
413
            $this->linkTags[$key] = Html::tag('link', '', $options);
414
        }
415
    }
416
417
    /**
418
     * Registers a noscript tag.
419
     *
420
     * For example, a noscript tag for a tracking pixel
421
     * can be added like the following:
422
     *
423
     * ```php
424
     * $view->registerNoscriptTag('<img src="https://www.google-analytics.com/collect?v=1&amp;tid=UA-12345678-1&amp;cid=555&amp;t=event&amp;ec=pageview&amp;ea=noscript&amp;dp=%2Fnoscript&amp;dt=No%20Script%20Test" alt="">');
425
     * ```
426
     *
427
     * which will result in the following HTML: `<noscript><img src="https://www.google-analytics.com/collect?v=1&amp;tid=UA-12345678-1&amp;cid=555&amp;t=event&amp;ec=pageview&amp;ea=noscript&amp;dp=%2Fnoscript&amp;dt=No%20Script%20Test" alt=""></noscript>`.
428
     *
429
     * @param array $options the HTML attributes for the link tag.
430
     * @param int $position the position at which the noscript tag should be inserted
431
     * @param string|null $key the key that identifies the link tag. If two link tags are registered
432
     * with the same key, the latter will overwrite the former. If this is null, the new link tag
433
     * will be appended to the existing ones.
434
     */
435
    public function registerNoscriptTag($content, $options, $position = self::POS_HEAD, $key = null)
436
    {
437
        if ($key === null) {
438
            $this->noscriptTags[$position][] = Html::tag('noscript', $content, $options);
439
        } else {
440
            $this->noscriptTags[$position][$key] = Html::tag('noscript', $content, $options);
441
        }
442
    }
443
444
    /**
445
     * Registers a ld+json script tag.
446
     *
447
     * For example, a ld+json script tag for a custom video object
448
     * can be added like the following:
449
     *
450
     * ```php
451
     * $ldJson = [
452
     *    '@context' => 'https://schema.org',
453
     *    '@type' => 'VideoObject',
454
     *    'name' => $name,
455
     *    'description' => $description,
456
     *    'url' => $url
457
     * ];
458
     * $view->registerLdJson($ldJson);
459
     * ```
460
     *
461
     * which will result in the following HTML:
462
     * ```html
463
     *      <script type="application/ld+json">
464
     *          [{"@context":"https:\/\/schema.org","@type":"VideoObject","name":"$name","description":"$description","url":"$url"}]
465
     *      </script>
466
     * ```.
467
     *
468
     * **Note:** All registered ld+json script tags which should be rendered in the head section will be merged into one script tag in an array.
469
     *
470
     * @param array $ldJson the LD+JSON object to be registered
471
     * @param int $position the position at which the LD+JSON object should be inserted
472
     * @param string|null $key the key that identifies the LD+JSON tag. If two objects are registered
473
     * with the same key, the latter will overwrite the former. If this is null, the new link tag
474
     * will be appended to the existing ones.
475
     */
476
    public function registerLdJson($ldJson, $position = self::POS_HEAD, $key = null)
477
    {
478
        if ($key === null) {
479
            $this->ldJson[$position][] = Html::script(Json::encode($ldJson), ['type' => 'application/ld+json']);
480
        } else {
481
            $this->ldJson[$position][$key] = Html::script(Json::encode($ldJson), ['type' => 'application/ld+json']);
482
        }
483
    }
484
485
    /**
486
     * Registers a CSS code block.
487
     * @param string $css the content of the CSS code block to be registered
488
     * @param array $options the HTML attributes for the `<style>`-tag.
489
     * @param string|null $key the key that identifies the CSS code block. If null, it will use
490
     * $css as the key. If two CSS code blocks are registered with the same key, the latter
491
     * will overwrite the former.
492
     */
493
    public function registerCss($css, $options = [], $key = null)
494
    {
495
        $key = $key ?: md5($css);
496
        $this->css[$key] = Html::style($css, $options);
497
    }
498
499
    /**
500
     * Registers a CSS file.
501
     *
502
     * This method should be used for simple registration of CSS files. If you want to use features of
503
     * [[AssetManager]] like appending timestamps to the URL and file publishing options, use [[AssetBundle]]
504
     * and [[registerAssetBundle()]] instead.
505
     *
506
     * @param string $url the CSS file to be registered.
507
     * @param array $options the HTML attributes for the link tag. Please refer to [[Html::cssFile()]] for
508
     * the supported options. The following options are specially handled and are not treated as HTML attributes:
509
     *
510
     * - `depends`: array, specifies the names of the asset bundles that this CSS file depends on.
511
     * - `appendTimestamp`: bool whether to append a timestamp to the URL.
512
     *
513
     * @param string|null $key the key that identifies the CSS script file. If null, it will use
514
     * $url as the key. If two CSS files are registered with the same key, the latter
515
     * will overwrite the former.
516
     * @throws InvalidConfigException
517
     */
518 20
    public function registerCssFile($url, $options = [], $key = null)
519
    {
520 20
        $this->registerFile('css', $url, $options, $key);
521 20
    }
522
523
    /**
524
     * Registers a JS code block.
525
     * @param string $js the JS code block to be registered
526
     * @param int $position the position at which the JS script tag should be inserted
527
     * in a page. The possible values are:
528
     *
529
     * - [[POS_HEAD]]: in the head section
530
     * - [[POS_BEGIN]]: at the beginning of the body section
531
     * - [[POS_END]]: at the end of the body section
532
     * - [[POS_LOAD]]: enclosed within jQuery(window).load().
533
     *   Note that by using this position, the method will automatically register the jQuery js file.
534
     * - [[POS_READY]]: enclosed within jQuery(document).ready(). This is the default value.
535
     *   Note that by using this position, the method will automatically register the jQuery js file.
536
     *
537
     * @param string|null $key the key that identifies the JS code block. If null, it will use
538
     * $js as the key. If two JS code blocks are registered with the same key, the latter
539
     * will overwrite the former.
540
     */
541 10
    public function registerJs($js, $position = self::POS_READY, $key = null)
542
    {
543 10
        $key = $key ?: md5($js);
544 10
        $this->js[$position][$key] = $js;
545 10
        if ($position === self::POS_READY || $position === self::POS_LOAD) {
546 8
            JqueryAsset::register($this);
547
        }
548 10
    }
549
550
    /**
551
     * Registers a JS or CSS file.
552
     *
553
     * @param string $url the JS file to be registered.
554
     * @param string $type type (js or css) of the file.
555
     * @param array $options the HTML attributes for the script tag. The following options are specially handled
556
     * and are not treated as HTML attributes:
557
     *
558
     * - `depends`: array, specifies the names of the asset bundles that this CSS file depends on.
559
     * - `appendTimestamp`: bool whether to append a timestamp to the URL.
560
     *
561
     * @param string|null $key the key that identifies the JS script file. If null, it will use
562
     * $url as the key. If two JS files are registered with the same key at the same position, the latter
563
     * will overwrite the former. Note that position option takes precedence, thus files registered with the same key,
564
     * but different position option will not override each other.
565
     * @throws InvalidConfigException
566
     */
567 36
    private function registerFile($type, $url, $options = [], $key = null)
568
    {
569 36
        $url = Yii::getAlias($url);
570 36
        $key = $key ?: $url;
571 36
        $depends = ArrayHelper::remove($options, 'depends', []);
572 36
        $originalOptions = $options;
573 36
        $position = ArrayHelper::remove($options, 'position', self::POS_END);
574
575
        try {
576 36
            $assetManagerAppendTimestamp = $this->getAssetManager()->appendTimestamp;
577
        } catch (InvalidConfigException $e) {
578
            $depends = null; // the AssetManager is not available
579
            $assetManagerAppendTimestamp = false;
580
        }
581 36
        $appendTimestamp = ArrayHelper::remove($options, 'appendTimestamp', $assetManagerAppendTimestamp);
582
583 36
        if ($this->isPageEnded) {
584
            Yii::warning('You\'re trying to register a file after View::endPage() has been called.');
585
        }
586
587 36
        if (empty($depends)) {
588
            // register directly without AssetManager
589 36
            if ($appendTimestamp && Url::isRelative($url)) {
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type false; however, parameter $url of yii\helpers\BaseUrl::isRelative() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

589
            if ($appendTimestamp && Url::isRelative(/** @scrutinizer ignore-type */ $url)) {
Loading history...
590 5
                $prefix = Yii::getAlias('@web');
591 5
                $prefixLength = strlen($prefix);
0 ignored issues
show
Bug introduced by
It seems like $prefix can also be of type false; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

591
                $prefixLength = strlen(/** @scrutinizer ignore-type */ $prefix);
Loading history...
592 5
                $trimmedUrl = ltrim((substr($url, 0, $prefixLength) === $prefix) ? substr($url, $prefixLength) : $url, '/');
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type false; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

592
                $trimmedUrl = ltrim((substr(/** @scrutinizer ignore-type */ $url, 0, $prefixLength) === $prefix) ? substr($url, $prefixLength) : $url, '/');
Loading history...
Bug introduced by
It seems like substr($url, 0, $prefixL..., $prefixLength) : $url can also be of type false; however, parameter $string of ltrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

592
                $trimmedUrl = ltrim(/** @scrutinizer ignore-type */ (substr($url, 0, $prefixLength) === $prefix) ? substr($url, $prefixLength) : $url, '/');
Loading history...
593 5
                $timestamp = @filemtime(Yii::getAlias('@webroot/' . $trimmedUrl, false));
0 ignored issues
show
Bug introduced by
It seems like Yii::getAlias('@webroot/' . $trimmedUrl, false) can also be of type false; however, parameter $filename of filemtime() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

593
                $timestamp = @filemtime(/** @scrutinizer ignore-type */ Yii::getAlias('@webroot/' . $trimmedUrl, false));
Loading history...
594 5
                if ($timestamp > 0) {
595 2
                    $url = $timestamp ? "$url?v=$timestamp" : $url;
596
                }
597
            }
598 36
            if ($type === 'js') {
599 24
                $this->jsFiles[$position][$key] = Html::jsFile($url, $options);
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type false; however, parameter $url of yii\helpers\BaseHtml::jsFile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

599
                $this->jsFiles[$position][$key] = Html::jsFile(/** @scrutinizer ignore-type */ $url, $options);
Loading history...
600
            } else {
601 36
                $this->cssFiles[$key] = Html::cssFile($url, $options);
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type false; however, parameter $url of yii\helpers\BaseHtml::cssFile() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

601
                $this->cssFiles[$key] = Html::cssFile(/** @scrutinizer ignore-type */ $url, $options);
Loading history...
602
            }
603
        } else {
604 4
            $this->getAssetManager()->bundles[$key] = Yii::createObject([
605 4
                'class' => AssetBundle::className(),
0 ignored issues
show
Deprecated Code introduced by
The function yii\base\BaseObject::className() has been deprecated: since 2.0.14. On PHP >=5.5, use `::class` instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

605
                'class' => /** @scrutinizer ignore-deprecated */ AssetBundle::className(),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
606 4
                'baseUrl' => '',
607 4
                'basePath' => '@webroot',
608 4
                (string)$type => [ArrayHelper::merge([!Url::isRelative($url) ? $url : ltrim($url, '/')], $originalOptions)],
609 4
                "{$type}Options" => $options,
610 4
                'depends' => (array)$depends,
611
            ]);
612 4
            $this->registerAssetBundle($key);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $name of yii\web\View::registerAssetBundle() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

612
            $this->registerAssetBundle(/** @scrutinizer ignore-type */ $key);
Loading history...
613
        }
614 36
    }
615
616
    /**
617
     * Registers a JS file.
618
     *
619
     * This method should be used for simple registration of JS files. If you want to use features of
620
     * [[AssetManager]] like appending timestamps to the URL and file publishing options, use [[AssetBundle]]
621
     * and [[registerAssetBundle()]] instead.
622
     *
623
     * @param string $url the JS file to be registered.
624
     * @param array $options the HTML attributes for the script tag. The following options are specially handled
625
     * and are not treated as HTML attributes:
626
     *
627
     * - `depends`: array, specifies the names of the asset bundles that this JS file depends on.
628
     * - `position`: specifies where the JS script tag should be inserted in a page. The possible values are:
629
     *     * [[POS_HEAD]]: in the head section
630
     *     * [[POS_BEGIN]]: at the beginning of the body section
631
     *     * [[POS_END]]: at the end of the body section. This is the default value.
632
     * - `appendTimestamp`: bool whether to append a timestamp to the URL.
633
     *
634
     * Please refer to [[Html::jsFile()]] for other supported options.
635
     *
636
     * @param string|null $key the key that identifies the JS script file. If null, it will use
637
     * $url as the key. If two JS files are registered with the same key at the same position, the latter
638
     * will overwrite the former. Note that position option takes precedence, thus files registered with the same key,
639
     * but different position option will not override each other.
640
     * @throws InvalidConfigException
641
     */
642 24
    public function registerJsFile($url, $options = [], $key = null)
643
    {
644 24
        $this->registerFile('js', $url, $options, $key);
645 24
    }
646
647
    /**
648
     * Registers a JS code block defining a variable. The name of variable will be
649
     * used as key, preventing duplicated variable names.
650
     *
651
     * @param string $name Name of the variable
652
     * @param array|string $value Value of the variable
653
     * @param int $position the position in a page at which the JavaScript variable should be inserted.
654
     * The possible values are:
655
     *
656
     * - [[POS_HEAD]]: in the head section. This is the default value.
657
     * - [[POS_BEGIN]]: at the beginning of the body section.
658
     * - [[POS_END]]: at the end of the body section.
659
     * - [[POS_LOAD]]: enclosed within jQuery(window).load().
660
     *   Note that by using this position, the method will automatically register the jQuery js file.
661
     * - [[POS_READY]]: enclosed within jQuery(document).ready().
662
     *   Note that by using this position, the method will automatically register the jQuery js file.
663
     *
664
     * @since 2.0.14
665
     */
666 1
    public function registerJsVar($name, $value, $position = self::POS_HEAD)
667
    {
668 1
        $js = sprintf('var %s = %s;', $name, \yii\helpers\Json::htmlEncode($value));
669 1
        $this->registerJs($js, $position, $name);
670 1
    }
671
672
    /**
673
     * Renders the content to be inserted in the head section.
674
     * The content is rendered using the registered meta tags, link tags, CSS/JS code blocks and files.
675
     * @return string the rendered content
676
     */
677 56
    protected function renderHeadHtml()
678
    {
679 56
        $lines = [];
680 56
        if (!empty($this->metaTags)) {
681 1
            $lines[] = implode("\n", $this->metaTags);
682
        }
683 56
        if (!empty($this->linkTags)) {
684
            $lines[] = implode("\n", $this->linkTags);
685
        }
686 56
        if (!empty($this->noscriptTags[self::POS_HEAD])) {
687
            $lines[] = implode("\n", $this->noscriptTags[self::POS_HEAD]);
688
        }
689 56
        if (!empty($this->ldJson[self::POS_HEAD])) {
690
            $lines[] = implode("\n", $this->ldJson[self::POS_HEAD]);
691
        }
692 56
        if (!empty($this->cssFiles)) {
693 20
            $lines[] = implode("\n", $this->cssFiles);
694
        }
695 56
        if (!empty($this->css)) {
696
            $lines[] = implode("\n", $this->css);
697
        }
698 56
        if (!empty($this->jsFiles[self::POS_HEAD])) {
699 3
            $lines[] = implode("\n", $this->jsFiles[self::POS_HEAD]);
700
        }
701 56
        if (!empty($this->js[self::POS_HEAD])) {
702 1
            $lines[] = Html::script(implode("\n", $this->js[self::POS_HEAD]));
703
        }
704
705 56
        return empty($lines) ? '' : implode("\n", $lines);
706
    }
707
708
    /**
709
     * Renders the content to be inserted at the beginning of the body section.
710
     * The content is rendered using the registered JS code blocks and files.
711
     * @return string the rendered content
712
     */
713 56
    protected function renderBodyBeginHtml()
714
    {
715 56
        $lines = [];
716 56
        if (!empty($this->ldJson[self::POS_BEGIN])) {
717
            $lines[] = implode("\n", $this->ldJson[self::POS_BEGIN]);
718
        }
719 56
        if (!empty($this->noscriptTags[self::POS_BEGIN])) {
720
            $lines[] = implode("\n", $this->noscriptTags[self::POS_BEGIN]);
721
        }
722 56
        if (!empty($this->jsFiles[self::POS_BEGIN])) {
723 3
            $lines[] = implode("\n", $this->jsFiles[self::POS_BEGIN]);
724
        }
725 56
        if (!empty($this->js[self::POS_BEGIN])) {
726
            $lines[] = Html::script(implode("\n", $this->js[self::POS_BEGIN]));
727
        }
728
729 56
        return empty($lines) ? '' : implode("\n", $lines);
730
    }
731
732
    /**
733
     * Renders the content to be inserted at the end of the body section.
734
     * The content is rendered using the registered JS code blocks and files.
735
     * @param bool $ajaxMode whether the view is rendering in AJAX mode.
736
     * If true, the JS scripts registered at [[POS_READY]] and [[POS_LOAD]] positions
737
     * will be rendered at the end of the view like normal scripts.
738
     * @return string the rendered content
739
     */
740 56
    protected function renderBodyEndHtml($ajaxMode)
741
    {
742 56
        $lines = [];
743
744 56
        if (!empty($this->jsFiles[self::POS_END])) {
745 19
            $lines[] = implode("\n", $this->jsFiles[self::POS_END]);
746
        }
747 56
        if (!empty($this->ldJson[self::POS_END])) {
748
            $lines[] = implode("\n", $this->ldJson[self::POS_END]);
749
        }
750 56
        if (!empty($this->noscriptTags[self::POS_END])) {
751
            $lines[] = implode("\n", $this->noscriptTags[self::POS_END]);
752
        }
753 56
        if ($ajaxMode) {
754
            $scripts = [];
755
            if (!empty($this->js[self::POS_END])) {
756
                $scripts[] = implode("\n", $this->js[self::POS_END]);
757
            }
758
            if (!empty($this->js[self::POS_READY])) {
759
                $scripts[] = implode("\n", $this->js[self::POS_READY]);
760
            }
761
            if (!empty($this->js[self::POS_LOAD])) {
762
                $scripts[] = implode("\n", $this->js[self::POS_LOAD]);
763
            }
764
            if (!empty($scripts)) {
765
                $lines[] = Html::script(implode("\n", $scripts));
766
            }
767
        } else {
768 56
            if (!empty($this->js[self::POS_END])) {
769
                $lines[] = Html::script(implode("\n", $this->js[self::POS_END]));
770
            }
771 56
            if (!empty($this->js[self::POS_READY])) {
772
                $js = "jQuery(function ($) {\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
773
                $lines[] = Html::script($js);
774
            }
775 56
            if (!empty($this->js[self::POS_LOAD])) {
776
                $js = "jQuery(window).on('load', function () {\n" . implode("\n", $this->js[self::POS_LOAD]) . "\n});";
777
                $lines[] = Html::script($js);
778
            }
779
        }
780
781 56
        return empty($lines) ? '' : implode("\n", $lines);
782
    }
783
}
784