Completed
Push — master ( bf2930...494091 )
by David
07:08 queued 03:26
created

File::getResourceIdentifier()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 5
nop 0
dl 0
loc 17
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright (c) 2013-2017
4
 *
5
 * @category  Library
6
 * @package   Dwoo\Template
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 Modified BSD License
12
 * @version   1.3.2
13
 * @date      2017-01-03
14
 * @link      http://dwoo.org/
15
 */
16
17
namespace Dwoo\Template;
18
19
use Dwoo\Exception as DwooException;
20
use Dwoo\Core as Core;
21
use Dwoo\ITemplate as ITemplate;
22
use Dwoo\Security\Exception as SecurityException;
23
use Dwoo\Template\File as TemplateFile;
24
25
/**
26
 * Represents a Dwoo template contained in a file.
27
 * This software is provided 'as-is', without any express or implied warranty.
28
 * In no event will the authors be held liable for any damages arising from the use of this software.
29
 */
30
class File extends Str
31
{
32
    /**
33
     * Template filename.
34
     *
35
     * @var string
36
     */
37
    protected $file;
38
39
    /**
40
     * Include path(s) to look into to find this template.
41
     *
42
     * @var array
43
     */
44
    protected $includePath = array();
45
46
    /**
47
     * Resolved path cache when looking for a file in multiple include paths.
48
     * this is reset when the include path is changed
49
     *
50
     * @var string
51
     */
52
    protected $resolvedPath = null;
53
54
    /**
55
     * Creates a template from a file.
56
     *
57
     * @param string $file        the path to the template file, make sure it exists
58
     * @param int    $cacheTime   duration of the cache validity for this template,
59
     *                            if null it defaults to the Dwoo instance that will
60
     *                            render this template
61
     * @param string $cacheId     the unique cache identifier of this page or anything else that
62
     *                            makes this template's content unique, if null it defaults
63
     *                            to the current url
64
     * @param string $compileId   the unique compiled identifier, which is used to distinguish this
65
     *                            template from others, if null it defaults to the filename+bits of the path
66
     * @param mixed  $includePath a string for a single path to look into for the given file, or an array of paths
67
     */
68
    public function __construct($file, $cacheTime = null, $cacheId = null, $compileId = null, $includePath = array())
69
    {
70
        parent::__construct($file, $cacheTime, $cacheId, $compileId);
71
        $this->template = null;
72
        $this->file     = $file;
73
        $this->name     = basename($file);
74
        $this->setIncludePath($includePath);
75
    }
76
77
    /**
78
     * Sets the include path(s) to where the given template filename must be looked up.
79
     *
80
     * @param mixed $paths the path to look into, can be string for a single path or an array of paths
81
     */
82
    public function setIncludePath($paths)
83
    {
84
        if (is_array($paths) === false) {
85
            $paths = array($paths);
86
        }
87
88
        $this->includePath  = $paths;
89
        $this->resolvedPath = null;
90
    }
91
92
    /**
93
     * Return the current include path(s).
94
     *
95
     * @return array
96
     */
97
    public function getIncludePath()
98
    {
99
        return $this->includePath;
100
    }
101
102
    /**
103
     * Checks if compiled file is valid (exists and it's the modification is greater or
104
     * equal to the modification time of the template file).
105
     *
106
     * @param string file
107
     *
108
     * @return bool True cache file existance and it's modification time
109
     */
110
    protected function isValidCompiledFile($file)
111
    {
112
        return parent::isValidCompiledFile($file) && (int)$this->getUid() <= filemtime($file);
113
    }
114
115
    /**
116
     * Returns the template source of this template.
117
     *
118
     * @return string
119
     */
120
    public function getSource()
121
    {
122
        return file_get_contents($this->getResourceIdentifier());
123
    }
124
125
    /**
126
     * Returns the resource name for this template class.
127
     *
128
     * @return string
129
     */
130
    public function getResourceName()
131
    {
132
        return 'file';
133
    }
134
135
    /**
136
     * Returns this template's source filename.
137
     *
138
     * @return string
139
     * @throws DwooException
140
     */
141
    public function getResourceIdentifier()
142
    {
143
        if ($this->resolvedPath !== null) {
144
            return $this->resolvedPath;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->resolvedPath; (string) is incompatible with the return type of the parent method Dwoo\Template\Str::getResourceIdentifier of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
145
        } elseif (array_filter($this->getIncludePath()) == array()) {
146
            return $this->file;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->file; (string) is incompatible with the return type of the parent method Dwoo\Template\Str::getResourceIdentifier of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
147
        } else {
148
            foreach ($this->getIncludePath() as $path) {
149
                $path = rtrim($path, DIRECTORY_SEPARATOR);
150
                if (file_exists($path . DIRECTORY_SEPARATOR . $this->file) === true) {
151
                    return $this->resolvedPath = $path . DIRECTORY_SEPARATOR . $this->file;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->resolvedPa...EPARATOR . $this->file; (string) is incompatible with the return type of the parent method Dwoo\Template\Str::getResourceIdentifier of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
152
                }
153
            }
154
155
            throw new DwooException('Template "' . $this->file . '" could not be found in any of your include path(s)');
156
        }
157
    }
158
159
    /**
160
     * Returns an unique value identifying the current version of this template,
161
     * in this case it's the unix timestamp of the last modification.
162
     *
163
     * @return string
164
     */
165
    public function getUid()
166
    {
167
        return (string)filemtime($this->getResourceIdentifier());
168
    }
169
170
    /**
171
     * Returns a new template object from the given include name, null if no include is
172
     * possible (resource not found), or false if include is not permitted by this resource type.
173
     *
174
     * @param Core      $core           the dwoo instance requiring it
175
     * @param mixed     $resourceId     the filename (relative to this template's dir) of the template to
176
     *                                  include
177
     * @param int       $cacheTime      duration of the cache validity for this template, if null it defaults
178
     *                                  to the Dwoo instance that will render this template if null it
179
     *                                  defaults to the Dwoo instance that will render this template if null
180
     *                                  it defaults to the Dwoo instance that will render this template
181
     * @param string    $cacheId        the unique cache identifier of this page or anything else that makes
182
     *                                  this template's content unique, if null it defaults to the current
183
     *                                  url makes this template's content unique, if null it defaults to the
184
     *                                  current url makes this template's content unique, if null it defaults
185
     *                                  to the current url
186
     * @param string    $compileId      the unique compiled identifier, which is used to distinguish this
187
     *                                  template from others, if null it defaults to the filename+bits of the
188
     *                                  path template from others, if null it defaults to the filename+bits
189
     *                                  of the path template from others, if null it defaults to the
190
     *                                  filename+bits of the path
191
     * @param ITemplate $parentTemplate the template that is requesting a new template object (through an
192
     *                                  include, extends or any other plugin) an include, extends or any
193
     *                                  other plugin) an include, extends or any other plugin)
194
     *
195
     * @return TemplateFile|null
196
     * @throws DwooException
197
     * @throws SecurityException
198
     */
199
    public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, ITemplate $parentTemplate = null)
