Subversion-Projekte lars-tiefland.laravel_shop

Revision

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