Completed
Pull Request — master (#6648)
by Ingo
08:49
created

SSViewer::flush_cacheblock_cache()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\View;
4
5
use SilverStripe\Core\Config\Configurable;
6
use SilverStripe\Core\ClassInfo;
7
use Psr\SimpleCache\CacheInterface;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\Core\Flushable;
10
use SilverStripe\Core\Injector\Injector;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Dev\Deprecation;
13
use SilverStripe\ORM\FieldType\DBField;
14
use SilverStripe\ORM\FieldType\DBHTMLText;
15
use SilverStripe\Security\Permission;
16
use InvalidArgumentException;
17
18
/**
19
 * Parses a template file with an *.ss file extension.
20
 *
21
 * In addition to a full template in the templates/ folder, a template in
22
 * templates/Content or templates/Layout will be rendered into $Content and
23
 * $Layout, respectively.
24
 *
25
 * A single template can be parsed by multiple nested {@link SSViewer} instances
26
 * through $Layout/$Content placeholders, as well as <% include MyTemplateFile %> template commands.
27
 *
28
 * <b>Themes</b>
29
 *
30
 * See http://doc.silverstripe.org/themes and http://doc.silverstripe.org/themes:developing
31
 *
32
 * <b>Caching</b>
33
 *
34
 * Compiled templates are cached via {@link Cache}, usually on the filesystem.
35
 * If you put ?flush=1 on your URL, it will force the template to be recompiled.
36
 *
37
 * @see http://doc.silverstripe.org/themes
38
 * @see http://doc.silverstripe.org/themes:developing
39
 */
40
class SSViewer implements Flushable
41
{
42
    use Configurable;
43
44
    /**
45
     * Identifier for the default theme
46
     */
47
    const DEFAULT_THEME = '$default';
48
49
    /**
50
     * @config
51
     * @var boolean $source_file_comments
52
     */
53
    private static $source_file_comments = false;
54
55
    /**
56
     * @ignore
57
     */
58
    private static $template_cache_flushed = false;
59
60
    /**
61
     * @ignore
62
     */
63
    private static $cacheblock_cache_flushed = false;
64
65
    /**
66
     * Set whether HTML comments indicating the source .SS file used to render this page should be
67
     * included in the output.  This is enabled by default
68
     *
69
     * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
70
     * @param boolean $val
71
     */
72
    public static function set_source_file_comments($val)
73
    {
74
        Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
75
        SSViewer::config()->update('source_file_comments', $val);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
76
    }
77
78
    /**
79
     * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
80
     * @return boolean
81
     */
82
    public static function get_source_file_comments()
83
    {
84
        Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
85
        return SSViewer::config()->get('source_file_comments');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
86
    }
87
88
    /**
89
     * @var array $templates List of templates to select from
90
     */
91
    private $templates = null;
92
93
    /**
94
     * @var string $chosen Absolute path to chosen template file
95
     */
96
    private $chosen = null;
97
98
    /**
99
     * @var array Templates to use when looking up 'Layout' or 'Content'
100
     */
101
    private $subTemplates = null;
102
103
    /**
104
     * @var boolean
105
     */
106
    protected $rewriteHashlinks = true;
107
108
    /**
109
     * @config
110
     * @var string A list (highest priority first) of themes to use
111
     * Only used when {@link $theme_enabled} is set to TRUE.
112
     */
113
    private static $themes = [];
114
115
    /**
116
     * @deprecated 4.0..5.0
117
     * @config
118
     * @var string The used "theme", which usually consists of templates, images and stylesheets.
119
     * Only used when {@link $theme_enabled} is set to TRUE, and $themes is empty
120
     */
121
    private static $theme = null;
122
123
    /**
124
     * @config
125
     * @var boolean Use the theme. Set to FALSE in order to disable themes,
126
     * which can be useful for scenarios where theme overrides are temporarily undesired,
127
     * such as an administrative interface separate from the website theme.
128
     * It retains the theme settings to be re-enabled, for example when a website content
129
     * needs to be rendered from within this administrative interface.
130
     */
131
    private static $theme_enabled = true;
132
133
    /**
134
     * @var boolean
135
     */
136
    protected $includeRequirements = true;
137
138
    /**
139
     * @var TemplateParser
140
     */
141
    protected $parser;
142
143
    /*
144
	 * Default prepended cache key for partial caching
145
	 *
146
	 * @var string
147
	 * @config
148
	 */
149
    private static $global_key = '$CurrentReadingMode, $CurrentUser.ID';
150
151
    /**
152
     * Triggered early in the request when someone requests a flush.
153
     */
154
    public static function flush()
155
    {
156
        self::flush_template_cache(true);
157
        self::flush_cacheblock_cache(true);
158
    }
159
160
    /**
161
     * Create a template from a string instead of a .ss file
162
     *
163
     * @param string $content The template content
164
     * @param bool|void $cacheTemplate Whether or not to cache the template from string
165
     * @return SSViewer
166
     */
167
    public static function fromString($content, $cacheTemplate = null)
168
    {
169
        $viewer = new SSViewer_FromString($content);
170
        if ($cacheTemplate !== null) {
171
            $viewer->setCacheTemplate($cacheTemplate);
172
        }
173
        return $viewer;
174
    }
175
176
    /**
177
     * Assign the list of active themes to apply.
178
     * If default themes should be included add $default as the last entry.
179
     *
180
     * @param array $themes
181
     */
182
    public static function set_themes($themes = [])
183
    {
184
        SSViewer::config()
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
185
            ->remove('themes')
186
            ->update('themes', $themes);
187
    }
188
189
    public static function add_themes($themes = [])
190
    {
191
        SSViewer::config()->update('themes', $themes);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
192
    }
193
194
    public static function get_themes()
195
    {
196
        $default = [self::DEFAULT_THEME];
197
198
        if (!SSViewer::config()->get('theme_enabled')) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
199
            return $default;
200
        }
201
202
        // Explicit list is assigned
203
        if ($list = SSViewer::config()->get('themes')) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
204
            return $list;
205
        }
206
207
        // Support legacy behaviour
208
        if ($theme = SSViewer::config()->get('theme')) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
209
            return [$theme, self::DEFAULT_THEME];
210
        }