200
    {
201
        if (DIRECTORY_SEPARATOR === '\\') {
202
            $resourceId = str_replace(
203
                array("\t", "\n", "\r", "\f", "\v"), array(
204
                '\\t',
205
                '\\n',
206
                '\\r',
207
                '\\f',
208
                '\\v'
209
                ), $resourceId
210
            );
211
        }
212
        $resourceId = strtr($resourceId, '\\', '/');
213
214
        $includePath = null;
215
216
        if (file_exists($resourceId) === false) {
217
            if ($parentTemplate === null) {
218
                $parentTemplate = $core->getTemplate();
219
            }
220
            if ($parentTemplate instanceof self) {
221
                if ($includePath = $parentTemplate->getIncludePath()) {
222
                    if (strstr($resourceId, '../')) {
223
                        throw new DwooException('When using an include path you can not reference a template into a parent directory (using ../)');
224
                    }
225
                } else {
226
                    $resourceId = dirname($parentTemplate->getResourceIdentifier()) . DIRECTORY_SEPARATOR . $resourceId;
227
                    if (file_exists($resourceId) === false) {
228
                        return null;
229
                    }
230
                }
231
            } else {
232
                return null;
233
            }
234
        }
235
236
        if ($policy = $core->getSecurityPolicy()) {
237
            while (true) {
238
                if (preg_match('{^([a-z]+?)://}i', $resourceId)) {
239
                    throw new SecurityException('The security policy prevents you to read files from external sources : <em>' . $resourceId . '</em>.');
240
                }
241
242
                if ($includePath) {
243
                    break;
244
                }
245
246
                $resourceId = realpath($resourceId);
247
                $dirs       = $policy->getAllowedDirectories();
248
                foreach ($dirs as $dir => $dummy) {
249
                    if (strpos($resourceId, $dir) === 0) {
250
                        break 2;
251
                    }
252
                }
253
                throw new SecurityException('The security policy prevents you to read <em>' . $resourceId . '</em>');
254
            }
255
        }
256
257
        $class = 'Dwoo\Template\File';
258
        if ($parentTemplate) {
259
            $class = get_class($parentTemplate);
260
        }
261
262
        return new $class($resourceId, $cacheTime, $cacheId, $compileId, $includePath);
263
    }
264
265
    /**
266
     * Returns the full compiled file name and assigns a default value to it if
267
     * required.
268
     *
269
     * @param Core $core the dwoo instance that requests the file name
270
     *
271
     * @return string the full path to the compiled file
272
     */
273
    protected function getCompiledFilename(Core $core)
274
    {
275
        // no compile id was provided, set default
276
        if ($this->compileId === null) {
277
            $this->compileId = str_replace('../', '__', strtr($this->getResourceIdentifier(), '\\:', '/-'));
278
        }
279
280
        return $this->compileId . '.d' . Core::RELEASE_TAG . '.php';
281
    }
282
283
    /**
284
     * Returns some php code that will check if this template has been modified or not.
285
     * if the function returns null, the template will be instanciated and then the Uid checked
286
     *
287
     * @return string
288
     */
289
    public function getIsModifiedCode()
290
    {
291
        return '"' . $this->getUid() . '" == filemtime(' . var_export($this->getResourceIdentifier(), true) . ')';
292
    }
293
}
294