Passed
Push — master ( 523107...3d7bf1 )
by Andreas
25:53
created

blobdir::cleanup_file()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 4
nc 3
nop 2
dl 0
loc 6
ccs 0
cts 6
cp 0
crap 30
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.console
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
namespace midcom\console\command\cleanup;
10
11
use Symfony\Component\Console\Command\Command;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use midcom_db_attachment;
16
17
/**
18
 * Cleanup the blobdir
19
 * Search for corrupted (0 bytes) files
20
 *
21
 * @package midcom.console
22
 */
23
class blobdir extends Command
24
{
25
    /**
26
     * @var int
27
     */
28
    private $_file_counter = 0;
29
30
    /**
31
     * @var string
32
     */
33
    private $_dir = "";
34
35
    private $dry = false;
36
37
    private $findings = [
38
        'corrupted' => [],
39
        'orphaned' => []
40
    ];
41
42
    protected function configure()
43
    {
44
        $this->setName('midcom:cleanup:blobdir')
45
            ->setAliases(['blobdircleanup'])
46
            ->setDescription('Cleanup the blobdir')
47
            ->addOption('dry', 'd', InputOption::VALUE_NONE, 'If set, files and attachments will not be deleted');
48
    }
49
50
    public function check_dir(string $outerDir)
51
    {
52
        $outerDir = rtrim($outerDir, "/");
53
        $dirs = array_diff(scandir($outerDir), [".", ".."]);
54
        foreach ($dirs as $d) {
55
            if (is_dir($outerDir . "/" . $d)) {
56
                $this->check_dir($outerDir . "/" . $d);
57
            } else {
58
                // got something
59
                $file = $outerDir . "/" . $d;
60
                if (filesize($file) == 0) {
61
                    $this->findings['corrupted'][] = $file;
62
                } elseif (!$this->get_attachment($file)) {
63
                    $this->findings['orphaned'][] = $file;
64
                }
65
                $this->_file_counter++;
66
            }
67
        }
68
    }
69
70
    private function _determine_location(string $path) : string
71
    {
72
        return ltrim(str_replace($this->_dir, "", $path), "/");
73
    }
74
75
    private function get_attachment(string $file) : ?midcom_db_attachment
76
    {
77
        $location = $this->_determine_location($file);
78
        // get attachments
79
        $qb = midcom_db_attachment::new_query_builder();
80
        $qb->add_constraint("location", "=", $location);
81
        $attachments = $qb->execute();
82
        if (empty($attachments)) {
83
            return null;
84
        }
85
        if (count($attachments) === 1) {
86
            return $attachments[0];
87
        }
88
        throw new \midcom_error('Multiple attachments share location ' . $location);
89
    }
90
91
    private function cleanup_corrupted(OutputInterface $output, array $files)
92
    {
93
        $i = 0;
94
        foreach ($files as $file) {
95
            $i++;
96
            // cleanup file
97
            $output->writeln($i . ") " . $file);
98
            $this->cleanup_file($output, $file);
99
100
            if ($att = $this->get_attachment($file)) {
101
                if (!$this->dry) {
102
                    $stat = $att->purge();
103
                    if (!$stat || $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
104
                        $output->writeln(($stat) ? "<info>Purge OK</info>" : "<comment>Purge FAILED, reason: " . \midcom_connection::get_error_string() . "</comment>");
105
                    }
106
                }
107
            }
108
        }
109
    }
110
111
    private function cleanup_file(OutputInterface $output, string $file)
112
    {
113
        if (!$this->dry) {
114
            $stat = unlink($file);
115
            if (!$stat || $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
116
                $output->writeln(($stat) ? "<info>Cleanup OK</info>" : "<comment>Cleanup FAILED</comment>");
117
            }
118
        }
119
    }
120
121
    protected function execute(InputInterface $input, OutputInterface $output) : int
122
    {
123
        $dir = \midgard_connection::get_instance()->config->blobdir;
124
        if (!is_dir($dir)) {
125
            $output->writeln("<comment>Unable to detect blobdir</comment>");
126
            return 1;
127
        }
128
        $this->_dir = $dir;
129
        $this->dry = $input->getOption("dry");
130
        if ($this->dry) {
131
            $output->writeln("<comment>Running in dry mode!</comment>");
132
        }
133
134
        $output->writeln("Start scanning dir: <comment>" . $dir . "</comment>");
135
136
        $this->check_dir($dir);
137
138
        $output->writeln("Scanned <info>" . $this->_file_counter . "</info> files");
139
        $output->writeln("Found <info>" . count($this->findings['corrupted']) . "</info> corrupted files:");
140
        $output->writeln("Found <info>" . count($this->findings['orphaned']) . "</info> orphaned files:");
141
142
        $this->cleanup_corrupted($output, $this->findings['corrupted']);
143
144
        foreach ($this->findings['orphaned'] as $file) {
145
            $this->cleanup_file($output, $file);
146
        }
147
148
        $output->writeln("<comment>Done</comment>");
149
        return 0;
150
    }
151
}
152