Passed
Push — master ( a45bc4...a9b3ed )
by David
15:26
created

PluginInherits::replaceBlock()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 17
nc 5
nop 1
dl 0
loc 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright (c) 2013-2017
4
 *
5
 * @category  Library
6
 * @package   Dwoo\Plugins\Functions
7
 * @author    Jordi Boggiano <[email protected]>
8
 * @author    David Sanchez <[email protected]>
9
 * @copyright 2008-2013 Jordi Boggiano
10
 * @copyright 2013-2017 David Sanchez
11
 * @license   http://dwoo.org/LICENSE LGPLv3
12
 * @version   1.3.6
13
 * @date      2017-03-21
14
 * @link      http://dwoo.org/
15
 */
16
17
namespace Dwoo\Plugins\Functions;
18
19
use Dwoo\Compiler;
20
use Dwoo\ICompilable;
21
use Dwoo\Plugin;
22
use Dwoo\Exception as Exception;
23
use Dwoo\Security\Exception as SecurityException;
24
use Dwoo\Compilation\Exception as CompilationException;
25
use Dwoo\Template\File;
26
27
/**
28
 * Extends another template, read more about template inheritance at {@link
29
 * http://wiki.dwoo.org/index.php/TemplateInheritance}
30
 * <pre>
31
 *  * file : the template to extend
32
 * </pre>
33
 * This software is provided 'as-is', without any express or implied warranty.
34
 * In no event will the authors be held liable for any damages arising from the use of this software.
35
 */
