DataIntegrityTest::obsoletefields()   F
last analyzed

Complexity

Conditions 59
Paths 1808

Size

Total Lines 208

Duplication

Lines 5
Ratio 2.4 %

Importance

Changes 0
Metric Value
dl 5
loc 208
rs 0
c 0
b 0
f 0
cc 59
nc 1808
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
4
class DataIntegrityTest extends BuildTask
5
{
6
7
8
    /**
9
     * standard SS variable
10
     * @var String
11
     */
12
    protected $title = "Check Database Integrity";
13
14
    /**
15
     * standard SS variable
16
     * @var String
17
     */
18
    protected $description = "Go through all fields in the database and work out what fields are superfluous.";
19
20
    private static $warning = "are you sure - this step is irreversible! - MAKE SURE TO MAKE A BACKUP OF YOUR DATABASE BEFORE YOU CONFIRM THIS!";
21
22
    private static $test_array = array(
23
        "In SiteTree_Live but not in SiteTree" =>
24
        "SELECT SiteTree.ID, SiteTree.Title FROM SiteTree_Live RIGHT JOIN SiteTree ON SiteTree_Live.ID = SiteTree.ID WHERE SiteTree.ID IS NULL;",
25
        "ParentID does not exist in SiteTree" =>
26
            "SELECT SiteTree.ID, SiteTree.Title FROM SiteTree RIGHT JOIN SiteTree Parent ON SiteTree.ParentID = Parent.ID Where SiteTree.ID IS NULL and SiteTree.ParentID <> 0;",
27
        "ParentID does not exists in SiteTree_Live" =>
28
            "SELECT SiteTree_Live.ID, SiteTree_Live.Title FROM SiteTree_Live RIGHT JOIN SiteTree_Live Parent ON SiteTree_Live.ParentID = Parent.ID Where SiteTree_Live.ID IS NULL and SiteTree_Live.ParentID <> 0;",
29
    );
30
31
    private static $global_exceptions = array(
32
        "EditableFormField" => "Version",
33
        "EditableOption" => "Version",
34
        "OrderItem" => "Version"
35
    );
36
37
    /**
38
    *@param array = should be provided as follows: array("Member.UselessField1", "Member.UselessField2", "SiteTree.UselessField3")
39
    */
40
    private static $fields_to_delete = array();
41
42
    private static $allowed_actions = array(
43
        "obsoletefields" => "ADMIN",
44
        "deleteonefield" => "ADMIN",
45
        "deletemarkedfields" => "ADMIN",
46
        "deleteobsoletetables" => "ADMIN",
47
        "deleteallversions" => "ADMIN",
48
        "cleanupdb" => "ADMIN"
49
    );
50
51
52
    public function init()
53
    {
54
        //this checks security
55
        parent::init();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class BuildTask as the method init() does only exist in the following sub-classes of BuildTask: CleanImageManipulationCache, DataIntegrityTest, EncryptAllPasswordsTask, RegenerateCachedImagesTask, i18nTextCollectorTask. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
56
    }
57
58
    public function run($request)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
59
    {
60
        ini_set('max_execution_time', 3000);
61
        if ($action = $request->getVar("do")) {
62
            $methodArray = explode("/", $action);
63
            $method = $methodArray[0];
64
            $allowedActions = Config::inst()->get("DataIntegrityTest", "allowed_actions");
65
            if (isset($allowedActions[$method])) {
66
                if ($method == "obsoletefields") {
67
                    $deletesafeones = $fixbrokendataobjects = $deleteall = false;
68
                    if (isset($_GET["deletesafeones"]) && $_GET["deletesafeones"]) {
69
                        $deletesafeones = true;
70
                    }
71
                    if (isset($_GET["fixbrokendataobjects"]) && $_GET["fixbrokendataobjects"]) {
72
                        $fixbrokendataobjects = true;
73
                    }
74
                    if (isset($_GET["deleteall"]) && $_GET["deleteall"]) {
75
                        $deleteall = true;
76
                    }
77
                    return $this->$method($deletesafeones, $fixbrokendataobjects, $deleteall);
78
                } else {
79
                    return $this->$method();
80
                }
81
            } else {
82
                user_error("could not find method: $method");
83
            }
84
        }
85
        $warning = Config::inst()->get("DataIntegrityTest", "warning");
86
        echo "<h2>Database Administration Helpers</h2>";
87
        echo "<p><a href=\"".$this->Link()."?do=obsoletefields\">Prepare a list of obsolete fields.</a></p>";
88
        echo "<p><a href=\"".$this->Link()."?do=obsoletefields&amp;deletesafeones=1\" onclick=\"return confirm('".$warning."');\">Prepare a list of obsolete fields and DELETE! obsolete fields without any data.</a></p>";
89
        echo "<p><a href=\"".$this->Link()."?do=obsoletefields&amp;fixbrokendataobjects=1\" onclick=\"return confirm('".$warning."');\">Fix broken dataobjects.</a></p>";
90
        echo "<p><a href=\"".$this->Link()."?do=obsoletefields&amp;deleteall=1\" onclick=\"return confirm('".$warning."');\">Delete all obsolete fields now!</a></p>";
91
        echo "<hr />";
92
        echo "<p><a href=\"".$this->Link()."?do=deletemarkedfields\" onclick=\"return confirm('".$warning."');\">Delete fields listed in _config.</a></p>";
93
        echo "<hr />";
94
        echo "<p><a href=\"".$this->Link()."?do=deleteobsoletetables\" onclick=\"return confirm('".$warning."');\">Delete all tables that are marked as obsolete</a></p>";
95
        echo "<hr />";
96
        echo "<p><a href=\"".$this->Link()."?do=deleteallversions\" onclick=\"return confirm('".$warning."');\">Delete all versioned data</a></p>";
97
        echo "<hr />";
98
        echo "<p><a href=\"".$this->Link()."?do=cleanupdb\" onclick=\"return confirm('".$warning."');\">Clean up Database (remove obsolete records)</a></p>";
99
        echo "<hr />";
100
        echo "<p><a href=\"/dev/tasks/DataIntegrityTestInnoDB/\" onclick=\"return confirm('".$warning."');\">Set all tables to innoDB</a></p>";
101
        echo "<p><a href=\"/dev/tasks/DataIntegrityTestUTF8/\" onclick=\"return confirm('".$warning."');\">Set all tables to utf-8</a></p>";
102
    }
103
104
    protected function Link()
105
    {
106
        return "/dev/tasks/DataIntegrityTest/";
107
    }
108
109
    protected function obsoletefields($deleteSafeOnes = false, $fixBrokenDataObject = false, $deleteAll = false)
110
    {
111
        increase_time_limit_to(600);
112
        $dataClasses = ClassInfo::subclassesFor('DataObject');
113
        $notCheckedArray = array();
114
        $canBeSafelyDeleted = array();
115
        //remove dataobject
116
        array_shift($dataClasses);
117
        $rows = DB::query("SHOW TABLES;");
118
        $actualTables = array();
119
        if ($rows) {
120
            foreach ($rows as $key => $item) {
121
                foreach ($item as $table) {
122
                    $actualTables[$table] = $table;
123
                }
124
            }
125
        }
126
        echo "<h1>Report of fields that may not be required.</h1>";
127
        echo "<p>NOTE: it may contain fields that are actually required (e.g. versioning or many-many relationships) and it may also leave out some obsolete fields.  Use as a guide only.</p>";
128
        foreach ($dataClasses as $dataClass) {
129
            // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
130
            if (class_exists($dataClass)) {
131
                $dataObject = $dataClass::create();
132
                if (!($dataObject instanceof TestOnly)) {
133
                    $requiredFields = $this->swapArray(DataObject::database_fields($dataObject->ClassName));
134
                    if (count($requiredFields)) {
135
                        foreach ($requiredFields as $field) {
136
                            if (!$dataObject->hasOwnTableDatabaseField($field)) {
137
                                DB::alteration_message("  **** $dataClass.$field DOES NOT EXIST BUT IT SHOULD BE THERE!", "deleted");
138
                            }
139
                        }
140
                        $actualFields = $this->swapArray(DB::fieldList($dataClass));
0 ignored issues
show
Deprecated Code introduced by
The method DB::fieldList() has been deprecated with message: since version 4.0 Use DB::field_list instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
141
                        if ($actualFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $actualFields 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...
142
                            foreach ($actualFields as $actualField) {
143
                                if ($deleteAll) {
144
                                    $link = " !!!!!!!!!!! DELETED !!!!!!!!!";
145
                                } else {
146
                                    $warning = Config::inst()->get("DataIntegrityTest", "warning");
147
                                    $link = "<a href=\"".Director::absoluteBaseURL()."dev/tasks/DataIntegrityTest/?do=deleteonefield/".$dataClass."/".$actualField."/\" onclick=\"return confirm('".$warning."');\">delete field</a>";
148
                                }
149
                                if (!in_array($actualField, array("ID", "Version"))) {
150
                                    if (!in_array($actualField, $requiredFields)) {
151
                                        $distinctCount = DB::query("SELECT COUNT(DISTINCT \"$actualField\") FROM \"$dataClass\" WHERE \"$actualField\" IS NOT NULL AND \"$actualField\" <> '' AND \"$actualField\" <> '0';")->value();
152
                                        DB::alteration_message("<br /><br />\n\n$dataClass.$actualField $link - unique entries: $distinctCount", "deleted");
153
                                        if ($distinctCount) {
154
                                            $rows = DB::query("
155
                                                SELECT \"$actualField\" as N, COUNT(\"$actualField\") as C
156
                                                FROM \"$dataClass\"
157
                                                GROUP BY \"$actualField\"
158
                                                ORDER BY C DESC
159
                                                LIMIT 7");
160
                                            if ($rows) {
161
                                                foreach ($rows as $row) {
162
                                                    DB::alteration_message(" &nbsp; &nbsp; &nbsp; ".$row["C"].": ".$row["N"]);
163
                                                }
164
                                            }
165
                                        } else {
166
                                            if (!isset($canBeSafelyDeleted[$dataClass])) {
167
                                                $canBeSafelyDeleted[$dataClass] = array();
168
                                            }
169
                                            $canBeSafelyDeleted[$dataClass][$actualField] = "$dataClass.$actualField";
170
                                        }
171
                                        if ($deleteAll || ($deleteSafeOnes && $distinctCount == 0)) {
172
                                            $this->deleteField($dataClass, $actualField);
173
                                        }
174
                                    }
175
                                }
176
                                if ($actualField == "Version" && !in_array($actualField, $requiredFields)) {
177
                                    $versioningPresent = $dataObject->hasVersioning();
178
                                    if (!$versioningPresent) {
179
                                        DB::alteration_message("$dataClass.$actualField $link", "deleted");
180
                                        if ($deleteAll) {
181
                                            $this->deleteField($dataClass, $actualField);
182
                                        }
183
                                    }
184
                                }
185
                            }
186
                        }
187
                        $rawCount = DB::query("SELECT COUNT(\"ID\") FROM \"$dataClass\"")->value();
188
                        Versioned::set_reading_mode("Stage.Stage");
189
                        $realCount = 0;
190
                        $allSubClasses = array_unique(array($dataClass)+ClassInfo::subclassesFor($dataClass));
191
                        $objects = $dataClass::get()->filter(array("ClassName" =>  $allSubClasses));
192
                        if ($objects->count()) {
193
                            $realCount = $objects->count();
194
                        }
195
                        if ($rawCount != $realCount) {
196
                            echo "<hr />";
197
                            $sign = " > ";
198
                            if ($rawCount < $realCount) {
199
                                $sign = " < ";
200
                            }
201
                            DB::alteration_message("The DB Table Row Count does not seem to match the DataObject Count for <strong>$dataClass ($rawCount $sign $realCount)</strong>.  This could indicate an error as generally these numbers should match.", "deleted");
202
                            if ($fixBrokenDataObject) {
203
                                $objects = $dataClass::get()->where("LinkedTable.ID IS NULL")->leftJoin($dataClass, "$dataClass.ID = LinkedTable.ID", "LinkedTable");
204
                                if ($objects->count() > 500) {
205
                                    DB::alteration_message("It is recommended that you manually fix the difference in real vs object count in $dataClass. There are more than 500 records so it would take too long to do it now.", "deleted");
206
                                } else {
207
                                    DB::alteration_message("Now trying to recreate missing items... COUNT = ".$objects->count(), "created");
208
                                    foreach ($objects as $object) {
209
                                        if (DB::query("SELECT COUNT(\"ID\") FROM \"$dataClass\" WHERE \"ID\" = ".$object->ID.";")->value() != 1) {
210
                                            Config::inst()->update('DataObject', 'validation_enabled', false);
211
                                            $object->write(true, false, true, false);
212
                                            Config::inst()->update('DataObject', 'validation_enabled', true);
213
                                        }
214
                                    }
215
                                    $objectCount = $dataClass::get()->count();
216
                                    DB::alteration_message("Consider deleting superfluous records from table $dataClass .... COUNT =".($rawCount - $objectCount));
217
                                    $ancestors = ClassInfo::ancestry($dataClass, true);
218
                                    if ($ancestors && is_array($ancestors) && count($ancestors)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ancestors 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...
219
                                        foreach ($ancestors as $ancestor) {
220
                                            if ($ancestor != $dataClass) {
221
                                                echo "DELETE `$dataClass`.* FROM `$dataClass` LEFT JOIN `$ancestor` ON `$dataClass`.`ID` = `$ancestor`.`ID` WHERE `$ancestor`.`ID` IS NULL;";
222
                                                DB::query("DELETE `$dataClass`.* FROM `$dataClass` LEFT JOIN `$ancestor` ON `$dataClass`.`ID` = `$ancestor`.`ID` WHERE `$ancestor`.`ID` IS NULL;");
223
                                            }
224
                                        }
225
                                    }
226
                                }
227
                            }
228
                            echo "<hr />";
229
                        }
230
                        unset($actualTables[$dataClass]);
231
                    } else {
232
                        $db = DB::getConn();
0 ignored issues
show
Deprecated Code introduced by
The method DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
233
                        if ($db->hasTable($dataClass)) {
0 ignored issues
show
Deprecated Code introduced by
The method SS_Database::hasTable() has been deprecated with message: since version 4.0 Use DB::get_schema()->hasTable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
234
                            DB::alteration_message("  **** The $dataClass table exists, but according to the data-scheme it should not be there ", "deleted");
235
                        } else {
236
                            $notCheckedArray[] = $dataClass;
237
                        }
238
                    }
239
                }
240
            }
241
        }
242
243
        if (count($canBeSafelyDeleted)) {
244
            DB::alteration_message("<h2>Can be safely deleted: </h2>");
245
            foreach ($canBeSafelyDeleted as $table => $fields) {
246
                DB::alteration_message($table.": ".implode(", ", $fields));
247
            }
248
        }
249
250
        if (count($notCheckedArray)) {
251
            echo "<h3>Did not check the following classes as no fields appear to be required and hence there is no database table.</h3>";
252
            foreach ($notCheckedArray as $table) {
253 View Code Duplication
                if (DB::query("SHOW TABLES LIKE '".$table."'")->value()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
                    DB::alteration_message($table ." - NOTE: a table exists for this Class, this is an unexpected result", "deleted");
255
                } else {
256
                    DB::alteration_message($table, "created");
257
                }
258
            }
259
        }
260
261
        if (count($actualTables)) {
262
            echo "<h3>Other Tables in Database not directly linked to a Silverstripe DataObject:</h3>";
263
            foreach ($actualTables as $table) {
264
                $remove = true;
265
                if (class_exists($table)) {
266
                    $classExistsMessage = " a PHP class with this name exists.";
267
                    $obj = singleton($table);
268
                    //not sure why we have this.
269
                    if ($obj instanceof DataExtension) {
270
                        $remove = false;
271
                    } elseif (class_exists("Versioned") && $obj->hasExtension("Versioned")) {
272
                        $remove = false;
273
                    }
274
                } else {
275
                    $classExistsMessage = " NO PHP class with this name exists.";
276
                    if (substr($table, -5) == "_Live") {
277
                        $remove = false;
278
                    }
279
                    if (substr($table, -9) == "_versions") {
280
                        $remove = false;
281
                    }
282
                    //many 2 many tables...
283
                    if (strpos($table, "_")) {
284
                        $class = explode("_", $table);
0 ignored issues
show
Unused Code introduced by
$class is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
285
                        $manyManyClass = substr($table, 0, strrpos($table, '_'));
286
                        $manyManyExtension = substr($table, strrpos($table, '_') + 1 - strlen($table));
287
                        if (class_exists($manyManyClass)) {
288
                            $manyManys = Config::inst()->get($manyManyClass, "many_many");
289
                            if (isset($manyManys[$manyManyExtension])) {
290
                                $remove = false;
291
                            }
292
                        }
293
                    }
294
                }
295
                if ($remove) {
296
                    if (substr($table, 0, strlen("_obsolete_")) != "_obsolete_") {
297
                        $rowCount = DB::query("SELECT COUNT(*) FROM $table")->value();
298
                        DB::alteration_message($table.", rows ".$rowCount);
299
                        $obsoleteTableName = "_obsolete_".$table;
300
                        if (!$this->tableExists($obsoleteTableName)) {
301
                            DB::alteration_message("We recommend deleting $table or making it obsolete by renaming it to ".$obsoleteTableName, "deleted");
302
                            if ($deleteAll) {
303
                                DB::getConn()->renameTable($table, $obsoleteTableName);
0 ignored issues
show
Deprecated Code introduced by
The method DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method SS_Database::renameTable() has been deprecated with message: since version 4.0 Use DB::get_schema()->renameTable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
304
                            } else {
305
                                DB::alteration_message($table." - ".$classExistsMessage." It can be moved to _obsolete_".$table.".", "created");
306
                            }
307
                        } else {
308
                            DB::alteration_message("I'd recommend to move <strong>$table</strong> to <strong>".$obsoleteTableName."</strong>, but that table already exists", "deleted");
309
                        }
310
                    }
311
                }
312
            }
313
        }
314
315
        echo "<a href=\"".Director::absoluteURL("/dev/tasks/DataIntegrityTest/")."\">back to main menu.</a>";
316
    }
317
318
319
320
    public function deletemarkedfields()
321
    {
322
        $fieldsToDelete = Config::inst()->get("DataIntegrityTest", "fields_to_delete");
323
        if (is_array($fieldsToDelete)) {
324
            if (count($fieldsToDelete)) {
325
                foreach ($fieldsToDelete as $key => $tableDotField) {
326
                    $tableFieldArray = explode(".", $tableDotField);
327
                    $this->deleteField($tableFieldArray[0], $tableFieldArray[1]);
328
                }
329
            } else {
330
                DB::alteration_message("there are no fields to delete", "created");
331
            }
332
        } else {
333
            user_error("you need to select these fields to be deleted first (DataIntegrityTest.fields_to_delete)");
334
        }
335
        echo "<a href=\"".Director::absoluteURL("/dev/tasks/DataIntegrityTest/")."\">back to main menu.</a>";
336
    }
337
338
    public function deleteonefield()
339
    {
340
        $requestExploded = explode("/", $_GET["do"]);
341
        if (!isset($requestExploded[1])) {
342
            user_error("no table has been specified", E_USER_WARNING);
343
        }
344
        if (!isset($requestExploded[2])) {
345
            user_error("no field has been specified", E_USER_WARNING);
346
        }
347
        $table = $requestExploded[1];
348
        $field = $requestExploded[2];
349
        if ($this->deleteField($table, $field)) {
350
            DB::alteration_message("successfully deleted $field from $table now");
351
        } else {
352
            DB::alteration_message("COULD NOT delete $field from $table now", "deleted");
353
        }
354
        DB::alteration_message("<a href=\"".Director::absoluteURL("dev/tasks/DataIntegrityTest/?do=obsoletefields")."\">return to list of obsolete fields</a>", "created");
355
        echo "<a href=\"".Director::absoluteURL("/dev/tasks/DataIntegrityTest/")."\">back to main menu.</a>";
356
    }
357
358
    private function cleanupdb()
359
    {
360
        $obj = new DatabaseAdmin();
361
        $obj->cleanup();
362
        DB::alteration_message("============= COMPLETED =================", "");
363
        echo "<a href=\"".Director::absoluteURL("/dev/tasks/DataIntegrityTest/")."\">back to main menu.</a>";
364
    }
365
366
    private function deleteField($table, $field)
367
    {
368
        $fields = $this->swapArray(DB::fieldList($table));
0 ignored issues
show
Deprecated Code introduced by
The method DB::fieldList() has been deprecated with message: since version 4.0 Use DB::field_list instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
369
        $globalExeceptions = Config::inst()->get("DataIntegrityTest", "global_exceptions");
370
        if (count($globalExeceptions)) {
371
            foreach ($globalExeceptions as $exceptionTable => $exceptionField) {
0 ignored issues
show
Bug introduced by
The expression $globalExeceptions of type array|integer|double|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
372
                if ($exceptionTable == $table && $exceptionField == $field) {
373
                    DB::alteration_message("tried to delete $table.$field but this is listed as a global exception and can not be deleted", "created");
374
                    return false;
375
                }
376
            }
377
        }
378 View Code Duplication
        if (!DB::query("SHOW TABLES LIKE '".$table."'")->value()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
            DB::alteration_message("tried to delete $table.$field but TABLE does not exist", "deleted");
380
            return false;
381
        }
382
        if (!class_exists($table)) {
383
            DB::alteration_message("tried to delete $table.$field but CLASS does not exist", "deleted");
384
            return false;
385
        }
386 View Code Duplication
        if (!in_array($field, $fields)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
387
            DB::alteration_message("tried to delete $table.$field but FIELD does not exist", "deleted");
388
            return false;
389
        } else {
390
            DB::alteration_message("Deleting $field in $table", "deleted");
391
            DB::query('ALTER TABLE "'.$table.'" DROP "'.$field.'";');
392
            $obj = singleton($table);
393
            //to do: make this more reliable - checking for versioning rather than SiteTree
394
            if ($obj instanceof SiteTree) {
395
                DB::query('ALTER TABLE "'.$table.'_Live" DROP "'.$field.'";');
396
                DB::alteration_message("Deleted $field in {$table}_Live", "deleted");
397
                DB::query('ALTER TABLE "'.$table.'_versions" DROP "'.$field.'";');
398
                DB::alteration_message("Deleted $field in {$table}_versions", "deleted");
399
            }
400
            return true;
401
        }
402
    }
403
404
    private function swapArray($array)
405
    {
406
        $newArray = array();
407
        if (is_array($array)) {
408
            foreach ($array as $key => $value) {
409
                $newArray[] = $key;
410
            }
411
        }
412
        return $newArray;
413
    }
414
415
    protected function hasVersioning($dataObject)
416
    {
417
        $versioningPresent = false;
418
        $array = $dataObject->stat('extensions');
419
        if (is_array($array) && count($array)) {
420
            if (in_array("Versioned('Stage', 'Live')", $array)) {
421
                $versioningPresent = true;
422
            }
423
        }
424
        if ($dataObject->stat('versioning')) {
425
            $versioningPresent = true;
426
        }
427
        return $versioningPresent;
428
    }
429
430
431
    private function deleteobsoletetables()
432
    {
433
        $tables = DB::query('SHOW tables');
434
        $unique = array();
0 ignored issues
show
Unused Code introduced by
$unique is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
435
        foreach ($tables as $table) {
436
            $table = array_pop($table);
437
            if (substr($table, 0, 10) == "_obsolete_") {
438
                DB::alteration_message("Removing table $table", "deleted");
439
                DB::query("DROP TABLE \"$table\" ");
440
            }
441
        }
442
        echo "<a href=\"".Director::absoluteURL("/dev/tasks/DataIntegrityTest/")."\">back to main menu.</a>";
443
    }
444
445
    private function deleteallversions()
446
    {
447
        $tables = DB::query('SHOW tables');
448
        $unique = array();
0 ignored issues
show
Unused Code introduced by
$unique is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
449
        foreach ($tables as $table) {
450
            $table = array_pop($table);
451
            $endOfTable = substr($table, -9);
452
            if ($endOfTable == "_versions") {
453
                $className = substr($table, 0, strlen($table) - 9);
454
                if (class_exists($className)) {
455
                    $obj = DataObject::get_one($className);
456
                    if ($obj) {
457
                        if ($obj->hasExtension("Versioned")) {
458
                            DB::alteration_message("Removing all records from $table", "created");
459
                            DB::query("DELETE FROM \"$table\" ");
460
                        }
461
                    }
462
                } else {
463
                    DB::alteration_message("Could not find $className class... the $table may be obsolete", "deleted");
464
                }
465
            }
466
        }
467
        echo "<a href=\"".Director::absoluteURL("/dev/tasks/DataIntegrityTest/")."\">back to main menu.</a>";
468
    }
469
470
    private function tableExists($table)
471
    {
472
        $db = DB::getConn();
0 ignored issues
show
Deprecated Code introduced by
The method DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
473
        return $db->hasTable($table);
0 ignored issues
show
Deprecated Code introduced by
The method SS_Database::hasTable() has been deprecated with message: since version 4.0 Use DB::get_schema()->hasTable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
474
    }
475
}
476