211
212
        return $default;
213
    }
214
215
    /**
216
     * @deprecated 4.0 Use the "SSViewer.theme" config setting instead
217
     * @param string $theme The "base theme" name (without underscores).
218
     */
219
    public static function set_theme($theme)
220
    {
221
        Deprecation::notice('4.0', 'Use the "SSViewer#set_themes" instead');
222
        self::set_themes([$theme, self::DEFAULT_THEME]);
223
    }
224
225
    /**
226
     * Traverses the given the given class context looking for candidate template names
227
     * which match each item in the class hierarchy. The resulting list of template candidates
228
     * may or may not exist, but you can invoke {@see SSViewer::chooseTemplate} on any list
229
     * to determine the best candidate based on the current themes.
230
     *
231
     * @param string|object $classOrObject Valid class name, or object
232
     * @param string $suffix
233
     * @param string $baseClass Class to halt ancestry search at
234
     * @return array
235
     */
236
    public static function get_templates_by_class($classOrObject, $suffix = '', $baseClass = null)
237
    {
238
        // Figure out the class name from the supplied context.
239
        if (!is_object($classOrObject) && !(
240
            is_string($classOrObject) && class_exists($classOrObject)
241
        )) {
242
            throw new InvalidArgumentException(
243
                'SSViewer::get_templates_by_class() expects a valid class name as its first parameter.'
244
            );
245
        }
246
        $templates = array();
247
        $classes = array_reverse(ClassInfo::ancestry($classOrObject));
248
        foreach ($classes as $class) {
249
            $template = $class . $suffix;
250
            $templates[] = $template;
251
            $templates[] = ['type' => 'Includes', $template];
252
253
            // If the class is "PageController" (PSR-2 compatibility) or "Page_Controller" (legacy), look for Page.ss
254
            if (preg_match('/^(?<name>.+[^\\\\])_?Controller$/iU', $class, $matches)) {
255
                $templates[] = $matches['name'] . $suffix;
256
            }
257
258
            if ($baseClass && $class == $baseClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $baseClass of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
259
                break;
260
            }
261
        }
262
        return $templates;
263
    }
