Completed
Push — master ( 4ad6bd...3873e4 )
by Ingo
11:53
created

ThemeManifest::init()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 4
nop 2
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\View;
4
5
use Psr\SimpleCache\CacheInterface;
6
use SilverStripe\Core\Cache\CacheFactory;
7
use SilverStripe\Core\Manifest\ManifestFileFinder;
8
9
/**
10
 * A class which builds a manifest of all themes (which is really just a directory called "templates")
11
 */
12
class ThemeManifest implements ThemeList
13
{
14
15
    const TEMPLATES_DIR = 'templates';
16
17
    /**
18
     * Base path
19
     *
20
     * @var string
21
     */
22
    protected $base;
23
24
    /**
25
     * Path to application code
26
     *
27
     * @var string
28
     */
29
    protected $project;
30
31
    /**
32
     * Cache
33
     *
34
     * @var CacheInterface
35
     */
36
    protected $cache;
37
38
    /**
39
     * Cache key
40
     *
41
     * @var string
42
     */
43
    protected $cacheKey;
44
45
    /**
46
     * List of theme root directories
47
     *
48
     * @var string[]
49
     */
50
    protected $themes = null;
51
52
    /**
53
     * @var CacheFactory
54
     */
55
    protected $cacheFactory= null;
56
57
    /**
58
     * Constructs a new template manifest. The manifest is not actually built
59
     * or loaded from cache until needed.
60
     *
61
     * @param string $base The base path.
62
     * @param string $project Path to application code
63
     * @param CacheFactory $cacheFactory Cache factory to generate backend cache with
64
     */
65
    public function __construct($base, $project, CacheFactory $cacheFactory = null)
66
    {
67
        $this->base = $base;
68
        $this->project = $project;
69
        $this->cacheFactory = $cacheFactory;
70
    }
71
72
    /**
73
     * @param bool $includeTests Include tests in the manifest
74
     * @param bool $forceRegen Force the manifest to be regenerated.
75
     */
76
    public function init($includeTests = false, $forceRegen = false)
77
    {
78
        // build cache from factory
79
        if ($this->cacheFactory) {
80
            $this->cache = $this->cacheFactory->create(
81
                CacheInterface::class.'.thememanifest',
82
                [ 'namespace' => 'thememanifest' . ($includeTests ? '_tests' : '') ]
83
            );
84
        }
85
        $this->cacheKey = $this->getCacheKey($includeTests);
86
        if (!$forceRegen && $this->cache && ($data = $this->cache->get($this->cacheKey))) {
87
            $this->themes = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data of type * is incompatible with the declared type array<integer,string> of property $themes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
88
        } else {
89
            $this->regenerate($includeTests);
90
        }
91
    }
92
93
    /**
94
     * @return string
95
     */
96
    public function getBase()
97
    {
98
        return $this->base;
99
    }
100
101
    /**
102
     * Generate a unique cache key to avoid manifest cache collisions.
103
     * We compartmentalise based on the base path, the given project, and whether
104
     * or not we intend to include tests.
105
     *
106
     * @param bool $includeTests
107
     * @return string
108
     */
109
    public function getCacheKey($includeTests = false)
110
    {
111
        return sha1(sprintf(
112
            "manifest-%s-%s-%u",
113
            $this->base,
114
            $this->project,
115
            $includeTests
116
        ));
117
    }
118
119
    public function getThemes()
120
    {
121
        return $this->themes;
122
    }
123
124
    /**
125
     * Regenerates the manifest by scanning the base path.
126
     *
127
     * @param bool $includeTests
128
     */
129
    public function regenerate($includeTests = false)
130
    {
131
        $finder = new ManifestFileFinder();
132
        $finder->setOptions(array(
133
            'include_themes' => false,
134
            'ignore_dirs' => array('node_modules', THEMES_DIR),
135
            'ignore_tests'  => !$includeTests,
136
            'dir_callback'  => array($this, 'handleDirectory')
137
        ));
138
139
        $this->themes = [];
140
        $finder->find($this->base);
141
142
        if ($this->cache) {
143
            $this->cache->set($this->cacheKey, $this->themes);
144
        }
145
    }
146
147
    /**
148
     * Add a directory to the manifest
149
     *
150
     * @param string $basename
151
     * @param string $pathname
152
     * @param int $depth
153
     */
154
    public function handleDirectory($basename, $pathname, $depth)
155
    {
156
        if ($basename !== self::TEMPLATES_DIR) {
157
            return;
158
        }
159
160
        // We only want part of the full path, so split into directories
161
        $parts = explode('/', $pathname);
162
        // Take the end (the part relative to base), except the very last directory
163
        $themeParts = array_slice($parts, -$depth, $depth-1);
164
        // Then join again
165
        $path = '/'.implode('/', $themeParts);
166
167
        // If this is in the project, add to beginning of list. Else add to end.
168
        if ($themeParts && $themeParts[0] == $this->project) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $themeParts 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...
169
            array_unshift($this->themes, $path);
170
        } else {
171
            array_push($this->themes, $path);
172
        }
173
    }
174
}
175