Passed
Pull Request — 4 (#10016)
by Ingo
10:13
created

NullDatabase   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 448
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 50
c 1
b 0
f 0
dl 0
loc 448
rs 5.04
wmc 57

46 Methods

Rating   Name   Duplication   Size   Complexity  
A transactionEnd() 0 2 1
A formattedDatetimeClause() 0 2 1
A escapeIdentifier() 0 3 1
A selectDatabase() 0 2 1
A supportsExtensions() 0 3 1
A random() 0 2 1
A nullCheckClause() 0 6 2
A datetimeDifferenceClause() 0 2 1
A preparedQuery() 0 3 1
A supportsCollations() 0 2 1
A canLock() 0 3 1
A getLock() 0 3 1
A concatOperator() 0 2 1
A getGeneratedID() 0 3 1
A getVersion() 0 2 1
A transactionStart() 0 2 1
A __construct() 0 6 1
A transactionSavepoint() 0 2 1
A clearAllData() 0 3 1
A affectedRows() 0 2 1
A now() 0 2 1
A supportsLocks() 0 3 1
A comparisonClause() 0 8 1
A supportsTransactionMode() 0 2 1
B withTransaction() 0 26 8
A transactionDepth() 0 7 2
A transactionRollback() 0 2 1
A getSelectedDatabase() 0 2 1
A supportsTimezoneOverride() 0 2 1
A getDatabaseServer() 0 2 1
A dropSelectedDatabase() 0 2 1
A supportsSavepoints() 0 2 1
A searchEngine() 0 11 1
A releaseLock() 0 3 1
A connect() 0 15 3
A escapeColumnKeys() 0 3 1
A isActive() 0 3 1
A clearTable() 0 3 1
A quoteString() 0 3 1
A escapeString() 0 3 1
A supportsTransactions() 0 2 1
A query() 0 3 1
A datetimeIntervalClause() 0 2 1
A databaseList() 0 3 1
A manipulate() 0 3 1
A databaseExists() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like NullDatabase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use NullDatabase, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\ORM\Connect;
4
5
use BadMethodCallException;
6
use Exception;
7
8
/**
9
 * Utility class required due to bad coupling in framework.
10
 * Not every framework execution should require a working database connection.
11
 * For example, when generating class and config manifests for deployment bundles,
12
 * or when generating code in a silverstripe/graphql schema build.
13
 *
14
 * This class creates the required no-ops to
15
 *
16
 * @internal
17
 */
18
class NullDatabase extends Database
19
{
20
21
    public function __construct()
22
    {
23
        $this->connector = new NullConnector();
24
        $this->schemaManager = new MySQLSchemaManager();
25
        $this->schemaManager->setDatabase($this);
26
        $this->queryBuilder = new MySQLQueryBuilder();
27
    }
28
29
    public function query($sql, $errorLevel = E_USER_ERROR)
30
    {
31
        throw new \Exception('cannot query');
32
    }
33
34
    public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR)
35
    {
36
        throw new \Exception('cannot query');
37
    }
38
39
    /**
40
     * Get the autogenerated ID from the previous INSERT query.
41
     *
42
     * @param string $table The name of the table to get the generated ID for
43
     * @return integer the most recently generated ID for the specified table
44
     */
45
    public function getGeneratedID($table)
46
    {
47
        return $this->connector->getGeneratedID($table);
48
    }
49
50
    /**
51
     * Determines if we are connected to a server AND have a valid database
52
     * selected.
53
     *
54
     * @return boolean Flag indicating that a valid database is connected
55
     */
56
    public function isActive()
57
    {
58
        return true;
59
    }
60
61
    /**
62
     * Returns an escaped string. This string won't be quoted, so would be suitable
63
     * for appending to other quoted strings.
64
     *
65
     * @param mixed $value Value to be prepared for database query
66
     * @return string Prepared string
67
     */
68
    public function escapeString($value)
69
    {
70
        return $value;
71
    }
72
73
    /**
74
     * Wrap a string into DB-specific quotes.
75
     *
76
     * @param mixed $value Value to be prepared for database query
77
     * @return string Prepared string
78
     */
79
    public function quoteString($value)
80
    {
81
        return $value;
82
    }
83
84
    public function escapeIdentifier($value, $separator = '.')
85
    {
86
        return $value;
87
    }
88
89
    protected function escapeColumnKeys($fieldValues)
90
    {
91
        return $fieldValues;
92
    }
93
94
    public function manipulate($manipulation)
95
    {
96
        throw new \Exception('cannot query');
97
    }
98
99
    /**
100
     * Clear all data out of the database
101
     */
102
    public function clearAllData()
103
    {
104
        throw new \Exception('cannot query');
105
    }
106
107
    /**
108
     * Clear all data in a given table
109
     *
110
     * @param string $table Name of table
111
     */
112
    public function clearTable($table)
113
    {
114
        throw new \Exception('cannot query');
115
    }
116
117
    /**
118
     * Generates a WHERE clause for null comparison check
119
     *
120
     * @param string $field Quoted field name
121
     * @param bool $isNull Whether to check for NULL or NOT NULL
122
     * @return string Non-parameterised null comparison clause
123
     */
124
    public function nullCheckClause($field, $isNull)
125
    {
126
        $clause = $isNull
127
            ? "%s IS NULL"
128
            : "%s IS NOT NULL";
129
        return sprintf($clause, $field);
130
    }
131
132
    public function comparisonClause(
133
        $field,
134
        $value,
135
        $exact = false,
136
        $negate = false,
137
        $caseSensitive = null,
138
        $parameterised = false
139
    ) {
140
        // no-op
141
    }
142
143
    public function formattedDatetimeClause($date, $format)
144
    {
145
        // no-op
146
    }
147
148
    public function datetimeIntervalClause($date, $interval)
149
    {
150
        // no-op
151
    }
152
153
    public function datetimeDifferenceClause($date1, $date2)
154
    {
155
        // no-op
156
    }
157
158
    public function concatOperator()
159
    {
160
        // no-op
161
    }
162
163
    public function supportsCollations()
164
    {
165
        // no-op
166
    }
167
168
    public function supportsTimezoneOverride()
169
    {
170
        // no-op
171
    }
172
173
    public function getVersion()
174
    {
175
        // no-op
176
    }
177
178
    public function getDatabaseServer()
179
    {
180
        // no-op
181
    }
182
183
    public function affectedRows()
184
    {
185
        // no-op
186
    }
187
188
    public function searchEngine(
189
        $classesToSearch,
190
        $keywords,
191
        $start,
192
        $pageLength,
193
        $sortBy = "Relevance DESC",
194
        $extraFilter = "",
195
        $booleanSearch = false,
196
        $alternativeFileFilter = "",
197
        $invertedMatch = false
198
    ) {
199
        // no-op
200
    }
201
202
    public function supportsTransactions()
203
    {
204
        // no-op
205
    }
206
207
    public function supportsSavepoints()
208
    {
209
        // no-op
210
    }
211
212
213
    public function supportsTransactionMode(string $mode): bool
214
    {
215
        // no-op
216
    }
217
218
    /**
219
     * Invoke $callback within a transaction
220
     *
221
     * @param callable $callback Callback to run
222
     * @param callable $errorCallback Optional callback to run after rolling back transaction.
223
     * @param bool|string $transactionMode Optional transaction mode to use
224
     * @param bool $errorIfTransactionsUnsupported If true, this method will fail if transactions are unsupported.
225
     * Otherwise, the $callback will potentially be invoked outside of a transaction.
226
     * @throws Exception
227
     */
228
    public function withTransaction(
229
        $callback,
230
        $errorCallback = null,
231
        $transactionMode = false,
232
        $errorIfTransactionsUnsupported = false
233
    ) {
234
        $supported = $this->supportsTransactions();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $supported is correct as $this->supportsTransactions() targeting SilverStripe\ORM\Connect...:supportsTransactions() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
235
        if (!$supported && $errorIfTransactionsUnsupported) {
0 ignored issues
show
introduced by
$supported is defined implicitly as null, thus it is always evaluated to false.
Loading history...
236
            throw new BadMethodCallException("Transactions not supported by this database.");
237
        }
238
        if ($supported) {
0 ignored issues
show
introduced by
$supported is defined implicitly as null, thus it is always evaluated to false.
Loading history...
239
            $this->transactionStart($transactionMode);
240
        }
241
        try {
242
            call_user_func($callback);
243
        } catch (Exception $ex) {
244
            if ($supported) {
0 ignored issues
show
introduced by
$supported is defined implicitly as null, thus it is always evaluated to false.
Loading history...
245
                $this->transactionRollback();
246
            }
247
            if ($errorCallback) {
248
                call_user_func($errorCallback);
249
            }
250
            throw $ex;
251
        }
252
        if ($supported) {
0 ignored issues
show
introduced by
$supported is defined implicitly as null, thus it is always evaluated to false.
Loading history...
253
            $this->transactionEnd();
254
        }
255
    }
256
257
    /*
258
     * Determines if the current database connection supports a given list of extensions
259
     *
260
     * @param array $extensions List of extensions to check for support of. The key of this array
261
     * will be an extension name, and the value the configuration for that extension. This
262
     * could be one of partitions, tablespaces, or clustering
263
     * @return boolean Flag indicating support for all of the above
264
     * @todo Write test cases
265
     */
266
    public function supportsExtensions($extensions)
267
    {
268
        return false;
269
    }
270
271
    /**
272
     * Start a prepared transaction
273
     * See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on
274
     * transaction isolation options
275
     *
276
     * @param string|boolean $transactionMode Transaction mode, or false to ignore
277
     * @param string|boolean $sessionCharacteristics Session characteristics, or false to ignore
278
     */
279
    public function transactionStart($transactionMode = false, $sessionCharacteristics = false)
280
    {
281
        // no-op
282
    }
283
284
    /**
285
     * Create a savepoint that you can jump back to if you encounter problems
286
     *
287
     * @param string $savepoint Name of savepoint
288
     */
289
    public function transactionSavepoint($savepoint)
290
    {
291
        // no-op
292
    }
293
294
    /**
295
     * Rollback or revert to a savepoint if your queries encounter problems
296
     * If you encounter a problem at any point during a transaction, you may
297
     * need to rollback that particular query, or return to a savepoint
298
     *
299
     * @param string|boolean $savepoint Name of savepoint, or leave empty to rollback
300
     * to last savepoint
301
     * @return bool|null Boolean is returned if success state is known, or null if
302
     * unknown. Note: For error checking purposes null should not be treated as error.
303
     */
304
    public function transactionRollback($savepoint = false)
305
    {
306
        // no-op
307
    }
308
309
    /**
310
     * Commit everything inside this transaction so far
311
     *
312
     * @param bool $chain
313
     * @return bool|null Boolean is returned if success state is known, or null if
314
     * unknown. Note: For error checking purposes null should not be treated as error.
315
     */
316
    public function transactionEnd($chain = false)
317
    {
318
        // no-op
319
    }
320
321
    /**
322
     * Return depth of current transaction
323
     *
324
     * @return int Nesting level, or 0 if not in a transaction
325
     */
326
    public function transactionDepth()
327
    {
328
        // Placeholder error for transactional DBs that don't expose depth
329
        if ($this->supportsTransactions()) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->supportsTransactions() targeting SilverStripe\ORM\Connect...:supportsTransactions() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
330
            user_error(get_class($this) . " does not support transactionDepth", E_USER_WARNING);
331
        }
332
        return 0;
333
    }