264
265
    /**
266
     * @param string|array $templates If passed as a string with .ss extension, used as the "main" template.
267
     *  If passed as an array, it can be used for template inheritance (first found template "wins").
268
     *  Usually the array values are PHP class names, which directly correlate to template names.
269
     *  <code>
270
     *  array('MySpecificPage', 'MyPage', 'Page')
271
     *  </code>
272
     * @param TemplateParser $parser
273
     */
274
    public function __construct($templates, TemplateParser $parser = null)
275
    {
276
        if ($parser) {
277
            $this->setParser($parser);
278
        }
279
280
        $this->setTemplate($templates);
281
282
        if (!$this->chosen) {
283
            $message = 'None of the following templates could be found: ';
284
            $message .= print_r($templates, true);
285
286
            $themes = self::get_themes();
287
            if (!$themes) {
288
                $message .= ' (no theme in use)';
289
            } else {
290
                $message .= ' in themes "' . print_r($themes, true) . '"';
291
            }
292
293
            user_error($message, E_USER_WARNING);
294
        }
295
    }
296
297
    public function setTemplate($templates)
298
    {
299
        $this->templates = $templates;
300
        $this->chosen = $this->chooseTemplate($templates);
301
        $this->subTemplates = [];
302
    }
303
304
    /**
305
     * Find the template to use for a given list
306
     *
307
     * @param array|string $templates
308
     * @return string
309
     */
310
    public static function chooseTemplate($templates)
311
    {
312
        return ThemeResourceLoader::instance()->findTemplate($templates, self::get_themes());
313
    }
314
315
    /**
316
     * Set the template parser that will be used in template generation
317
     *
318
     * @param TemplateParser $parser
319
     */
320
    public function setParser(TemplateParser $parser)
321
    {
322
        $this->parser = $parser;
323
    }
324
325
    /**
326
     * Returns the parser that is set for template generation
327
     *
328
     * @return TemplateParser
329
     */
330
    public function getParser()
331
    {
332
        if (!$this->parser) {
333
            $this->setParser(Injector::inst()->get('SilverStripe\\View\\SSTemplateParser'));
334
        }
335
        return $this->parser;
336
    }
337
338
    /**
339
     * Returns true if at least one of the listed templates exists.
340
     *
341
     * @param array $templates
342
     *
343
     * @return boolean
344
     */
345
    public static function hasTemplate($templates)
346
    {
347
        return (bool)ThemeResourceLoader::instance()->findTemplate($templates, self::get_themes());
348
    }
349
350
    /**
351
     * Set a global rendering option.
352
     *
353
     * The following options are available:
354
     *  - rewriteHashlinks: If true (the default), <a href="#..."> will be rewritten to contain the
355
     *    current URL.  This lets it play nicely with our <base> tag.
356
     *  - If rewriteHashlinks = 'php' then, a piece of PHP script will be inserted before the hash
357
     *    links: "<?php echo $_SERVER['REQUEST_URI']; ?>".  This is useful if you're generating a
358
     *    page that will be saved to a .php file and may be accessed from different URLs.
359
     *
360
     * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
361
     * @param string $optionName
362
     * @param mixed $optionVal
363
     */
364
    public static function setOption($optionName, $optionVal)
365
    {
366
        if ($optionName == 'rewriteHashlinks') {
367
            Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead');
368
            SSViewer::config()->update('rewrite_hash_links', $optionVal);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
369
        } else {
370
            Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead');
371
            SSViewer::config()->update($optionName, $optionVal);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
372
        }
373
    }
374
375
    /**
376
     * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
377
     * @param string
378
     * @return mixed
379
     */
380
    public static function getOption($optionName)
381
    {
382
        if ($optionName == 'rewriteHashlinks') {
383
            Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead');
384
            return SSViewer::config()->get('rewrite_hash_links');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
385
        } else {
386
            Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead');
387
            return SSViewer::config()->get($optionName);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
388
        }
389
    }
