File::getUid()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 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 LGPLv3
12
 * @version   1.4.0
13
 * @date      2017-03-16
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\ICompiler;
22
use Dwoo\ITemplate as ITemplate;
23
use Dwoo\Security\Exception as SecurityException;
24
use Dwoo\Template\File as TemplateFile;
25
26
/**
27
 * Represents a Dwoo template contained in a file.
28
 * This software is provided 'as-is', without any express or implied warranty.
29
 * In no event will the authors be held liable for any damages arising from the use of this software.
30
 */
31
class File extends Str
32
{
33
    /**
34
     * Template filename.
35
     *
36
     * @var string
37
     */
38
    protected $file;
39
40
    /**
41
     * Include path(s) to look into to find this template.
42
     *
43
     * @var array
44
     */
45
    protected $includePath = array();
46
47
    /**
48
     * Resolved path cache when looking for a file in multiple include paths.
49
     * this is reset when the include path is changed
50
     *
51
     * @var string
52
     */
53
    protected $resolvedPath = null;
54
55
    /**
56
     * Creates a template from a file.
57
     *
58
     * @param string $file        the path to the template file, make sure it exists
59
     * @param int    $cacheTime   duration of the cache validity for this template,
60
     *                            if null it defaults to the Dwoo instance that will
61
     *                            render this template
62
     * @param string $cacheId     the unique cache identifier of this page or anything else that
63
     *                            makes this template's content unique, if null it defaults
64
     *                            to the current url
65
     * @param string $compileId   the unique compiled identifier, which is used to distinguish this
66
     *                            template from others, if null it defaults to the filename+bits of the path
67
     * @param mixed  $includePath a string for a single path to look into for the given file, or an array of paths
68
     */
69
    public function __construct($file, $cacheTime = null, $cacheId = null, $compileId = null, $includePath = array())
70
    {
71
        parent::__construct($file, $cacheTime, $cacheId, $compileId);
72
        $this->template = null;
73
        $this->file     = $file;
74
        $this->name     = basename($file);
75
        $this->setIncludePath($includePath);
76
        $this->compileId = $this->getResourceIdentifier();
77
    }
78
79
    /**
80
     * Sets the include path(s) to where the given template filename must be looked up.
81
     *
82
     * @param mixed $paths the path to look into, can be string for a single path or an array of paths
83
     */
84
    public function setIncludePath($paths)
85
    {
86
        if ($paths == null) {
87
          $paths = array();
88
        } elseif (is_array($paths) === false) {
89
            $paths = array($paths);
90
        }
91
92
        $this->includePath  = $paths;
93
        $this->resolvedPath = null;
94
    }
95
96
    /**
97
     * Return the current include path(s).
98
     *
99
     * @return array
100
     */
101
    public function getIncludePath()
102
    {
103
        return $this->includePath;
104
    }
105
106
    /**
107
     * Checks if compiled file is valid (exists and it's the modification is greater or
108
     * equal to the modification time of the template file).
109
     *
110
     * @param string file
111
     *
112
     * @return bool True cache file existance and it's modification time
113
     */
114
    protected function isValidCompiledFile($file)
115
    {
116
        return parent::isValidCompiledFile($file) && (int)$this->getUid() <= filemtime($file);
117
    }
118
119
    /**
120
     * Returns the template source of this template.
121
     *
122
     * @return string
123
     */
124
    public function getSource()
125
    {
126
        return file_get_contents($this->getResourceIdentifier());
127
    }
128
129
    /**
130
     * Returns the resource name for this template class.
131
     *
132
     * @return string
133
     */
134
    public function getResourceName()
135
    {
136
        return 'file';
137
    }
138
139
    /**
140
     * Returns this template's source filename.
141
     *
142
     * @return string
143
     * @throws DwooException
144
     */
145
    public function getResourceIdentifier()
146
    {
147
        if ($this->resolvedPath !== null) {
148
            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...
149
        } elseif (array_filter($this->getIncludePath()) == array()) {
150
            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...
151
        } else {
152
            foreach ($this->getIncludePath() as $path) {
153
                $path = rtrim($path, DIRECTORY_SEPARATOR);
154
                if (file_exists($path . DIRECTORY_SEPARATOR . $this->file) === true) {
155
                    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...
156
                }
157
            }
158
159
            throw new DwooException('Template "' . $this->file . '" could not be found in any of your include path(s)');
160
        }
161
    }
162
163
    /**
164
     * Returns an unique value identifying the current version of this template,
165
     * in this case it's the unix timestamp of the last modification.
166
     *
167
     * @return string
168
     */
169
    public function getUid()
170
    {
171
        return (string)filemtime($this->getResourceIdentifier());
172
    }
173
174
    /**
175
     * Returns a new template object from the given include name, null if no include is
176
     * possible (resource not found), or false if include is not permitted by this resource type.
177
     *
178
     * @param Core      $core           the dwoo instance requiring it
179
     * @param mixed     $resourceId     the filename (relative to this template's dir) of the template to
180
     *                                  include
181
     * @param int       $cacheTime      duration of the cache validity for this template, if null it defaults
182
     *                                  to the Dwoo instance that will render this template if null it
183
     *                                  defaults to the Dwoo instance that will render this template if null
184
     *                                  it defaults to the Dwoo instance that will render this template
185
     * @param string    $cacheId        the unique cache identifier of this page or anything else that makes
186
     *                                  this template's content unique, if null it defaults to the current
187
     *                                  url makes this template's content unique, if null it defaults to the
188
     *                                  current url makes this template's content unique, if null it defaults
189
     *                                  to the current url
190
     * @param string    $compileId      the unique compiled identifier, which is used to distinguish this
191
     *                                  template from others, if null it defaults to the filename+bits of the
192
     *                                  path template from others, if null it defaults to the filename+bits
193
     *                                  of the path template from others, if null it defaults to the
194
     *                                  filename+bits of the path
195
     * @param ITemplate $parentTemplate the template that is requesting a new template object (through an
196
     *                                  include, extends or any other plugin) an include, extends or any
197
     *                                  other plugin) an include, extends or any other plugin)
198
     *
199
     * @return TemplateFile|null
200
     * @throws DwooException
201
     * @throws SecurityException
202
     */
203
    public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
204
                                           $compileId = null, ITemplate $parentTemplate = null)
