Passed
Push — 4 ( cb36aa...e08bf1 )
by Loz
09:04 queued 11s
created

getConfigureDatabasePaths()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 18
rs 9.8333
1
<?php
2
3
namespace SilverStripe\Dev\Install;
4
5
use InvalidArgumentException;
6
use Psr\SimpleCache\CacheInterface;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Dev\Deprecation;
9
use SilverStripe\Core\Flushable;
10
11
/**
12
 * This class keeps track of the available database adapters
13
 * and provides a meaning of registering community built
14
 * adapters in to the installer process.
15
 *
16
 * @author Tom Rix
17
 */
18
class DatabaseAdapterRegistry implements Flushable
19
{
20
21
    /**
22
     * Default database connector registration fields
23
     *
24
     * @var array
25
     */
26
    private static $default_fields = array(
27
        'server' => array(
28
            'title' => 'Database server',
29
            'envVar' => 'SS_DATABASE_SERVER',
30
            'default' => 'localhost'
31
        ),
32
        'username' => array(
33
            'title' => 'Database username',
34
            'envVar' => 'SS_DATABASE_USERNAME',
35
            'default' => 'root'
36
        ),
37
        'password' => array(
38
            'title' => 'Database password',
39
            'envVar' => 'SS_DATABASE_PASSWORD',
40
            'default' => 'password'
41
        ),
42
        'database' => array(
43
            'title' => 'Database name',
44
            'default' => 'SS_mysite',
45
            'attributes' => array(
46
                "onchange" => "this.value = this.value.replace(/[\/\\:*?&quot;<>|. \t]+/g,'');"
47
            )
48
        ),
49
    );
50
51
    /**
52
     * Internal array of registered database adapters
53
     *
54
     * @var array
55
     */
56
    private static $adapters = array();
57
58
    /**
59
     * Add new adapter to the registry
60
     *
61
     * @param array $config Associative array of configuration details. This must include:
62
     *  - title
63
     *  - class
64
     *  - helperClass
65
     *  - supported
66
     * This SHOULD include:
67
     *  - fields
68
     *  - helperPath (if helperClass can't be autoloaded via psr-4/-0)
69
     *  - missingExtensionText
70
     *  - module OR missingModuleText
71
     */
72
    public static function register($config)
73
    {
74
        // Validate config
75
        $missing = array_diff(['title', 'class', 'helperClass', 'supported'], array_keys($config));
76
        if ($missing) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $missing 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...
77
            throw new InvalidArgumentException(
78
                "Missing database helper config keys: '" . implode("', '", $missing) . "'"
79
            );
80
        }
81
82
        // Guess missing module text if not given
83
        if (empty($config['missingModuleText'])) {
84
            if (empty($config['module'])) {
85
                $moduleText = 'Module for database connector ' . $config['title'] . 'is missing.';
86
            } else {
87
                $moduleText = "The SilverStripe module '" . $config['module'] . "' is missing.";
88
            }
89
            $config['missingModuleText'] = $moduleText
90
                . ' Please install it via composer or from http://addons.silverstripe.org/.';
91
        }
92
93
        // Set missing text
94
        if (empty($config['missingExtensionText'])) {
95
            $config['missingExtensionText'] = 'The PHP extension is missing, please enable or install it.';
96
        }
97
98
        // set default fields if none are defined already
99
        if (!isset($config['fields'])) {
100
            $config['fields'] = self::$default_fields;
101
        }
102
103
        self::$adapters[$config['class']] = $config;
104
    }
105
106
    /**
107
     * Unregisters a database connector by classname
108
     *
109
     * @param string $class
110
     */
111
    public static function unregister($class)
112
    {
113
        unset(self::$adapters[$class]);
114
    }
115
116
    /**
117
     * Detects all _register_database.php files and invokes them.
118
     * Searches through vendor/*\/* folders only,
119
     * does not support "legacy" folder location in webroot
120
     */
121
    public static function autodiscover()