390
391
    /**
392
     * @config
393
     * @var boolean
394
     */
395
    private static $rewrite_hash_links = true;
396
397
    protected static $topLevel = array();
398
399
    public static function topLevel()
400
    {
401
        if (SSViewer::$topLevel) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \SilverStripe\View\SSViewer::$topLevel of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
402
            return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
403
        }
404
        return null;
405
    }
406
407
    /**
408
     * Call this to disable rewriting of <a href="#xxx"> links.  This is useful in Ajax applications.
409
     * It returns the SSViewer objects, so that you can call new SSViewer("X")->dontRewriteHashlinks()->process();
410
     */
411
    public function dontRewriteHashlinks()
412
    {
413
        $this->rewriteHashlinks = false;
414
        SSViewer::config()->update('rewrite_hash_links', false);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
415
        return $this;
416
    }
417
418
    public function exists()
419
    {
420
        return $this->chosen;
421
    }
422
423
    /**
424
     * @param string $identifier A template name without '.ss' extension or path
425
     * @param string $type The template type, either "main", "Includes" or "Layout"
426
     *
427
     * @return string Full system path to a template file
428
     */
429
    public static function getTemplateFileByType($identifier, $type = null)
430
    {
431
        return ThemeResourceLoader::instance()->findTemplate(['type' => $type, $identifier], self::get_themes());
432
    }
433
434
    /**
435
     * Clears all parsed template files in the cache folder.
436
     *
437
     * Can only be called once per request (there may be multiple SSViewer instances).
438
     *
439
     * @param bool $force Set this to true to force a re-flush. If left to false, flushing
440
     * may only be performed once a request.
441
     */
442
    public static function flush_template_cache($force = false)
443
    {
444
        if (!self::$template_cache_flushed || $force) {
445
            $dir = dir(TEMP_FOLDER);
446
            while (false !== ($file = $dir->read())) {
447
                if (strstr($file, '.cache')) {
448
                    unlink(TEMP_FOLDER . '/' . $file);
449
                }
450
            }
451
            self::$template_cache_flushed = true;
452
        }
453
    }
454
455
    /**
456
     * Clears all partial cache blocks.
457
     *
458
     * Can only be called once per request (there may be multiple SSViewer instances).
459
     *
460
     * @param bool $force Set this to true to force a re-flush. If left to false, flushing
461
     * may only be performed once a request.
462
     */
463
    public static function flush_cacheblock_cache($force = false)
464
    {
465
        if (!self::$cacheblock_cache_flushed || $force) {
466
            $cache = Injector::inst()->get(CacheInterface::class . '.cacheblock');
467
            $cache->clear();
468
469
470
            self::$cacheblock_cache_flushed = true;
471
        }
472
    }
473
474
    /**
475
     * @var CacheInterface
476
     */
477
    protected $partialCacheStore = null;
478
479
    /**
480
     * Set the cache object to use when storing / retrieving partial cache blocks.
481
     *
482
     * @param CacheInterface $cache
483
     */
484
    public function setPartialCacheStore($cache)
485
    {
486
        $this->partialCacheStore = $cache;
487
    }
488
489
    /**
490
     * Get the cache object to use when storing / retrieving partial cache blocks.
491
     *
492
     * @return CacheInterface
493
     */
494
    public function getPartialCacheStore()
495
    {
496
        return $this->partialCacheStore ? $this->partialCacheStore : Injector::inst()->get(CacheInterface::class . '.cacheblock');
497
    }
498
499
    /**
500
     * Flag whether to include the requirements in this response.
501
     *
502
     * @param boolean
503
     */
504
    public function includeRequirements($incl = true)
505
    {
506
        $this->includeRequirements = $incl;
507
    }
508
509
    /**
510
     * An internal utility function to set up variables in preparation for including a compiled
511
     * template, then do the include
512
     *
513
     * Effectively this is the common code that both SSViewer#process and SSViewer_FromString#process call
514
     *
515
     * @param string $cacheFile The path to the file that contains the template compiled to PHP
516
     * @param ViewableData $item The item to use as the root scope for the template
517
     * @param array $overlay Any variables to layer on top of the scope
518
     * @param array $underlay Any variables to layer underneath the scope
519
     * @param ViewableData $inheritedScope The current scope of a parent template including a sub-template
520
     * @return string The result of executing the template
521
     */
