Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
Engine
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
14 / 14
15
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 unescaped
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 method
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 methods
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 wrapper
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 escape
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 filter
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 template
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderEscaped
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderUnescaped
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resolve
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 exists
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Boiler;
6
7use Celema\Boiler\Exception\LookupException;
8use Celema\Boiler\Exception\UnexpectedValueException;
9
10/**
11 * @api
12 * @psalm-type DirsInput = non-empty-string|array<array-key, non-empty-string>
13 */
14final class Engine
15{
16    private readonly Contract\Environment $environment;
17    private readonly Contract\Resolver $resolver;
18    private readonly Methods $methods;
19    private ?Contract\Wrapper $wrapper = null;
20
21    /**
22     * @param array<string, mixed> $defaults
23     * @param list<class-string> $trusted
24     */
25    public function __construct(
26        Contract\Resolver $resolver,
27        Contract\Environment $environment,
28        public readonly bool $autoescape,
29        public readonly array $defaults = [],
30        public readonly array $trusted = [],
31    ) {
32        $this->resolver = $resolver;
33        $this->environment = $environment;
34        $this->methods = new Methods();
35    }
36
37    /**
38     * @psalm-param DirsInput $dirs
39     * @param array<string, mixed> $defaults
40     * @param list<class-string> $trusted
41     */
42    public static function create(
43        array|string $dirs,
44        array $defaults = [],
45        array $trusted = [],
46    ): self {
47        return new self(new Resolver($dirs), new Environment(), true, $defaults, $trusted);
48    }
49
50    /**
51     * @psalm-param DirsInput $dirs
52     * @param array<string, mixed> $defaults
53     * @param list<class-string> $trusted
54     */
55    public static function unescaped(
56        array|string $dirs,
57        array $defaults = [],
58        array $trusted = [],
59    ): self {
60        return new self(new Resolver($dirs), new Environment(), false, $defaults, $trusted);
61    }
62
63    /** @param non-empty-string $name */
64    public function method(string $name, callable $callable, bool $safe = false): static
65    {
66        $this->methods->add($name, $callable, $safe);
67
68        return $this;
69    }
70
71    /** @internal */
72    public function methods(): Methods
73    {
74        return $this->methods;
75    }
76
77    public function wrapper(): Contract\Wrapper
78    {
79        return $this->wrapper ??= $this->environment->wrapper();
80    }
81
82    public function escape(string $name, Contract\Escaper $with): static
83    {
84        $this->environment->registerEscaper($name, $with);
85
86        return $this;
87    }
88
89    public function filter(string $name, Contract\Filter $with): static
90    {
91        $this->environment->registerFilter($name, $with);
92
93        return $this;
94    }
95
96    /** @param non-empty-string $path */
97    public function template(string $path): Template
98    {
99        return new Template($this->resolve($path), engine: $this);
100    }
101
102    /**
103     * @param non-empty-string $path
104     * @param array<string, mixed> $context
105     */
106    public function render(
107        string $path,
108        array $context = [],
109    ): string {
110        return $this->template($path)->render($context);
111    }
112
113    /**
114     * @param non-empty-string $path
115     * @param array<string, mixed> $context
116     */
117    public function renderEscaped(
118        string $path,
119        array $context = [],
120    ): string {
121        return $this->template($path)->renderEscaped($context);
122    }
123
124    /**
125     * @param non-empty-string $path
126     * @param array<string, mixed> $context
127     */
128    public function renderUnescaped(
129        string $path,
130        array $context = [],
131    ): string {
132        return $this->template($path)->renderUnescaped($context);
133    }
134
135    /** @return non-empty-string */
136    public function resolve(string $path): string
137    {
138        return $this->resolver->resolve($path);
139    }
140
141    /** @param non-empty-string $path */
142    public function exists(string $path): bool
143    {
144        try {
145            $this->resolve($path);
146
147            return true;
148        } catch (LookupException|UnexpectedValueException) {
149            return false;
150        }
151    }
152}