Completed
Push — master ( a5eb31...208ea1 )
by Arnold
03:45
created

Twig::addLoaderPaths()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

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