Passed
Push — master ( 8910de...82f320 )
by Thomas
09:31
created

DropInvalidFilesTask::deleteFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 5
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
namespace LeKoala\DevToolkit\Tasks;
4
5
use FilesystemIterator;
6
use SilverStripe\ORM\DB;
7
use SilverStripe\Assets\File;
8
use RecursiveIteratorIterator;
9
use RecursiveDirectoryIterator;
10
use SilverStripe\Dev\BuildTask;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\Core\Environment;
13
use SilverStripe\Core\Config\Config;
14
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...
15
use LeKoala\DevToolkit\BuildTaskTools;
16
use SilverStripe\Assets\Flysystem\ProtectedAssetAdapter;
17
18
/**
19
 * @author lekoala
20
 */
21
class DropInvalidFilesTask extends BuildTask
22
{
23
    use BuildTaskTools;
24
25
    protected $title = "Drop Invalid Files";
26
    protected $description = 'Drop file objects that are not linked to a proper asset (warning ! experimental)';
27
    private static $segment = 'DropInvalidFilesTask';
0 ignored issues
show
introduced by
The private property $segment is not used, and could be removed.
Loading history...
28
29
    public function run($request)
30
    {
31
        $this->request = $request;
32
33
        $this->addOption("go", "Tick this to proceed", false);
34
        $this->addOption("remove_files", "Remove db files", false);
35
        $this->addOption("remove_local", "Remove local files", false);
36
37
        $options = $this->askOptions();
38
39
        $go = $options['go'];
40
        $remove_files = $options['remove_files'];
41
        $remove_local = $options['remove_local'];
42
43
        if (!$go) {
44
            echo ('Previewing what this task is about to do.');
45
        } else {
46
            echo ("Let's clean this up!");
47
        }
48
        echo ('<hr/>');
49
        if ($remove_files) {
50
            $this->removeFiles($request, $go);
51
        }
52
        if ($remove_local) {
53
            $this->removeLocalFiles($request, $go);
54
        }
55
    }
56
57
    protected function removeLocalFiles($request, $go = false)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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

57
    protected function removeLocalFiles(/** @scrutinizer ignore-unused */ $request, $go = 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...
58
    {
59
        $iter = new RecursiveDirectoryIterator(ASSETS_PATH);
60
        $iter2 = new RecursiveIteratorIterator($iter);
61
62
        foreach ($iter2 as $file) {
63
            // Ignore roots and _
64
            $startsWithSlash = strpos($file->getName(), '_') === 0;
65
            $hasVariant = strpos($file->getName(), '__') !== false;
66
            if ($startsWithSlash || $hasVariant) {
67
                // $this->message("Ignore " . $file->getPath());
68
                continue;
69
            }
70
71
            // Check for empty dirs
72
            if ($file->isDir()) {
73
                // ignores .dot files
74
                $dirFiles = scandir($file->getPath());
75
                $empty = (count($dirFiles) - 2) === 0;
76
                if ($empty) {
77
                    $this->message($file->getPath() . " is empty");
78
                    if ($go) {
79
                        rmdir($file->getPath());
80
                    }
81
                }
82
                continue;
83
            }
84
85
            // Check for files not matching anything in the db
86
            $thisPath = str_replace(ASSETS_PATH, "", $file->getPath());
0 ignored issues
show
Unused Code introduced by
The assignment to $thisPath is dead and can be removed.
Loading history...
87
            // $this->message($thisPath);
88
            // $dbFile = File::get()->filter("FileFilename", $thisPath);
89
        }
90
    }
91
92
    protected function removeFiles($request, $go = false)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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

92
    protected function removeFiles(/** @scrutinizer ignore-unused */ $request, $go = 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...
93
    {
94
        $conn = DB::get_conn();
95
        $schema = DB::get_schema();
96
        $dataObjectSchema = DataObject::getSchema();
0 ignored issues
show
Unused Code introduced by
The assignment to $dataObjectSchema is dead and can be removed.
Loading history...
97
        $tableList = $schema->tableList();
0 ignored issues
show
Unused Code introduced by
The assignment to $tableList is dead and can be removed.
Loading history...
98
99
        $files = File::get();
100
101
        if ($go) {
102
            $conn->transactionStart();
103
        }
104
105
        $i = 0;
106
107
        /** @var File $file  */
108
        foreach ($files as $file) {
109
            $path = self::getFullPath($file);
110
            if (!trim($file->getRelativePath(), '/')) {
111
                $this->message("#{$file->ID}: path is empty");
112
                if ($go) {
113
                    // $file->delete();
114
                    self::deleteFile($file->ID);
115
                    $i++;
116
                }
117
            }
118
            if (!file_exists($path)) {
119
                $this->message("#{$file->ID}: $path does not exist");
120
                if ($go) {
121
                    // $file->delete();
122
                    self::deleteFile($file->ID);
123
                    $i++;
124
                }
125
            } else {
126
                $this->message("#{$file->ID}: $path is valid", "success");
127
            }
128
            if ($go && $i % 100 == 0) {
129
                $conn->transactionEnd();
130
                $conn->transactionStart();
131
            }
132
        }
133
134
        if ($go) {
135
            $conn->transactionEnd();
136
        }
137
    }
138
139
    /**
140
     * ORM is just too slow for this
141
     *
142
     * @param int $ID
143
     * @return void
144
     */
145
    public static function deleteFile($ID)
146
    {
147
        DB::prepared_query("DELETE FROM File WHERE ID = ?", [$ID]);
148
        DB::prepared_query("DELETE FROM File_Live WHERE ID = ?", [$ID]);
149
        DB::prepared_query("DELETE FROM File_Versions WHERE RecordID = ?", [$ID]);
150
        DB::prepared_query("DELETE FROM File_ViewerGroups WHERE FileID = ?", [$ID]);
151
        DB::prepared_query("DELETE FROM File_EditorGroups WHERE FileID = ?", [$ID]);
152
    }
153
154
    public static function getFullPath(File $file)
155
    {
156
        return ASSETS_PATH . '/' . $file->getRelativePath();
157
    }
158
159
    public function getProtectedFullPath(File $file)
160
    {
161
        return self::getBaseProtectedPath() . '/' . $file->getRelativePath();
162
    }
163
164
    public static function getBaseProtectedPath()
165
    {
166
        // Use environment defined path or default location is under assets
167
        if ($path = Environment::getEnv('SS_PROTECTED_ASSETS_PATH')) {
168
            return $path;
169
        }
170
171
        // Default location
172
        return ASSETS_PATH . '/' . Config::inst()->get(ProtectedAssetAdapter::class, 'secure_folder');
173
    }
174
}
175