522
    protected function includeGeneratedTemplate($cacheFile, $item, $overlay, $underlay, $inheritedScope = null)
0 ignored issues
show
Coding Style introduced by
includeGeneratedTemplate uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
523
    {
524
        if (isset($_GET['showtemplate']) && $_GET['showtemplate'] && Permission::check('ADMIN')) {
525
            $lines = file($cacheFile);
526
            echo "<h2>Template: $cacheFile</h2>";
527
            echo "<pre>";
528
            foreach ($lines as $num => $line) {
529
                echo str_pad($num+1, 5) . htmlentities($line, ENT_COMPAT, 'UTF-8');
530
            }
531
            echo "</pre>";
532
        }
533
534
        $cache = $this->getPartialCacheStore();
535
        $scope = new SSViewer_DataPresenter($item, $overlay, $underlay, $inheritedScope);
536
        $val = '';
537
538
        include($cacheFile);
539
540
        return $val;
541
    }
542
543
    /**
544
     * The process() method handles the "meat" of the template processing.
545
     *
546
     * It takes care of caching the output (via {@link Cache}), as well as
547
     * replacing the special "$Content" and "$Layout" placeholders with their
548
     * respective subtemplates.
549
     *
550
     * The method injects extra HTML in the header via {@link Requirements::includeInHTML()}.
551
     *
552
     * Note: You can call this method indirectly by {@link ViewableData->renderWith()}.
553
     *
554
     * @param ViewableData $item
555
     * @param array|null $arguments Arguments to an included template
556
     * @param ViewableData $inheritedScope The current scope of a parent template including a sub-template
557
     * @return DBHTMLText Parsed template output.
558
     */
559
    public function process($item, $arguments = null, $inheritedScope = null)
0 ignored issues
show
Coding Style introduced by
process uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
560
    {
561
        SSViewer::$topLevel[] = $item;
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
562
563
        $template = $this->chosen;
564
565
        $cacheFile = TEMP_FOLDER . "/.cache"
566
            . str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
567
        $lastEdited = filemtime($template);
568
569
        if (!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited) {
570
            $content = file_get_contents($template);
571
            $content = $this->parseTemplateContent($content, $template);
572
573
            $fh = fopen($cacheFile, 'w');
574
            fwrite($fh, $content);
575
            fclose($fh);
576
        }
577
578
        $underlay = array('I18NNamespace' => basename($template));
579
580
        // Makes the rendered sub-templates available on the parent item,
581
        // through $Content and $Layout placeholders.
582
        foreach (array('Content', 'Layout') as $subtemplate) {
583
            $sub = null;
584
            if (isset($this->subTemplates[$subtemplate])) {
585
                $sub = $this->subTemplates[$subtemplate];
586
            } elseif (!is_array($this->templates)) {
587
                $sub = ['type' => $subtemplate, $this->templates];
588
            } elseif (!array_key_exists('type', $this->templates) || !$this->templates['type']) {
589
                $sub = array_merge($this->templates, ['type' => $subtemplate]);
590
            }
591
592
            if ($sub) {
593
                $subtemplateViewer = clone $this;
594
                // Disable requirements - this will be handled by the parent template
595
                $subtemplateViewer->includeRequirements(false);
596
                // Select the right template
597
                $subtemplateViewer->setTemplate($sub);
598
599
                if ($subtemplateViewer->exists()) {
600
                    $underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
601
                }
602
            }
603
        }
604
605
        $output = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay, $inheritedScope);
0 ignored issues
show
Bug introduced by
It seems like $arguments defined by parameter $arguments on line 559 can also be of type null; however, SilverStripe\View\SSView...ludeGeneratedTemplate() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
606
607
        if ($this->includeRequirements) {
608
            $output = Requirements::includeInHTML($output);
609
        }
