Completed
Push — master ( 1efa22...cfa6a3 )
by Sam
22s
created

ModuleManifest   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 212
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 4

9 Methods

Rating   Name   Duplication   Size   Complexity  
A addModule() 0 20 3
A moduleExists() 0 5 1
C __construct() 0 22 7
A activateConfig() 0 6 2
B regenerate() 0 28 2
A addSourceConfigFile() 0 4 1
A addYAMLConfigFile() 0 6 2
B getModule() 0 18 5
A getModules() 0 4 1
1
<?php
2
3
namespace SilverStripe\Core\Manifest;
4
5
use LogicException;
6
use Psr\SimpleCache\CacheInterface;
7
use SilverStripe\Core\Cache\CacheFactory;
8
9
/**
10
 * A utility class which builds a manifest of configuration items
11
 */
12
class ModuleManifest
13
{
14
    /**
15
     * The base path used when building the manifest
16
     *
17
     * @var string
18
     */
19
    protected $base;
20
21
    /**
22
     * A string to prepend to all cache keys to ensure all keys are unique to just this $base
23
     *
24
     * @var string
25
     */
26
    protected $cacheKey;
27
28
    /**
29
     * Whether `test` directories should be searched when searching for configuration
30
     *
31
     * @var bool
32
     */
33
    protected $includeTests;
34
35
    /**
36
     * @var CacheInterface
37
     */
38
    protected $cache;
39
40
    /**
41
     * List of all modules.
42
     *
43
     * @var Module[]
44
     */
45
    protected $modules = array();
46
47
    /**
48
     * Adds a path as a module
49
     *
50
     * @param string $path
51
     */
52
    public function addModule($path)
53
    {
54
        $module = new Module($path, $this->base);
55
        $name = $module->getName();
56
57
        // Save if not already added
58
        if (empty($this->modules[$name])) {
59
            $this->modules[$name] = $module;
60
            return;
61
        }
62
63
        // Validate duplicate module
64
        $path = $module->getPath();
65
        $otherPath = $this->modules[$name]->getPath();
66
        if ($otherPath !== $path) {
67
            throw new LogicException(
68
                "Module {$name} is in two places - {$path} and {$otherPath}"
69
            );
70
        }
71
    }
72
73
    /**
74
     * Returns true if the passed module exists
75
     *
76
     * @param string $name Either full composer name or short name
77
     * @return bool
78
     */
79
    public function moduleExists($name)
80
    {
81
        $module = $this->getModule($name);
82
        return !empty($module);
83
    }
84
85
    /**
86
     * Constructs and initialises a new configuration object, either loading
87
     * from the cache or re-scanning for classes.
88
     *
89
     * @param string $base The project base path.
90
     * @param bool $includeTests
91
     * @param bool $forceRegen Force the manifest to be regenerated.
92
     * @param CacheFactory $cacheFactory Cache factory to use
93
     */
94
    public function __construct($base, $includeTests = false, $forceRegen = false, CacheFactory $cacheFactory = null)
95
    {
96
        $this->base = $base;
97
        $this->cacheKey = sha1($base).'_modules';
98
        $this->includeTests = $includeTests;
99
100
        // build cache from factory
101
        if ($cacheFactory) {
102
            $this->cache = $cacheFactory->create(
103
                CacheInterface::class.'.modulemanifest',
104
                [ 'namespace' => 'modulemanifest' . ($includeTests ? '_tests' : '') ]
105
            );
106
        }
107
108
        // Unless we're forcing regen, try loading from cache
109
        if (!$forceRegen && $this->cache) {
110
            $this->modules = $this->cache->get($this->cacheKey) ?: [];
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->cache->get($this->cacheKey) ?: array() of type * is incompatible with the declared type array<integer,object<Sil...\Core\Manifest\Module>> of property $modules.

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...
111
        }
112
        if (empty($this->modules)) {
113
            $this->regenerate($includeTests);
114
        }
115
    }
116
117
    /**
118
     * Includes all of the php _config.php files found by this manifest.
119
     */
120
    public function activateConfig()
121
    {
122
        foreach ($this->getModules() as $module) {
123
            $module->activate();
124
        }
125
    }
126
127
    /**
128
     * Completely regenerates the manifest file. Scans through finding all php _config.php and yaml _config/*.ya?ml
129
     * files,parses the yaml files into fragments, sorts them and figures out what values need to be checked to pick
130
     * the correct variant.
131
     *
132
     * Does _not_ build the actual variant
133
     *
134
     * @param bool $includeTests
135
     */
136
    public function regenerate($includeTests = false)
137
    {
138
        $this->modules = [];
139
140
        $finder = new ManifestFileFinder();
141
        $finder->setOptions(array(
142
            'min_depth' => 0,
143
            'name_regex'    => '/(^|[\/\\\\])_config.php$/',
144
            'ignore_tests'  => !$includeTests,
145
            'file_callback' => array($this, 'addSourceConfigFile'),
146
            // Cannot be max_depth: 1 due to "/framework/admin/_config.php"
147
            'max_depth'     => 2
148
        ));
149
        $finder->find($this->base);
150
151
        $finder = new ManifestFileFinder();
152
        $finder->setOptions(array(
153
            'name_regex'    => '/\.ya?ml$/',
154
            'ignore_tests'  => !$includeTests,
155
            'file_callback' => array($this, 'addYAMLConfigFile'),
156
            'max_depth'     => 2
157
        ));
158
        $finder->find($this->base);
159
160
        if ($this->cache) {
161
            $this->cache->set($this->cacheKey, $this->modules);
162
        }
163
    }
164
165
    /**
166
     * Record finding of _config.php file
167
     *
168
     * @param string $basename
169
     * @param string $pathname
170
     */
171
    public function addSourceConfigFile($basename, $pathname)
172
    {
173
        $this->addModule(dirname($pathname));
174
    }
175
176
    /**
177
     * Handle lookup of _config/*.yml file
178
     *
179
     * @param string $basename
180
     * @param string $pathname
181
     */
182
    public function addYAMLConfigFile($basename, $pathname)
183
    {
184
        if (preg_match('{/([^/]+)/_config/}', $pathname, $match)) {
185
            $this->addModule(dirname(dirname($pathname)));
186
        }
187
    }
188
189
    /**
190
     * Get module by name
191
     *
192
     * @param string $name
193
     * @return Module
194
     */
195
    public function getModule($name)
196
    {
197
        // Optimised find
198
        if (isset($this->modules[$name])) {
199
            return $this->modules[$name];
200
        }
201
202
        // Fall back to lookup by shortname
203
        if (!strstr($name, '/')) {
204
            foreach ($this->modules as $module) {
205
                if (strcasecmp($module->getShortName(), $name) === 0) {
206
                    return $module;
207
                }
208
            }
209
        }
210
211
        return null;
212
    }
213
214
    /**
215
     * Get modules found
216
     *
217
     * @return Module[]
218
     */
219
    public function getModules()
220
    {
221
        return $this->modules;
222
    }
223
}
224