122
    {
123
        // Search through all composer packages in vendor
124
        foreach (glob(BASE_PATH . '/vendor/*', GLOB_ONLYDIR) as $vendor) {
125
            foreach (glob($vendor . '/*', GLOB_ONLYDIR) as $directory) {
126
                if (file_exists($directory . '/_register_database.php')) {
127
                    include_once($directory . '/_register_database.php');
128
                }
129
            }
130
        }
131
    }
132
133
    /**
134
     * Detects all _configure_database.php files and invokes them
135
     * Called by ConfigureFromEnv.php.
136
     * Searches through vendor/ folder only,
137
     * does not support "legacy" folder location in webroot
138
     *
139
     * @param array $config Config to update. If not provided fall back to global $databaseConfig.
140
     * In 5.0.0 this will be mandatory and the global will be removed.
141
     */
142
    public static function autoconfigure(&$config = null)
143
    {
144
        if (!isset($config)) {
145
            Deprecation::notice('5.0', 'Configuration via global is deprecated');
146
            global $databaseConfig;
147
        } else {
148
            $databaseConfig = $config;
149
        }
150
        foreach (static::getConfigureDatabasePaths() as $configureDatabasePath) {
151
            include_once $configureDatabasePath;
152
        }
153
        // Update modified variable
154
        $config = $databaseConfig;
155
    }
156
157
    /**
158
     * Including _configure_database.php is a legacy method of configuring a database
159
     * It's still used by https://github.com/silverstripe/silverstripe-sqlite3
160
     */
161
    protected static function getConfigureDatabasePaths(): array
162
    {
163
        // autoconfigure() will get called before flush() on ?flush, so manually flush just to ensure no weirdness
164
        if (isset($_GET['flush'])) {
165
            static::flush();
166
        }
167
        $cache = static::getCache();
168
        $key = __FUNCTION__;
169
        if ($cache->has($key)) {
170
            return (array) $cache->get($key);
171
        } else {
172
            try {
173
                $paths = glob(BASE_PATH . '/vendor/*/*/_configure_database.php');
174
            } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The type SilverStripe\Dev\Install\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
175
                $paths = [];
176
            }
177
            $cache->set($key, $paths);
178
            return $paths;
179
        }
180
    }
181
182
    public static function getCache(): CacheInterface
183
    {
184
        return Injector::inst()->get(CacheInterface::class . '.DatabaseAdapterRegistry');
185
    }
186
187
    public static function flush()
188
    {
189
        static::getCache()->clear();
190
    }
191
192
    /**
193
     * Return all registered adapters
194
     *
195
     * @return array
196
     */
197
    public static function get_adapters()
198
    {
199
        return self::$adapters;
200
    }
201
202
    /**
203
     * Returns registry data for a class
204
     *
205
     * @param string $class
206
     * @return array List of adapter properties
207
     */
208
    public static function get_adapter($class)
209
    {
210
        if (isset(self::$adapters[$class])) {
211
            return self::$adapters[$class];
212
        }
213
        return null;
214
    }
215
216
    /**
217
     * Retrieves default field configuration
218
     *
219
     * @return array
220
     */
221
    public static function get_default_fields()
222
    {
223
        return self::$default_fields;
224
    }
225
226
    /**
227
     * Build configuration helper for a given class
228
     *
229
     * @param string $databaseClass Name of class
230
     * @return DatabaseConfigurationHelper|null Instance of helper, or null if cannot be loaded
231
     */
232
    public static function getDatabaseConfigurationHelper($databaseClass)
233
    {
234
        $adapters = static::get_adapters();
235
        if (empty($adapters[$databaseClass]) || empty($adapters[$databaseClass]['helperClass'])) {
236
            return null;
237
        }
238
239
        // Load if path given
240
        if (isset($adapters[$databaseClass]['helperPath'])) {
241
            include_once $adapters[$databaseClass]['helperPath'];
242
        }
243
244
        // Construct
245
        $class = $adapters[$databaseClass]['helperClass'];
246
        return (class_exists($class)) ? new $class() : null;
247
    }
248
}
249