HelperMethods   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Importance

Changes 21
Bugs 0 Features 0
Metric Value
wmc 47
eloc 118
c 21
b 0
f 0
dl 0
loc 246
rs 8.64

18 Methods

Rating   Name   Duplication   Size   Complexity  
A clearTable() 0 3 1
A dropTable() 0 5 1
A renameTable() 0 18 5
A makeTableObsolete() 0 15 4
A replaceTable() 0 16 6
A runSQLQueries() 0 6 3
B runPublishClasses() 0 29 7
A runUpdateQuery() 0 11 2
A tableExists() 0 5 1
A deleteObject() 0 13 3
A getSchema() 0 10 2
A getSchemaForDataObject() 0 7 2
A writeObject() 0 11 2
A getListOfIDs() 0 4 1
A getListAsIterableQuery() 0 16 3
A renameField() 0 3 1
A writePage() 0 3 1
A fieldExists() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like HelperMethods 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 HelperMethods, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Sunnysideup\MigrateData\Traits;
4
5
use Exception;
6
use SilverStripe\ORM\DataObject;
7
use SilverStripe\ORM\DB;
8
use SilverStripe\ORM\Queries\SQLSelect;
9
use SilverStripe\Versioned\Versioned;
0 ignored issues
show
Bug introduced by
The type SilverStripe\Versioned\Versioned was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Sunnysideup\Flush\FlushNow;
11
use Sunnysideup\Flush\FlushNowImplementor;
12
13
trait HelperMethods
14
{
15
    use FlushNow;
16
17
    public function deleteObject($obj)
18
    {
19
        if ($obj->exists()) {
20
            FlushNowImplementor::do_flush('DELETING ' . $obj->ClassName . '.' . $obj->ID, 'deleted');
21
            if ($obj->hasExtension(Versioned::class)) {
22
                $obj->DeleteFromStage(Versioned::LIVE);
23
                $obj->DeleteFromStage(Versioned::DRAFT);
24
            } else {
25
                $obj->delete();
26
            }
27
            @$obj->flushCache();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flushCache(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

27
            /** @scrutinizer ignore-unhandled */ @$obj->flushCache();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
28
        } else {
29
            FlushNowImplementor::do_flush('DOES NOT EXIST', 'added');
30
        }
31
    }
32
33
    /**
34
     * @param array  $queries list of queries
35
     * @param string $name    what is this list about?
36
     */
37
    protected function runSQLQueries($queries, $name = 'UPDATE QUERIES')
38
    {
39
        if ([] !== $queries) {
40
            $this->flushNow('<h3>Performing ' . $name . ' Queries</h3>');
41
            foreach ($queries as $sqlQuery) {
42
                $this->runUpdateQuery($sqlQuery);
43
            }
44
        }
45
    }
46
47
    /**
48
     * @param string $sqlQuery list of queries
49
     * @param int    $indents  what is this list about?
50
     */
51
    protected function runUpdateQuery(string $sqlQuery, ?int $indents = 1)
52
    {
53
        $this->flushNow(str_replace('"', '`', $sqlQuery), 'created');
54
        $prefix = str_repeat(' ... ', $indents);
0 ignored issues
show
Bug introduced by
It seems like $indents can also be of type null; however, parameter $times of str_repeat() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

54
        $prefix = str_repeat(' ... ', /** @scrutinizer ignore-type */ $indents);
Loading history...
55
56
        try {
57
            DB::query($sqlQuery);
58
            $this->flushNow($prefix . ' DONE ' . DB::affected_rows() . ' rows affected');
59
        } catch (Exception $exception) {
60
            $this->flushNow($prefix . "ERROR: Unable to run '{$sqlQuery}'", 'deleted');
61
            $this->flushNow('' . $exception->getMessage() . '', 'deleted');
62
        }
63
    }
64
65
    /**
66
     * @param array $publishClasses list of class names to write / publish
67
     */
68
    protected function runPublishClasses(array $publishClasses)
69
    {
70
        if ([] !== $publishClasses) {
71
            $this->flushNow('<h3>Publish classes</h3>');
72
            foreach ($publishClasses as $publishClass) {
73
                $this->flushNow('<h6>Publishing ' . $publishClass . '</h6>');
74
75
                try {
76
                    $count = 0;
77
                    $publishItems = $publishClass::get();
78
                    foreach ($publishItems as $publishItem) {
79
                        ++$count;
80
                        $this->flushNow(
81
                            'Publishing ' . $count . ' of ' . $publishItems->count() .
82
                                ' ' .
83
                                $publishClass .
84
                                ' item' . (1 === $publishItems->exists() ? '' : 's') . '.'
85
                        );
86
                        $publishItem->write();
87
                        if ($publishItem->hasMethod('publishRecursive')) {
88
                            $publishItem->publishRecursive();
89
                            $this->flushNow('... DONE - PUBLISHED');
90
                        } else {
91
                            $this->flushNow('... DONE - WRITE ONLY');
92
                        }
93
                    }
94
                } catch (Exception $e) {
95
                    $this->flushNow('Unable to publish ' . $publishClass . '', 'error');
96
                    $this->flushNow('' . $e->getMessage() . '', 'error');
97
                }
98
            }
99
        }
100
    }
101
102
    protected function makeTableObsolete(string $tableName, ?bool $doEvenIfAlreadyObsolete = false): bool
103
    {
104
        $schema = $this->getSchema();
105
        if ($this->tableExists($tableName)) {
106
            if (!$this->tableExists('_obsolete_' . $tableName) || $doEvenIfAlreadyObsolete) {
107
                $schema->dontRequireTable($tableName);
108
109
                return true;
110
            }
111
            $this->flushNow('Table ' . $tableName . ' is already obsolete');
112
        } else {
113
            $this->flushNow('Table ' . $tableName . ' does not exist.');
114
        }
115
116
        return false;
117
    }
118
119
    protected function tableExists(string $tableName): bool
120
    {
121
        $schema = $this->getSchema();
122
123
        return (bool) $schema->hasTable($tableName);
124
    }
125
126
    protected function clearTable(string $tableName)
127
    {
128
        DB::get_conn()->clearTable($tableName);
129
    }
130
131
    protected function replaceTable(string $a, string $b, ?bool $keepBackup = false)
132
    {
133
        if ($this->tableExists($a)) {
134
            if ($this->tableExists($b)) {
135
                $itemsInDB = DB::query('SELECT DISTINCT ID FROM  ' . stripslashes($b) . ';');
136
                if ($itemsInDB->numRecords() > 0 && $keepBackup) {
137
                    $this->makeTableObsolete($b, true);
138
                    $this->flushNow('Backing up ' . $b, 'deleted');
139
                }
140
                $this->dropTable($b);
141
            }
142
143
            if (!$this->tableExists($b)) {
144
                $this->renameTable($a, $b);
145
            } else {
146
                $this->flushNow('Could not delete ' . $b, 'deleted');
147
            }
148
        }
149
    }
150
151
    /**
152
     * delete the table both with and without slashes.
153
     */
154
    protected function dropTable(string $tableName)
155
    {
156
        $this->flushNow('Deleting ' . $tableName, 'deleted');
157
        DB::query('DROP TABLE IF EXISTS "' . $tableName . '";');
158
        DB::query('DROP TABLE IF EXISTS "' . stripslashes($tableName) . '";');
159
    }
160
161
    protected function renameTable(string $a, string $b)
162
    {
163
        $this->flushNow('Moving "' . $a . '" to "' . $b . '"', 'warning');
164
        if (!$this->tableExists($a)) {
165
            $this->flushNow(' -- Could not find "' . $a . '", consider using replaceTable', 'deleted');
166
167
            return;
168
        }
169
        if ($this->tableExists($b)) {
170
            $this->flushNow(' -- Destination table already exists "' . $b . '", consider using replaceTable', 'deleted');
171
172
            return;
173
        }
174
        if (false !== strpos($a, '\\') || false !== strpos($b, '\\')) {
175
            $this->flushNow('Special slashes case "' . $a . '" to "' . $b . '"', 'warning');
176
            DB::query('ALTER TABLE "' . stripslashes($a) . '" RENAME "' . stripslashes($b) . '"');
177
        } else {
178
            $this->getSchema()->renameTable($a, $b);
179
        }
180
    }
181
182
    protected function fieldExists(string $tableName, string $fieldName): bool
183
    {
184
        $key = $tableName;
185
        if (!isset($this->_cacheFieldExists[$key])) {
186
            $schema = $this->getSchema();
187
            $this->_cacheFieldExists[$key] = $schema->fieldList($tableName);
0 ignored issues
show
Bug Best Practice introduced by
The property _cacheFieldExists does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
188
        }
189
190
        return $this->_cacheFieldExists[$key][$fieldName] ?? false;
191
    }
192
193
    protected function renameField(string $table, string $oldFieldName, string $newFieldName)
194
    {
195
        $this->getSchema()->dontRequireField($table, $oldFieldName, $newFieldName);
196
    }
197
198
    protected function getSchema()
199
    {
200
        if (null === $this->_schema) {
201
            $this->_schema = DB::get_schema();
0 ignored issues
show
Bug Best Practice introduced by
The property _schema does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
202
            $this->_schema->schemaUpdate(function () {
203
                return true;
204
            });
205
        }
206
207
        return $this->_schema;
208
    }
209
210
    protected function getSchemaForDataObject()
211
    {
212
        if (null === $this->_schemaForDataObject) {
213
            $this->_schemaForDataObject = DataObject::getSchema();
0 ignored issues
show
Bug Best Practice introduced by
The property _schemaForDataObject does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
214
        }
215
216
        return $this->_schemaForDataObject;
217
    }
218
219
    protected function getListOfIDs(string $tableName, ?array $leftJoin = [], ?string $where = '')
0 ignored issues
show
Unused Code introduced by
The parameter $where is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

219
    protected function getListOfIDs(string $tableName, ?array $leftJoin = [], /** @scrutinizer ignore-unused */ ?string $where = '')

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

Loading history...
Unused Code introduced by
The parameter $leftJoin is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

219
    protected function getListOfIDs(string $tableName, /** @scrutinizer ignore-unused */ ?array $leftJoin = [], ?string $where = '')

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

Loading history...
220
    {
221
        return $this->getListAsIterableQuery($tableName, $leftJoin = [], $where = '')
222
            ->keyedColumn('ID');
0 ignored issues
show
Unused Code introduced by
The call to SilverStripe\ORM\Connect\Query::keyedColumn() has too many arguments starting with 'ID'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

222
            ->/** @scrutinizer ignore-call */ keyedColumn('ID');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
223
    }
224
225
    protected function getListAsIterableQuery(string $tableName, ?array $leftJoin = [], ?string $where = '')
226
    {
227
        $sqlSelect = new SQLSelect();
228
        $sqlSelect->setFrom($tableName);
229
230
        if ($leftJoin) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $leftJoin 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...
231
            $sqlSelect->addLeftJoin($leftJoin['table'], $leftJoin['onPredicate']);
232
        }
233
234
        if ($where) {
235
            $sqlSelect->addWhere($where);
236
        }
237
238
        $sqlSelect->setOrderBy($tableName . '.ID');
239
240
        return $sqlSelect->execute();
241
    }
242
243
    protected function writeObject($obj, ?array $row = [], ?bool $isPage = false)
0 ignored issues
show
Unused Code introduced by
The parameter $isPage is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

243
    protected function writeObject($obj, ?array $row = [], /** @scrutinizer ignore-unused */ ?bool $isPage = false)

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

Loading history...
Unused Code introduced by
The parameter $row is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

243
    protected function writeObject($obj, /** @scrutinizer ignore-unused */ ?array $row = [], ?bool $isPage = false)

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

Loading history...
244
    {
245
        DataObject::Config()->set('validation_enabled', false);
246
        if ($obj->hasMethod('writeToStage')) {
247
            $obj->writeToStage(Versioned::DRAFT);
248
            $obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
249
            $obj->publishRecursive();
250
        } else {
251
            $obj->write();
252
        }
253
        $obj->flushCache();
254
    }
255
256
    protected function writePage($obj, $row)
257
    {
258
        return $this->writeObject($obj, $row, $isPage = true);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->writeObject($obj, $row, $isPage = true) targeting Sunnysideup\MigrateData\...rMethods::writeObject() 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...
259
    }
260
}
261