205
    {
206
        if (DIRECTORY_SEPARATOR === '\\') {
207
            $resourceId = str_replace(array("\t", "\n", "\r", "\f", "\v"), array(
208
                '\\t',
209
                '\\n',
210
                '\\r',
211
                '\\f',
212
                '\\v'
213
            ), $resourceId);
214
        }
215
        $resourceId = strtr($resourceId, '\\', '/');
216
217
        $includePath = null;
218
219
        if (file_exists($resourceId) === false) {
220
            if ($parentTemplate === null) {
221
                $parentTemplate = $core->getTemplate();
222
            }
223
            if ($parentTemplate instanceof self) {
224
                if ($includePath = $parentTemplate->getIncludePath()) {
225
                    if (strstr($resourceId, '../')) {
226
                        throw new DwooException('When using an include path you can not reference a template into a parent directory (using ../)');
227
                    }
228
                } else {
229
                    $resourceId = dirname($parentTemplate->getResourceIdentifier()) . DIRECTORY_SEPARATOR . $resourceId;
230
                    if (file_exists($resourceId) === false) {
231
                        return null;
232
                    }
233
                }
234
            } else {
235
                return null;
236
            }
237
        }
238
239
        if ($policy = $core->getSecurityPolicy()) {
240
            while (true) {
241
                if (preg_match('{^([a-z]+?)://}i', $resourceId)) {
242
                    throw new SecurityException('The security policy prevents you to read files from external sources : <em>' . $resourceId . '</em>.');
243
                }
244
245
                if ($includePath) {
246
                    break;
247
                }
248
249
                $resourceId = realpath($resourceId);
250
                $dirs       = $policy->getAllowedDirectories();
251
                foreach ($dirs as $dir => $dummy) {
252
                    if (strpos($resourceId, $dir) === 0) {
253
                        break 2;
254
                    }
255
                }
256
                throw new SecurityException('The security policy prevents you to read <em>' . $resourceId . '</em>');
257
            }
258
        }
259
260
        $class = 'Dwoo\Template\File';
261
        if ($parentTemplate) {
262
            $class = get_class($parentTemplate);
263
        }
264
265
        return new $class($resourceId, $cacheTime, $cacheId, $compileId, $includePath);
266
    }
267
268
    /**
269
     * Returns some php code that will check if this template has been modified or not.
270
     * if the function returns null, the template will be instanciated and then the Uid checked
271
     *
272
     * @return string
273
     */
274
    public function getIsModifiedCode()
275
    {
276
        return '"' . $this->getUid() . '" == filemtime(' . var_export($this->getResourceIdentifier(), true) . ')';
277
    }
278
}
279