334
335
    /**
336
     * Determines if the used database supports application-level locks,
337
     * which is different from table- or row-level locking.
338
     * See {@link getLock()} for details.
339
     *
340
     * @return bool Flag indicating that locking is available
341
     */
342
    public function supportsLocks()
343
    {
344
        return false;
345
    }
346
347
    /**
348
     * Returns if the lock is available.
349
     * See {@link supportsLocks()} to check if locking is generally supported.
350
     *
351
     * @param string $name Name of the lock
352
     * @return bool
353
     */
354
    public function canLock($name)
355
    {
356
        return false;
357
    }
358
359
    /**
360
     * Sets an application-level lock so that no two processes can run at the same time,
361
     * also called a "cooperative advisory lock".
362
     *
363
     * Return FALSE if acquiring the lock fails; otherwise return TRUE, if lock was acquired successfully.
364
     * Lock is automatically released if connection to the database is broken (either normally or abnormally),
365
     * making it less prone to deadlocks than session- or file-based locks.
366
     * Should be accompanied by a {@link releaseLock()} call after the logic requiring the lock has completed.
367
     * Can be called multiple times, in which case locks "stack" (PostgreSQL, SQL Server),
368
     * or auto-releases the previous lock (MySQL).
369
     *
370
     * Note that this might trigger the database to wait for the lock to be released, delaying further execution.
371
     *
372
     * @param string $name Name of lock
373
     * @param integer $timeout Timeout in seconds
374
     * @return bool
375
     */