610
611
        array_pop(SSViewer::$topLevel);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
612
613
        // If we have our crazy base tag, then fix # links referencing the current page.
614
615
        $rewrite = SSViewer::config()->get('rewrite_hash_links');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
616
        if ($this->rewriteHashlinks && $rewrite) {
617
            if (strpos($output, '<base') !== false) {
618
                if ($rewrite === 'php') {
619
                    $thisURLRelativeToBase = "<?php echo \\SilverStripe\\Core\\Convert::raw2att(preg_replace(\"/^(\\\\/)+/\", \"/\", \$_SERVER['REQUEST_URI'])); ?>";
620
                } else {
621
                    $thisURLRelativeToBase = Convert::raw2att(preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI']));
622
                }
623
624
                $output = preg_replace('/(<a[^>]+href *= *)"#/i', '\\1"' . $thisURLRelativeToBase . '#', $output);
625
            }
626
        }
627
628
        return DBField::create_field('HTMLFragment', $output);
629
    }
630
631
    /**
632
     * Execute the given template, passing it the given data.
633
     * Used by the <% include %> template tag to process templates.
634
     *
635
     * @param string $template Template name
636
     * @param mixed $data Data context
637
     * @param array $arguments Additional arguments
638
     * @param Object $scope
639
     * @return string Evaluated result
640
     */
641
    public static function execute_template($template, $data, $arguments = null, $scope = null)
642
    {
643
        $v = new SSViewer($template);
644
        $v->includeRequirements(false);
645
646
        return $v->process($data, $arguments, $scope);
0 ignored issues
show
Bug introduced by
It seems like $scope defined by parameter $scope on line 641 can also be of type object; however, SilverStripe\View\SSViewer::process() does only seem to accept object<SilverStripe\View\ViewableData>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
647
    }
648
649
    /**
650
     * Execute the evaluated string, passing it the given data.
651
     * Used by partial caching to evaluate custom cache keys expressed using
652
     * template expressions
653
     *
654
     * @param string $content Input string
655
     * @param mixed $data Data context
656
     * @param array $arguments Additional arguments
657
     * @return string Evaluated result
658
     */
659
    public static function execute_string($content, $data, $arguments = null)
660
    {
661
        $v = SSViewer::fromString($content);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
662
        $v->includeRequirements(false);
663
664
        return $v->process($data, $arguments);
665
    }
666
667
    public function parseTemplateContent($content, $template = "")
668
    {
669
        return $this->getParser()->compileString(
670
            $content,
671
            $template,
672
            Director::isDev() && SSViewer::config()->get('source_file_comments')
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
673
        );
674
    }
675
676
    /**
677
     * Returns the filenames of the template that will be rendered.  It is a map that may contain
678
     * 'Content' & 'Layout', and will have to contain 'main'
679
     */
680
    public function templates()
681
    {
682
        return array_merge(['main' => $this->chosen], $this->subTemplates);
683
    }
684
685
    /**
686
     * @param string $type "Layout" or "main"
687
     * @param string $file Full system path to the template file
688
     */
689
    public function setTemplateFile($type, $file)
690
    {
691
        if (!$type || $type == 'main') {
692
            $this->chosen = $file;
693
        } else {
694
            $this->subTemplates[$type] = $file;
695
        }
696
    }
697
698
    /**
699
     * Return an appropriate base tag for the given template.
700
     * It will be closed on an XHTML document, and unclosed on an HTML document.
701
     *
702
     * @param string $contentGeneratedSoFar The content of the template generated so far; it should contain
703
     * the DOCTYPE declaration.
704
     * @return string
705
     */
706
    public static function get_base_tag($contentGeneratedSoFar)
707
    {
708
        $base = Director::absoluteBaseURL();
709
710
        // Is the document XHTML?
711
        if (preg_match('/<!DOCTYPE[^>]+xhtml/i', $contentGeneratedSoFar)) {
712
            return "<base href=\"$base\" />";
713
        } else {
714
            return "<base href=\"$base\"><!--[if lte IE 6]></base><![endif]-->";
715
        }
716
    }
717
}
718