Completed
Push — master ( deca00...5388ff )
by Sam
24s
created

ModuleManifest::regenerate()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 19
nc 2
nop 2
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Manifest;
4
5
use LogicException;
6
7
/**
8
 * A utility class which builds a manifest of configuration items
9
 */
10
class ModuleManifest
11
{
12
    /**
13
     * The base path used when building the manifest
14
     *
15
     * @var string
16
     */
17
    protected $base;
18
19
    /**
20
     * A string to prepend to all cache keys to ensure all keys are unique to just this $base
21
     *
22
     * @var string
23
     */
24
    protected $cacheKey;
25
26
    /**
27
     * Whether `test` directories should be searched when searching for configuration
28
     *
29
     * @var bool
30
     */
31
    protected $includeTests;
32
33
    /**
34
     * @var ManifestCache
35
     */
36
    protected $cache;
37
38
    /**
39
     * List of all modules.
40
     *
41
     * @var Module[]
42
     */
43
    protected $modules = array();
44
45
    /**
46
     * Adds a path as a module
47
     *
48
     * @param string $path
49
     */
50
    public function addModule($path)
51
    {
52
        $module = new Module($path, $this->base);
53
        $name = $module->getName();
54
55
        // Save if not already added
56
        if (empty($this->modules[$name])) {
57
            $this->modules[$name] = $module;
58
            return;
59
        }
60
61
        // Validate duplicate module
62
        $path = $module->getPath();
63
        $otherPath = $this->modules[$name]->getPath();
64
        if ($otherPath !== $path) {
65
            throw new LogicException(
66
                "Module {$name} is in two places - {$path} and {$otherPath}"
67
            );
68
        }
69
    }
70
71
    /**
72
     * Returns true if the passed module exists
73
     *
74
     * @param string $name Either full composer name or short name
75
     * @return bool
76
     */
77
    public function moduleExists($name)
78
    {
79
        $module = $this->getModule($name);
80
        return !empty($module);
81
    }
82
83
    /**
84
     * Constructs and initialises a new configuration object, either loading
85
     * from the cache or re-scanning for classes.
86
     *
87
     * @param string $base The project base path.
88
     * @param bool $includeTests
89
     * @param bool $forceRegen Force the manifest to be regenerated.
90
     */
91
    public function __construct($base, $includeTests = false, $forceRegen = false)
92
    {
93
        $this->base = $base;
94
        $this->cacheKey = sha1($base).'_modules';
95
        $this->includeTests = $includeTests;
96
97
        $this->cache = $this->getCache($includeTests);
98
99
        // Unless we're forcing regen, try loading from cache
100
        if (!$forceRegen) {
101
            $this->modules = $this->cache->load($this->cacheKey) ?: [];
102
        }
103
        if (empty($this->modules)) {
104
            $this->regenerate($includeTests);
105
        }
106
    }
107
108
    /**
109
     * Provides a hook for mock unit tests despite no DI
110
     *
111
     * @param bool $includeTests
112
     * @return ManifestCache
113
     */
114
    protected function getCache($includeTests = false)
115
    {
116
        // Cache
117
        $cacheClass = getenv('SS_MANIFESTCACHE') ?: ManifestCache_File::class;
118
        return new $cacheClass('classmanifest'.($includeTests ? '_tests' : ''));
119
    }
120
121
    /**
122
     * Includes all of the php _config.php files found by this manifest.
123
     */
124
    public function activateConfig()
125
    {
126
        foreach ($this->getModules() as $module) {
127
            $module->activate();
128
        }
129
    }
130
131
    /**
132
     * Completely regenerates the manifest file. Scans through finding all php _config.php and yaml _config/*.ya?ml
133
     * files,parses the yaml files into fragments, sorts them and figures out what values need to be checked to pick
134
     * the correct variant.
135
     *
136
     * Does _not_ build the actual variant
137
     *
138
     * @param bool $includeTests
139
     * @param bool $cache Cache the result.
140
     */
141
    public function regenerate($includeTests = false, $cache = true)
142
    {
143
        $this->modules = [];
144
145
        $finder = new ManifestFileFinder();
146
        $finder->setOptions(array(
147
            'min_depth' => 0,
148
            'name_regex'    => '/(^|[\/\\\\])_config.php$/',
149
            'ignore_tests'  => !$includeTests,
150
            'file_callback' => array($this, 'addSourceConfigFile'),
151
            // Cannot be max_depth: 1 due to "/framework/admin/_config.php"
152
            'max_depth'     => 2
153
        ));
154
        $finder->find($this->base);
155
156
        $finder = new ManifestFileFinder();
157
        $finder->setOptions(array(
158
            'name_regex'    => '/\.ya?ml$/',
159
            'ignore_tests'  => !$includeTests,
160
            'file_callback' => array($this, 'addYAMLConfigFile'),
161
            'max_depth'     => 2
162
        ));
163
        $finder->find($this->base);
164
165
        if ($cache) {
166
            $this->cache->save($this->modules, $this->cacheKey);
167
        }
168
    }
169
170
    /**
171
     * Record finding of _config.php file
172
     *
173
     * @param string $basename
174
     * @param string $pathname
175
     * @param int $depth
176
     */
177
    public function addSourceConfigFile($basename, $pathname, $depth)
0 ignored issues
show
Unused Code introduced by
The parameter $depth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
178
    {
179
        $this->addModule(dirname($pathname));
180
    }
181
182
    /**
183
     * Handle lookup of _config/*.yml file
184
     *
185
     * @param string $basename
186
     * @param string $pathname
187
     * @param int $depth
188
     */
189
    public function addYAMLConfigFile($basename, $pathname, $depth)
0 ignored issues
show
Unused Code introduced by
The parameter $depth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
190
    {
191
        if (preg_match('{/([^/]+)/_config/}', $pathname, $match)) {
192
            $this->addModule(dirname(dirname($pathname)));
193
        }
194
    }
195
196
    /**
197
     * Get module by name
198
     *
199
     * @param string $name
200
     * @return Module
201
     */
202
    public function getModule($name)
203
    {
204
        // Optimised find
205
        if (isset($this->modules[$name])) {
206
            return $this->modules[$name];
207
        }
208
209
        // Fall back to lookup by shortname
210
        if (!strstr($name, '/')) {
211
            foreach ($this->modules as $module) {
212
                if (strcasecmp($module->getShortName(), $name) === 0) {
213
                    return $module;
214
                }
215
            }
216
        }
217
218
        return null;
219
    }
220
221
    /**
222
     * Get modules found
223
     *
224
     * @return Module[]
225
     */
226
    public function getModules()
227
    {
228
        return $this->modules;
229
    }
230
}
231