Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 399 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\View\Compilers;
4
 
5
use Illuminate\Container\Container;
6
use Illuminate\Contracts\Support\Htmlable;
7
use Illuminate\Contracts\View\Factory as ViewFactory;
8
use Illuminate\Contracts\View\View;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Str;
11
use Illuminate\Support\Traits\ReflectsClosures;
12
use Illuminate\View\Component;
13
use InvalidArgumentException;
14
 
15
class BladeCompiler extends Compiler implements CompilerInterface
16
{
17
    use Concerns\CompilesAuthorizations,
18
        Concerns\CompilesClasses,
19
        Concerns\CompilesComments,
20
        Concerns\CompilesComponents,
21
        Concerns\CompilesConditionals,
22
        Concerns\CompilesEchos,
23
        Concerns\CompilesErrors,
24
        Concerns\CompilesFragments,
25
        Concerns\CompilesHelpers,
26
        Concerns\CompilesIncludes,
27
        Concerns\CompilesInjections,
28
        Concerns\CompilesJson,
29
        Concerns\CompilesJs,
30
        Concerns\CompilesLayouts,
31
        Concerns\CompilesLoops,
32
        Concerns\CompilesRawPhp,
33
        Concerns\CompilesStacks,
399 lars 34
        Concerns\CompilesStyles,
148 lars 35
        Concerns\CompilesTranslations,
36
        ReflectsClosures;
37
 
38
    /**
39
     * All of the registered extensions.
40
     *
41
     * @var array
42
     */
43
    protected $extensions = [];
44
 
45
    /**
46
     * All custom "directive" handlers.
47
     *
48
     * @var array
49
     */
50
    protected $customDirectives = [];
51
 
52
    /**
53
     * All custom "condition" handlers.
54
     *
55
     * @var array
56
     */
57
    protected $conditions = [];
58
 
59
    /**
60
     * All of the registered precompilers.
61
     *
62
     * @var array
63
     */
64
    protected $precompilers = [];
65
 
66
    /**
67
     * The file currently being compiled.
68
     *
69
     * @var string
70
     */
71
    protected $path;
72
 
73
    /**
74
     * All of the available compiler functions.
75
     *
76
     * @var string[]
77
     */
78
    protected $compilers = [
79
        // 'Comments',
80
        'Extensions',
81
        'Statements',
82
        'Echos',
83
    ];
84
 
85
    /**
86
     * Array of opening and closing tags for raw echos.
87
     *
88
     * @var string[]
89
     */
90
    protected $rawTags = ['{!!', '!!}'];
91
 
92
    /**
93
     * Array of opening and closing tags for regular echos.
94
     *
95
     * @var string[]
96
     */
97
    protected $contentTags = ['{{', '}}'];
98
 
99
    /**
100
     * Array of opening and closing tags for escaped echos.
101
     *
102
     * @var string[]
103
     */
104
    protected $escapedTags = ['{{{', '}}}'];
105
 
106
    /**
107
     * The "regular" / legacy echo string format.
108
     *
109
     * @var string
110
     */
111
    protected $echoFormat = 'e(%s)';
112
 
113
    /**
114
     * Array of footer lines to be added to the template.
115
     *
116
     * @var array
117
     */
118
    protected $footer = [];
119
 
120
    /**
121
     * Array to temporarily store the raw blocks found in the template.
122
     *
123
     * @var array
124
     */
125
    protected $rawBlocks = [];
126
 
127
    /**
128
     * The array of anonymous component paths to search for components in.
129
     *
130
     * @var array
131
     */
132
    protected $anonymousComponentPaths = [];
133
 
134
    /**
135
     * The array of anonymous component namespaces to autoload from.
136
     *
137
     * @var array
138
     */
139
    protected $anonymousComponentNamespaces = [];
140
 
141
    /**
142
     * The array of class component aliases and their class names.
143
     *
144
     * @var array
145
     */
146
    protected $classComponentAliases = [];
147
 
148
    /**
149
     * The array of class component namespaces to autoload from.
150
     *
151
     * @var array
152
     */
153
    protected $classComponentNamespaces = [];
154
 
155
    /**
156
     * Indicates if component tags should be compiled.
157
     *
158
     * @var bool
159
     */
160
    protected $compilesComponentTags = true;
161
 
162
    /**
163
     * Compile the view at the given path.
164
     *
165
     * @param  string|null  $path
166
     * @return void
167
     */
168
    public function compile($path = null)
169
    {
170
        if ($path) {
171
            $this->setPath($path);
172
        }
173
 
174
        if (! is_null($this->cachePath)) {
175
            $contents = $this->compileString($this->files->get($this->getPath()));
176
 
177
            if (! empty($this->getPath())) {
178
                $contents = $this->appendFilePath($contents);
179
            }
180
 
181
            $this->ensureCompiledDirectoryExists(
182
                $compiledPath = $this->getCompiledPath($this->getPath())
183
            );
184
 
185
            $this->files->put($compiledPath, $contents);
186
        }
187
    }
188
 
189
    /**
190
     * Append the file path to the compiled string.
191
     *
192
     * @param  string  $contents
193
     * @return string
194
     */
195
    protected function appendFilePath($contents)
196
    {
197
        $tokens = $this->getOpenAndClosingPhpTokens($contents);
198
 
199
        if ($tokens->isNotEmpty() && $tokens->last() !== T_CLOSE_TAG) {
200
            $contents .= ' ?>';
201
        }
202
 
203
        return $contents."<?php /**PATH {$this->getPath()} ENDPATH**/ ?>";
204
    }
205
 
206
    /**
207
     * Get the open and closing PHP tag tokens from the given string.
208
     *
209
     * @param  string  $contents
210
     * @return \Illuminate\Support\Collection
211
     */
212
    protected function getOpenAndClosingPhpTokens($contents)
213
    {
214
        return collect(token_get_all($contents))
215
            ->pluck(0)
216
            ->filter(function ($token) {
217
                return in_array($token, [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG]);
218
            });
219
    }
220
 
221
    /**
222
     * Get the path currently being compiled.
223
     *
224
     * @return string
225
     */
226
    public function getPath()
227
    {
228
        return $this->path;
229
    }
230
 
231
    /**
232
     * Set the path currently being compiled.
233
     *
234
     * @param  string  $path
235
     * @return void
236
     */
237
    public function setPath($path)
238
    {
239
        $this->path = $path;
240
    }
241
 
242
    /**
243
     * Compile the given Blade template contents.
244
     *
245
     * @param  string  $value
246
     * @return string
247
     */
248
    public function compileString($value)
249
    {
250
        [$this->footer, $result] = [[], ''];
251
 
252
        // First we will compile the Blade component tags. This is a precompile style
253
        // step which compiles the component Blade tags into @component directives
254
        // that may be used by Blade. Then we should call any other precompilers.
255
        $value = $this->compileComponentTags(
256
            $this->compileComments($this->storeUncompiledBlocks($value))
257
        );
258
 
259
        foreach ($this->precompilers as $precompiler) {
260
            $value = $precompiler($value);
261
        }
262
 
263
        // Here we will loop through all of the tokens returned by the Zend lexer and
264
        // parse each one into the corresponding valid PHP. We will then have this
265
        // template as the correctly rendered PHP that can be rendered natively.
266
        foreach (token_get_all($value) as $token) {
267
            $result .= is_array($token) ? $this->parseToken($token) : $token;
268
        }
269
 
270
        if (! empty($this->rawBlocks)) {
271
            $result = $this->restoreRawContent($result);
272
        }
273
 
274
        // If there are any footer lines that need to get added to a template we will
275
        // add them here at the end of the template. This gets used mainly for the
276
        // template inheritance via the extends keyword that should be appended.
277
        if (count($this->footer) > 0) {
278
            $result = $this->addFooters($result);
279
        }
280
 
281
        if (! empty($this->echoHandlers)) {
282
            $result = $this->addBladeCompilerVariable($result);
283
        }
284
 
285
        return str_replace(
286
            ['##BEGIN-COMPONENT-CLASS##', '##END-COMPONENT-CLASS##'],
287
            '',
288
            $result);
289
    }
290
 
291
    /**
292
     * Evaluate and render a Blade string to HTML.
293
     *
294
     * @param  string  $string
295
     * @param  array  $data
296
     * @param  bool  $deleteCachedView
297
     * @return string
298
     */
299
    public static function render($string, $data = [], $deleteCachedView = false)
300
    {
301
        $component = new class($string) extends Component
302
        {
303
            protected $template;
304
 
305
            public function __construct($template)
306
            {
307
                $this->template = $template;
308
            }
309
 
310
            public function render()
311
            {
312
                return $this->template;
313
            }
314
        };
315
 
316
        $view = Container::getInstance()
317
                    ->make(ViewFactory::class)
318
                    ->make($component->resolveView(), $data);
319
 
320
        return tap($view->render(), function () use ($view, $deleteCachedView) {
321
            if ($deleteCachedView) {
322
                unlink($view->getPath());
323
            }
324
        });
325
    }
326
 
327
    /**
328
     * Render a component instance to HTML.
329
     *
330
     * @param  \Illuminate\View\Component  $component
331
     * @return string
332
     */
333
    public static function renderComponent(Component $component)
334
    {
335
        $data = $component->data();
336
 
337
        $view = value($component->resolveView(), $data);
338
 
339
        if ($view instanceof View) {
340
            return $view->with($data)->render();
341
        } elseif ($view instanceof Htmlable) {
342
            return $view->toHtml();
343
        } else {
344
            return Container::getInstance()
345
                ->make(ViewFactory::class)
346
                ->make($view, $data)
347
                ->render();
348
        }
349
    }
350
 
351
    /**
352
     * Store the blocks that do not receive compilation.
353
     *
354
     * @param  string  $value
355
     * @return string
356
     */
357
    protected function storeUncompiledBlocks($value)
358
    {
359
        if (str_contains($value, '@verbatim')) {
360
            $value = $this->storeVerbatimBlocks($value);
361
        }
362
 
363
        if (str_contains($value, '@php')) {
364
            $value = $this->storePhpBlocks($value);
365
        }
366
 
367
        return $value;
368
    }
369
 
370
    /**
371
     * Store the verbatim blocks and replace them with a temporary placeholder.
372
     *
373
     * @param  string  $value
374
     * @return string
375
     */
376
    protected function storeVerbatimBlocks($value)
377
    {
378
        return preg_replace_callback('/(?<!@)@verbatim(.*?)@endverbatim/s', function ($matches) {
379
            return $this->storeRawBlock($matches[1]);
380
        }, $value);
381
    }
382
 
383
    /**
384
     * Store the PHP blocks and replace them with a temporary placeholder.
385
     *
386
     * @param  string  $value
387
     * @return string
388
     */
389
    protected function storePhpBlocks($value)
390
    {
391
        return preg_replace_callback('/(?<!@)@php(.*?)@endphp/s', function ($matches) {
392
            return $this->storeRawBlock("<?php{$matches[1]}?>");
393
        }, $value);
394
    }
395
 
396
    /**
397
     * Store a raw block and return a unique raw placeholder.
398
     *
399
     * @param  string  $value
400
     * @return string
401
     */
402
    protected function storeRawBlock($value)
403
    {
404
        return $this->getRawPlaceholder(
405
            array_push($this->rawBlocks, $value) - 1
406
        );
407
    }
408
 
409
    /**
410
     * Compile the component tags.
411
     *
412
     * @param  string  $value
413
     * @return string
414
     */
415
    protected function compileComponentTags($value)
416
    {
417
        if (! $this->compilesComponentTags) {
418
            return $value;
419
        }
420
 
421
        return (new ComponentTagCompiler(
422
            $this->classComponentAliases, $this->classComponentNamespaces, $this
423
        ))->compile($value);
424
    }
425
 
426
    /**
427
     * Replace the raw placeholders with the original code stored in the raw blocks.
428
     *
429
     * @param  string  $result
430
     * @return string
431
     */
432
    protected function restoreRawContent($result)
433
    {
434
        $result = preg_replace_callback('/'.$this->getRawPlaceholder('(\d+)').'/', function ($matches) {
435
            return $this->rawBlocks[$matches[1]];
436
        }, $result);
437
 
438
        $this->rawBlocks = [];
439
 
440
        return $result;
441
    }
442
 
443
    /**
444
     * Get a placeholder to temporarily mark the position of raw blocks.
445
     *
446
     * @param  int|string  $replace
447
     * @return string
448
     */
449
    protected function getRawPlaceholder($replace)
450
    {
451
        return str_replace('#', $replace, '@__raw_block_#__@');
452
    }
453
 
454
    /**
455
     * Add the stored footers onto the given content.
456
     *
457
     * @param  string  $result
458
     * @return string
459
     */
460
    protected function addFooters($result)
461
    {
462
        return ltrim($result, "\n")
463
                ."\n".implode("\n", array_reverse($this->footer));
464
    }
465
 
466
    /**
467
     * Parse the tokens from the template.
468
     *
469
     * @param  array  $token
470
     * @return string
471
     */
472
    protected function parseToken($token)
473
    {
474
        [$id, $content] = $token;
475
 
476
        if ($id == T_INLINE_HTML) {
477
            foreach ($this->compilers as $type) {
478
                $content = $this->{"compile{$type}"}($content);
479
            }
480
        }
481
 
482
        return $content;
483
    }
484
 
485
    /**
486
     * Execute the user defined extensions.
487
     *
488
     * @param  string  $value
489
     * @return string
490
     */
491
    protected function compileExtensions($value)
492
    {
493
        foreach ($this->extensions as $compiler) {
494
            $value = $compiler($value, $this);
495
        }
496
 
497
        return $value;
498
    }
499
 
500
    /**
501
     * Compile Blade statements that start with "@".
502
     *
503
     * @param  string  $template
504
     * @return string
505
     */
506
    protected function compileStatements($template)
507
    {
508
        preg_match_all('/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( [\S\s]*? ) \))?/x', $template, $matches);
509
 
621 lars 510
        $offset = 0;
511
 
148 lars 512
        for ($i = 0; isset($matches[0][$i]); $i++) {
513
            $match = [
514
                $matches[0][$i],
515
                $matches[1][$i],
516
                $matches[2][$i],
517
                $matches[3][$i] ?: null,
518
                $matches[4][$i] ?: null,
519
            ];
520
 
521
            // Here we check to see if we have properly found the closing parenthesis by
522
            // regex pattern or not, and will recursively continue on to the next ")"
523
            // then check again until the tokenizer confirms we find the right one.
524
            while (isset($match[4]) &&
525
                   Str::endsWith($match[0], ')') &&
526
                   ! $this->hasEvenNumberOfParentheses($match[0])) {
399 lars 527
                if (($after = Str::after($template, $match[0])) === $template) {
528
                    break;
529
                }
148 lars 530
 
399 lars 531
                $rest = Str::before($after, ')');
532
 
533
                if (isset($matches[0][$i + 1]) && Str::contains($rest.')', $matches[0][$i + 1])) {
534
                    unset($matches[0][$i + 1]);
535
                    $i++;
536
                }
537
 
148 lars 538
                $match[0] = $match[0].$rest.')';
539
                $match[3] = $match[3].$rest.')';
540
                $match[4] = $match[4].$rest;
541
            }
542
 
621 lars 543
            [$template, $offset] = $this->replaceFirstStatement(
544
                $match[0],
545
                $this->compileStatement($match),
546
                $template,
547
                $offset
548
            );
148 lars 549
        }
550
 
551
        return $template;
552
    }
553
 
554
    /**
621 lars 555
     * Replace the first match for a statement compilation operation.
556
     *
557
     * @param  string  $search
558
     * @param  string  $replace
559
     * @param  string  $subject
560
     * @param  int  $offset
561
     * @return array
562
     */
563
    protected function replaceFirstStatement($search, $replace, $subject, $offset)
564
    {
565
        $search = (string) $search;
566
 
567
        if ($search === '') {
568
            return $subject;
569
        }
570
 
571
        $position = strpos($subject, $search, $offset);
572
 
573
        if ($position !== false) {
574
            return [
575
                substr_replace($subject, $replace, $position, strlen($search)),
576
                $position + strlen($replace),
577
            ];
578
        }
579
 
580
        return [$subject, 0];
581
    }
582
 
583
    /**
148 lars 584
     * Determine if the given expression has the same number of opening and closing parentheses.
585
     *
586
     * @param  string  $expression
587
     * @return bool
588
     */
589
    protected function hasEvenNumberOfParentheses(string $expression)
590
    {
591
        $tokens = token_get_all('<?php '.$expression);
592
 
593
        if (Arr::last($tokens) !== ')') {
594
            return false;
595
        }
596
 
597
        $opening = 0;
598
        $closing = 0;
599
 
600
        foreach ($tokens as $token) {
601
            if ($token == ')') {
602
                $closing++;
603
            } elseif ($token == '(') {
604
                $opening++;
605
            }
606
        }
607
 
608
        return $opening === $closing;
609
    }
610
 
611
    /**
612
     * Compile a single Blade @ statement.
613
     *
614
     * @param  array  $match
615
     * @return string
616
     */
617
    protected function compileStatement($match)
618
    {
619
        if (str_contains($match[1], '@')) {
620
            $match[0] = isset($match[3]) ? $match[1].$match[3] : $match[1];
621
        } elseif (isset($this->customDirectives[$match[1]])) {
622
            $match[0] = $this->callCustomDirective($match[1], Arr::get($match, 3));
623
        } elseif (method_exists($this, $method = 'compile'.ucfirst($match[1]))) {
624
            $match[0] = $this->$method(Arr::get($match, 3));
625
        } else {
626
            return $match[0];
627
        }
628
 
629
        return isset($match[3]) ? $match[0] : $match[0].$match[2];
630
    }
631
 
632
    /**
633
     * Call the given directive with the given value.
634
     *
635
     * @param  string  $name
636
     * @param  string|null  $value
637
     * @return string
638
     */
639
    protected function callCustomDirective($name, $value)
640
    {
641
        $value ??= '';
642
 
643
        if (str_starts_with($value, '(') && str_ends_with($value, ')')) {
644
            $value = Str::substr($value, 1, -1);
645
        }
646
 
647
        return call_user_func($this->customDirectives[$name], trim($value));
648
    }
649
 
650
    /**
651
     * Strip the parentheses from the given expression.
652
     *
653
     * @param  string  $expression
654
     * @return string
655
     */
656
    public function stripParentheses($expression)
657
    {
658
        if (Str::startsWith($expression, '(')) {
659
            $expression = substr($expression, 1, -1);
660
        }
661
 
662
        return $expression;
663
    }
664
 
665
    /**
666
     * Register a custom Blade compiler.
667
     *
668
     * @param  callable  $compiler
669
     * @return void
670
     */
671
    public function extend(callable $compiler)
672
    {
673
        $this->extensions[] = $compiler;
674
    }
675
 
676
    /**
677
     * Get the extensions used by the compiler.
678
     *
679
     * @return array
680
     */
681
    public function getExtensions()
682
    {
683
        return $this->extensions;
684
    }
685
 
686
    /**
687
     * Register an "if" statement directive.
688
     *
689
     * @param  string  $name
690
     * @param  callable  $callback
691
     * @return void
692
     */
693
    public function if($name, callable $callback)
694
    {
695
        $this->conditions[$name] = $callback;
696
 
697
        $this->directive($name, function ($expression) use ($name) {
698
            return $expression !== ''
699
                    ? "<?php if (\Illuminate\Support\Facades\Blade::check('{$name}', {$expression})): ?>"
700
                    : "<?php if (\Illuminate\Support\Facades\Blade::check('{$name}')): ?>";
701
        });
702
 
703
        $this->directive('unless'.$name, function ($expression) use ($name) {
704
            return $expression !== ''
705
                ? "<?php if (! \Illuminate\Support\Facades\Blade::check('{$name}', {$expression})): ?>"
706
                : "<?php if (! \Illuminate\Support\Facades\Blade::check('{$name}')): ?>";
707
        });
708
 
709
        $this->directive('else'.$name, function ($expression) use ($name) {
710
            return $expression !== ''
711
                ? "<?php elseif (\Illuminate\Support\Facades\Blade::check('{$name}', {$expression})): ?>"
712
                : "<?php elseif (\Illuminate\Support\Facades\Blade::check('{$name}')): ?>";
713
        });
714
 
715
        $this->directive('end'.$name, function () {
716
            return '<?php endif; ?>';
717
        });
718
    }
719
 
720
    /**
721
     * Check the result of a condition.
722
     *
723
     * @param  string  $name
621 lars 724
     * @param  mixed  ...$parameters
148 lars 725
     * @return bool
726
     */
727
    public function check($name, ...$parameters)
728
    {
729
        return call_user_func($this->conditions[$name], ...$parameters);
730
    }
731
 
732
    /**
733
     * Register a class-based component alias directive.
734
     *
735
     * @param  string  $class
736
     * @param  string|null  $alias
737
     * @param  string  $prefix
738
     * @return void
739
     */
740
    public function component($class, $alias = null, $prefix = '')
741
    {
742
        if (! is_null($alias) && str_contains($alias, '\\')) {
743
            [$class, $alias] = [$alias, $class];
744
        }
745
 
746
        if (is_null($alias)) {
747
            $alias = str_contains($class, '\\View\\Components\\')
748
                            ? collect(explode('\\', Str::after($class, '\\View\\Components\\')))->map(function ($segment) {
749
                                return Str::kebab($segment);
750
                            })->implode(':')
751
                            : Str::kebab(class_basename($class));
752
        }
753
 
754
        if (! empty($prefix)) {
755
            $alias = $prefix.'-'.$alias;
756
        }
757
 
758
        $this->classComponentAliases[$alias] = $class;
759
    }
760
 
761
    /**
762
     * Register an array of class-based components.
763
     *
764
     * @param  array  $components
765
     * @param  string  $prefix
766
     * @return void
767
     */
768
    public function components(array $components, $prefix = '')
769
    {
770
        foreach ($components as $key => $value) {
771
            if (is_numeric($key)) {
772
                $this->component($value, null, $prefix);
773
            } else {
774
                $this->component($key, $value, $prefix);
775
            }
776
        }
777
    }
778
 
779
    /**
780
     * Get the registered class component aliases.
781
     *
782
     * @return array
783
     */
784
    public function getClassComponentAliases()
785
    {
786
        return $this->classComponentAliases;
787
    }
788
 
789
    /**
790
     * Register a new anonymous component path.
791
     *
792
     * @param  string  $path
793
     * @param  string|null  $prefix
794
     * @return void
795
     */
796
    public function anonymousComponentPath(string $path, string $prefix = null)
797
    {
798
        $prefixHash = md5($prefix ?: $path);
799
 
800
        $this->anonymousComponentPaths[] = [
801
            'path' => $path,
802
            'prefix' => $prefix,
803
            'prefixHash' => $prefixHash,
804
        ];
805
 
806
        Container::getInstance()
807
                ->make(ViewFactory::class)
808
                ->addNamespace($prefixHash, $path);
809
    }
810
 
811
    /**
812
     * Register an anonymous component namespace.
813
     *
814
     * @param  string  $directory
815
     * @param  string|null  $prefix
816
     * @return void
817
     */
818
    public function anonymousComponentNamespace(string $directory, string $prefix = null)
819
    {
820
        $prefix ??= $directory;
821
 
822
        $this->anonymousComponentNamespaces[$prefix] = Str::of($directory)
823
                ->replace('/', '.')
824
                ->trim('. ')
825
                ->toString();
826
    }
827
 
828
    /**
829
     * Register a class-based component namespace.
830
     *
831
     * @param  string  $namespace
832
     * @param  string  $prefix
833
     * @return void
834
     */
835
    public function componentNamespace($namespace, $prefix)
836
    {
837
        $this->classComponentNamespaces[$prefix] = $namespace;
838
    }
839
 
840
    /**
841
     * Get the registered anonymous component paths.
842
     *
843
     * @return array
844
     */
845
    public function getAnonymousComponentPaths()
846
    {
847
        return $this->anonymousComponentPaths;
848
    }
849
 
850
    /**
851
     * Get the registered anonymous component namespaces.
852
     *
853
     * @return array
854
     */
855
    public function getAnonymousComponentNamespaces()
856
    {
857
        return $this->anonymousComponentNamespaces;
858
    }
859
 
860
    /**
861
     * Get the registered class component namespaces.
862
     *
863
     * @return array
864
     */
865
    public function getClassComponentNamespaces()
866
    {
867
        return $this->classComponentNamespaces;
868
    }
869
 
870
    /**
871
     * Register a component alias directive.
872
     *
873
     * @param  string  $path
874
     * @param  string|null  $alias
875
     * @return void
876
     */
877
    public function aliasComponent($path, $alias = null)
878
    {
879
        $alias = $alias ?: Arr::last(explode('.', $path));
880
 
881
        $this->directive($alias, function ($expression) use ($path) {
882
            return $expression
883
                        ? "<?php \$__env->startComponent('{$path}', {$expression}); ?>"
884
                        : "<?php \$__env->startComponent('{$path}'); ?>";
885
        });
886
 
887
        $this->directive('end'.$alias, function ($expression) {
888
            return '<?php echo $__env->renderComponent(); ?>';
889
        });
890
    }
891
 
892
    /**
893
     * Register an include alias directive.
894
     *
895
     * @param  string  $path
896
     * @param  string|null  $alias
897
     * @return void
898
     */
899
    public function include($path, $alias = null)
900
    {
901
        $this->aliasInclude($path, $alias);
902
    }
903
 
904
    /**
905
     * Register an include alias directive.
906
     *
907
     * @param  string  $path
908
     * @param  string|null  $alias
909
     * @return void
910
     */
911
    public function aliasInclude($path, $alias = null)
912
    {
913
        $alias = $alias ?: Arr::last(explode('.', $path));
914
 
915
        $this->directive($alias, function ($expression) use ($path) {
916
            $expression = $this->stripParentheses($expression) ?: '[]';
917
 
918
            return "<?php echo \$__env->make('{$path}', {$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>";
919
        });
920
    }
921
 
922
    /**
923
     * Register a handler for custom directives.
924
     *
925
     * @param  string  $name
926
     * @param  callable  $handler
927
     * @return void
928
     *
929
     * @throws \InvalidArgumentException
930
     */
931
    public function directive($name, callable $handler)
932
    {
933
        if (! preg_match('/^\w+(?:::\w+)?$/x', $name)) {
934
            throw new InvalidArgumentException("The directive name [{$name}] is not valid. Directive names must only contain alphanumeric characters and underscores.");
935
        }
936
 
937
        $this->customDirectives[$name] = $handler;
938
    }
939
 
940
    /**
941
     * Get the list of custom directives.
942
     *
943
     * @return array
944
     */
945
    public function getCustomDirectives()
946
    {
947
        return $this->customDirectives;
948
    }
949
 
950
    /**
951
     * Register a new precompiler.
952
     *
953
     * @param  callable  $precompiler
954
     * @return void
955
     */
956
    public function precompiler(callable $precompiler)
957
    {
958
        $this->precompilers[] = $precompiler;
959
    }
960
 
961
    /**
962
     * Set the echo format to be used by the compiler.
963
     *
964
     * @param  string  $format
965
     * @return void
966
     */
967
    public function setEchoFormat($format)
968
    {
969
        $this->echoFormat = $format;
970
    }
971
 
972
    /**
973
     * Set the "echo" format to double encode entities.
974
     *
975
     * @return void
976
     */
977
    public function withDoubleEncoding()
978
    {
979
        $this->setEchoFormat('e(%s, true)');
980
    }
981
 
982
    /**
983
     * Set the "echo" format to not double encode entities.
984
     *
985
     * @return void
986
     */
987
    public function withoutDoubleEncoding()
988
    {
989
        $this->setEchoFormat('e(%s, false)');
990
    }
991
 
992
    /**
993
     * Indicate that component tags should not be compiled.
994
     *
995
     * @return void
996
     */
997
    public function withoutComponentTags()
998
    {
999
        $this->compilesComponentTags = false;
1000
    }
1001
}