376
    public function getLock($name, $timeout = 5)
377
    {
378
        return false;
379
    }
380
381
    /**
382
     * Remove an application-level lock file to allow another process to run
383
     * (if the execution aborts (e.g. due to an error) all locks are automatically released).
384
     *
385
     * @param string $name Name of the lock
386
     * @return bool Flag indicating whether the lock was successfully released
387
     */
388
    public function releaseLock($name)
389
    {
390
        return false;
391
    }
392
393
    /**
394
     * Instruct the database to generate a live connection
395
     *
396
     * @param array $parameters An map of parameters, which should include:
397
     *  - server: The server, eg, localhost
398
     *  - username: The username to log on with
399
     *  - password: The password to log on with
400
     *  - database: The database to connect to
401
     *  - charset: The character set to use. Defaults to utf8
402
     *  - timezone: (optional) The timezone offset. For example: +12:00, "Pacific/Auckland", or "SYSTEM"
403
     *  - driver: (optional) Driver name
404
     */
405
    public function connect($parameters)
406
    {
407
        // Ensure that driver is available (required by PDO)
408
        if (empty($parameters['driver'])) {
409
            $parameters['driver'] = $this->getDatabaseServer();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $parameters['driver'] is correct as $this->getDatabaseServer() targeting SilverStripe\ORM\Connect...se::getDatabaseServer() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
410
        }
411
412
        // Notify connector of parameters
413
        $this->connector->connect($parameters);
414
415
        // SS_Database subclass maintains responsibility for selecting database
416
        // once connected in order to correctly handle schema queries about
417
        // existence of database, error handling at the correct level, etc
418
        if (!empty($parameters['database'])) {
419
            $this->selectDatabase($parameters['database'], false, false);
420
        }
421
    }
422
423
    /**
424
     * Determine if the database with the specified name exists
425
     *
426
     * @param string $name Name of the database to check for
427
     * @return bool Flag indicating whether this database exists
428
     */
429
    public function databaseExists($name)
430
    {
431
        return $this->schemaManager->databaseExists($name);
432
    }
433
434
    /**
435
     * Retrieves the list of all databases the user has access to
436
     *
437
     * @return array List of database names
438
     */
439
    public function databaseList()
440
    {
441
        return $this->schemaManager->databaseList();
442
    }
443
444
    public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR)
445
    {
446
        // no-op
447
    }
448
449
    public function dropSelectedDatabase()
450
    {
451
        // no-op
452
    }
453
454
    public function getSelectedDatabase()
455
    {
456
        // no-op
457
    }
458
459
    public function now()
460
    {
461
        // no-op
462
    }
463
464
    public function random()
465
    {
466
        // no-op
467
    }
468
}
469