36
class PluginInherits extends PluginExtends implements ICompilable
37
{
38
    /**
39
     * @param Compiler $compiler
40
     * @param          $file
41
     *
42
     * @throws CompilationException
43
     */
44
    public static function compile(Compiler $compiler, $file)
45
    {
46
        list($l, $r) = $compiler->getDelimiters();
47
        self::$l     = preg_quote($l, '/');
48
        self::$r     = preg_quote($r, '/');
49
        self::$regex = '/
50
			' . self::$l . 'block\s(["\']?)(.+?)\1' . self::$r . '(?:\r?\n?)
51
			((?:
52
				(?R)
53
				|
54
				[^' . self::$l . ']*
55
				(?:
56
					(?! ' . self::$l . '\/?block\b )
57
					' . self::$l . '
58
					[^' . self::$l . ']*+
59
				)*
60
			)*)
61
			' . self::$l . '\/block' . self::$r . '
62
			/six';
63
64
        if ($compiler->getLooseOpeningHandling()) {
65
            self::$l .= '\s*';
66
            self::$r = '\s*' . self::$r;
67
        }
68
        $inheritanceTree = array(array('source' => $compiler->getTemplateSource()));
69
        $curPath         = dirname($compiler->getCore()->getTemplate()->getResourceIdentifier()) . DIRECTORY_SEPARATOR;
70
        $curTpl          = $compiler->getCore()->getTemplate();
71
72
        while (!empty($file)) {
73
            if ($file === '""' || $file === "''" || (substr($file, 0, 1) !== '"' && substr($file, 0, 1) !== '\'')) {
74
                throw new CompilationException($compiler, 'Inherits : The file name must be a non-empty string');
75
            }
76
77
            if (preg_match('#^["\']([a-z]{2,}):(.*?)["\']$#i', $file, $m)) {
78
                // resource:identifier given, extract them
79
                $resource   = $m[1];
80
                $identifier = $m[2];
81
            } else {
82
                // get the current template's resource
83
                $resource   = $curTpl->getResourceName();
84
                $identifier = substr($file, 1, - 1);
85
            }
86
87
            try {
88
                $templateDirs = $compiler->getCore()->getTemplateDir();
89
                $curTemplateDir = str_replace($identifier, '', $curTpl->getResourceIdentifier());
90
                $curTemplateKey = array_search($curTemplateDir, $templateDirs);
91
                if ($curTemplateKey !== false && isset($templateDirs[$curTemplateKey+1])) {
92
                    $nextTemplateDir = $templateDirs[$curTemplateKey+1];
93
                    $nextTpl = $nextTemplateDir . $identifier;
94
                    foreach ($templateDirs as $key => $dir) {
95
                        if ($key <= $curTemplateKey) {
96
                            unset($templateDirs[$key]);
97
                        }
98
                    }
99
                    $curTpl = new File(
100
                        $identifier,
101
                        null,
102
                        null,
103
                        null,
104
                        $templateDirs
105
                    );
106
                }
107
                $parent = $compiler->getCore()->templateFactory($resource, $identifier, null, null, null, $curTpl);
108
            }
109
            catch (SecurityException $e) {
110
                throw new CompilationException($compiler, 'Inherits : Security restriction : ' . $e->getMessage());
111
            }
112
            catch (Exception $e) {
113
                throw new CompilationException($compiler, 'Inherits : ' . $e->getMessage());
114
            }
115
116
            if ($parent === null) {
117
                throw new CompilationException($compiler, 'Inherits : Resource "' . $resource . ':' . $identifier . '" not found.');
118
            } elseif ($parent === false) {
119
                throw new CompilationException($compiler, 'Inherits : Resource "' . $resource . '" does not support inherits.');
120
            }
121
122
            $curTpl    = $parent;
123
            $newParent = array(
124
                'source'     => $parent->getSource(),
125
                'resource'   => $resource,
126
                'identifier' => $parent->getResourceIdentifier(),
127
                'uid'        => $parent->getUid()
128
            );
129
            if (array_search($newParent, $inheritanceTree, true) !== false) {
130
                throw new CompilationException($compiler, 'Inherits : Recursive template inheritance detected');
131
            }
132
            $inheritanceTree[] = $newParent;
133
134
            if (preg_match('/^' . self::$l . 'inherits(?:\(?\s*|\s+)(?:file=)?\s*((["\']).+?\2|\S+?)\s*\)?\s*?' . self::$r . '/i', $parent->getSource(), $match)) {
135
                $curPath = dirname($identifier) . DIRECTORY_SEPARATOR;
136
                if (isset($match[2]) && $match[2] == '"') {
137
                    $file = '"' . str_replace('"', '\\"', substr($match[1], 1, - 1)) . '"';
138
                } elseif (isset($match[2]) && $match[2] == "'") {
139
                    $file = '"' . substr($match[1], 1, - 1) . '"';
140
                } else {
141
                    $file = '"' . $match[1] . '"';
142
                }
143
            } else {
144
                $file = false;
145
            }
146
        }
147
148
        while (true) {
149
            $parent                = array_pop($inheritanceTree);
150
            $child                 = end($inheritanceTree);
151
            self::$childSource     = $child['source'];
152
            self::$lastReplacement = count($inheritanceTree) === 1;
153
            if (!isset($newSource)) {
154
                $newSource = $parent['source'];
155
            }
156
            $newSource = preg_replace_callback(self::$regex, array(
157
                'Dwoo\Plugins\Functions\PluginInherits',
158
                'replaceBlock'
159
            ), $newSource);
160
161
            if (self::$lastReplacement) {
162
                break;
163
            }
164
        }
165
        $compiler->setTemplateSource($newSource);
166
        $compiler->recompile();
167
    }
168
169
    /**
170
     * @param array $matches
171
     *
172
     * @return mixed|string
173
     */
174
    protected static function replaceBlock(array $matches)
175
    {
176
        $matches[3] = self::removeTrailingNewline($matches[3]);
177
178
        if (preg_match_all(self::$regex, self::$childSource, $override) && in_array($matches[2], $override[2])) {
179
            $key      = array_search($matches[2], $override[2]);
180
            $override = self::removeTrailingNewline($override[3][$key]);
181
182
            $l = stripslashes(self::$l);
183
            $r = stripslashes(self::$r);
184
185
            if (self::$lastReplacement) {
186
                return preg_replace('/' . self::$l . '\$dwoo\.parent' . self::$r . '/is', $matches[3], $override);
187
            }
188
189
            return $l . 'block ' . $matches[1] . $matches[2] . $matches[1] . $r . preg_replace('/' . self::$l . '\$dwoo\.parent' . self::$r . '/is', $matches[3], $override) . $l . '/block' . $r;
190
        }
191
192
        if (preg_match(self::$regex, $matches[3])) {
193
            return preg_replace_callback(self::$regex, array(
194
                'Dwoo\Plugins\Functions\PluginInherits',
195
                'replaceBlock'
196
            ), $matches[3]);
197
        }
198
199
        if (self::$lastReplacement) {
200
            return $matches[3];
201
        }
202
203
        return $matches[0];
204
    }
205
}
206