Twig::expose()   B
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 12
cts 12
cp 1
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 12
nc 4
nop 3
crap 6
1
<?php
2
3
namespace Jasny\View;
4
5
use Jasny\ViewInterface;
6
use Jasny\View\PluginInterface;
7
use Jasny\View\Plugin\DefaultTwigExtensions;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * Abstraction to use Twig with PSR-7
12
 */
13
class Twig implements ViewInterface
14
{
15
    /**
16
     * Twig environment
17
     * @var \Twig_Environment
18
     */
19
    protected $twig;
20
    
21
    /**
22
     * @var PluginInterface[]
23
     */
24
    protected $plugins = [];
25
    
26
    
27
    /**
28
     * Class constructor
29
     * 
30
     * @param \Twig_Environment|array $options
31
     */
32 22
    public function __construct($options)
33
    {
34 22
        $twig = is_array($options) ? $this->createTwigEnvironment($options) : $options;
35
        
36 21
        if (!$twig instanceof \Twig_Environment) {
37 1
            throw new \InvalidArgumentException("Was expecting an array with options or a Twig_Environment, got a "
38 1
                . (is_object($twig) ? get_class($twig) . ' ' : '') . gettype($twig));
39
        }
40
        
41 20
        $this->twig = $twig;
42 20
    }
43
    
44
    /**
45
     * Create a new Twig environment
46
     * 
47
     * @param array $options
48
     * @return \Twig_Environment
49
     */
50 2
    protected function createTwigEnvironment(array $options)
51
    {
52 2
        if (!isset($options['path'])) {
53 1
            throw new \BadMethodCallException("'path' option is required");
54
        }
55
56 1
        $loader = new \Twig_Loader_Filesystem();
57 1
        $this->addLoaderPaths($loader, $options['path']);
58
        
59 1
        return new \Twig_Environment($loader, $options);
60
    }
61
    
62
    /**
63
     * Add paths to Twig loader
64
     * 
65
     * @param \Twig_Loader_Filesystem $loader
66
     * @param string|array            $paths
67
     * @return type
68
     */
69 1
    protected function addLoaderPaths(\Twig_Loader_Filesystem $loader, $paths)
70
    {
71 1
        foreach ((array)$paths as $namespace => $path) {
72 1
            if (is_int($namespace)) {
73 1
                $namespace = \Twig_Loader_Filesystem::MAIN_NAMESPACE;
74 1
            }
75
76 1
            $loader->addPath($path, $namespace);
77 1
        }
78 1
    }
79
    
80
    /**
81
     * Get Twig environment
82
     *
83
     * @return \Twig_Environment
84
     */
85 15
    public function getTwig()
86
    {
87 15
        return $this->twig;
88
    }
89
90
    
91
    /**
92
     * Assert valid variable, function and filter name
93
     * 
94
     * @param string $name
95
     * @throws \InvalidArgumentException
96
     */
97 10
    protected function assertViewVariableName($name)
98
    {
99 10
        if (!is_string($name)) {
100 1
            $type = (is_object($name) ? get_class($name) . ' ' : '') . gettype($name);
101 1
            throw new \InvalidArgumentException("Expected name to be a string, not a $type");
102
        }
103
104 9
        if (!preg_match('/^[a-z]\w*$/i', $name)) {
105 1
            throw new \InvalidArgumentException("Invalid name '$name'");
106
        }
107 8
    }
108
109
    /**
110
     * Expose a function to the view.
111
     *
112
     * @param string        $name      Function name in template
113
     * @param callable|null $function
114
     * @param string        $as        'function' or 'filter'
115
     * @return $this
116
     */
117 10
    public function expose($name, $function = null, $as = 'function')
118
    {
119 10
        $this->assertViewVariableName($name);
120
        
121 8
        if ($as === 'function') {
122 3
            $function = new \Twig_SimpleFunction($name, $function ?: $name);
123 3
            $this->getTwig()->addFunction($function);
124 8
        } elseif ($as === 'filter') {
125 3
            $filter = new \Twig_SimpleFilter($name, $function ?: $name);
126 4
            $this->getTwig()->addFilter($filter);
127 4
        } else {
128 3
            $not = is_string($as) ? "'$as'" : gettype($as);
129 3
            throw new \InvalidArgumentException("You can create either a 'function' or 'filter', not a $not");
130
        }
131
132 6
        return $this;
133
    }
134
135
    
136
    /**
137
     * Add a plugin to the view
138
     * 
139
     * @param PluginInterface $plugin
140
     * @return $this
141
     */
142 3
    public function addPlugin(PluginInterface $plugin)
143
    {
144 3
        $plugin->onAdd($this); // Called before adding the plugin, because it may throw an exception
145 3
        $this->plugins[] = $plugin;
146
        
147 3
        return $this;
148
    }
149
    
150
    /**
151
     * Get all plugins
152
     * 
153
     * @return PluginInterface[]
154
     */
155 3
    public function getPlugins()
156
    {
157 3
        return $this->plugins;
158
    }
159
    
160
    /**
161
     * Invoke the onRender method of all plugins
162
     * 
163
     * @param string $template  Template filename
164
     * @param array  $context
165
     */
166 6
    protected function invokePluginsOnRender($template, array $context)
167
    {
168 6
        foreach ($this->plugins as $plugin) {
169 1
            $plugin->onRender($this, $template, $context);
170 6
        }
171 6
    }
172
    
173
    /**
174
     * Add the official Twig extension and Jansy Twig extensions.
175
     * @deprecated
176
     * 
177
     * @return $this
178
     */
179 1
    public function addDefaultExtensions()
180
    {
181 1
        return $this->addPlugin(new DefaultTwigExtensions());
182
    }
183
184
    
185
    /**
186
     * Get the filename
187
     * 
188
     * @return string
189
     */
190 6
    protected function getFilename($name)
191
    {
192 6
        if (pathinfo($name, PATHINFO_EXTENSION) === '') {
193 3
            $name .= substr($name, -1) === '/' ? 'index.html.twig' : '.html.twig';
194 3
        }
195
        
196 6
        return $name;
197
    }
198
    
199
    /**
200
     * Render and output template
201
     *
202
     * @param ResponseInterface $response
203
     * @param string            $name      Template name
204
     * @param array             $context   Template context as associated array
205
     * @return ResponseInterface
206
     */
207 6
    public function render(ResponseInterface $response, $name, array $context = [])
208
    {
209 6
        $file = $this->getFilename($name);
210
211 6
        $twig = $this->getTwig();
212 6
        $tmpl = $twig->load($file);
213
        
214 6
        $this->invokePluginsOnRender($file, $context);
215
216 6
        $contents = $tmpl->render($context);
217
        
218 6
        $newResponse = $response->withHeader('Content-Type', 'text/html; charset=' . $twig->getCharset());
219 6
        $newResponse->getBody()->write($contents);
220
        
221 6
        return $newResponse;
222
    }
223
}
224
225