Completed
Push — master ( c4a3c9...95b644 )
by Nikola
03:44
created

StreamDestination::has()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 8
rs 9.4286
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
/*
3
 * This file is part of the Backup package, an RunOpenCode project.
4
 *
5
 * (c) 2015 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * This project is fork of "kbond/php-backup", for full credits info, please
11
 * view CREDITS file that was distributed with this source code.
12
 */
13
namespace RunOpenCode\Backup\Destination;
14
15
use RunOpenCode\Backup\Backup\Backup;
16
use RunOpenCode\Backup\Backup\File;
17
use RunOpenCode\Backup\Contract\BackupInterface;
18
use RunOpenCode\Backup\Contract\DestinationInterface;
19
use RunOpenCode\Backup\Contract\LoggerAwareInterface;
20
use RunOpenCode\Backup\Exception\DestinationException;
21
use RunOpenCode\Backup\Log\LoggerAwareTrait;
22
use Symfony\Component\Filesystem\Filesystem;
23
use Symfony\Component\Finder\Finder;
24
25
/**
26
 * Class StreamDestination
27
 *
28
 * Stream destination is local, mountable, destination.
29
 *
30
 * @package RunOpenCode\Backup\Destination
31
 */
32
class StreamDestination implements DestinationInterface, LoggerAwareInterface
33
{
34
    use LoggerAwareTrait;
35
36
    /**
37
     * @var BackupInterface[]
38
     */
39
    protected $backups;
40
41
    /**
42
     * @var string
43
     */
44
    protected $directory;
45
46
    /**
47
     * @var Filesystem
48
     */
49
    private $filesystem;
50
51
    public function __construct($directory, Filesystem $filesystem = null)
52
    {
53
        $this->directory = $directory;
54
        $this->filesystem = is_null($filesystem) ? new Filesystem() : $filesystem;
55
56
        if (!$filesystem->exists($this->directory)) {
57
58
            $filesystem->mkdir($this->directory);
0 ignored issues
show
Bug introduced by
It seems like $filesystem is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
59
60
        } elseif (!is_dir($this->directory)) {
61
            throw new \RuntimeException(sprintf('Provided location "%s" is not directory.', $this->directory));
62
        } elseif (!is_writable($this->directory)) {
63
            throw new \RuntimeException(sprintf('Provided location "%s" is not writeable.', $this->directory));
64
        }
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function push(BackupInterface $backup)
71
    {
72
        $backupDirectory = sprintf('%s%s%s', rtrim($this->directory, DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR, $backup->getName());
73
        $this->filesystem->mkdir($backupDirectory);
74
75
        $existing = array();
76
77
        foreach ($existingFiles = Finder::create()->in($backupDirectory)->files() as $existingFile) {
78
            $file = File::fromLocal($existingFile, $backupDirectory);
79
            $existing[$file->getRelativePath()] = $file;
80
        }
81
82
        foreach ($backupFiles = $backup->getFiles() as $backupFile) {
83
84
            try {
85
                $this->filesystem->copy($backupFile, $filePath = sprintf('%s%s%s', $backupDirectory, DIRECTORY_SEPARATOR, $backupFile->getRelativePath()));
0 ignored issues
show
Documentation introduced by
$backupFile is of type object<RunOpenCode\Backup\Contract\FileInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
86
                $this->filesystem->touch($filePath, $backupFile->getModifiedAt()->getTimestamp());
87
            } catch (\Exception $e) {
88
                throw new DestinationException(sprintf('Unable to backup file "%s" to destination "%s".', $backupFile->getPath(), $this->directory), 0, $e);
89
            }
90
91
            if ($existingFiles[$backupFile->getRelativePath()]) {
92
                unset($existingFiles[$backupFile->getRelativePath()]);
93
            }
94
        }
95
96
        $this->filesystem->remove($existingFiles);
97
        $this->removeEmptyDirectories($backupDirectory);
98
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function getIterator()
105
    {
106
        if (is_null($this->backups)) {
107
            $this->load();
108
        }
109
110
        return new \ArrayIterator($this->backups);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function get($key)
117
    {
118
        if (is_null($this->backups)) {
119
            $this->load();
120
        }
121
122
        return $this->backups[$key];
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function has($key)
129
    {
130
        if (is_null($this->backups)) {
131
            $this->load();
132
        }
133
134
        return array_key_exists($key, $this->backups);
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function delete($key)
141
    {
142
        if (is_null($this->backups)) {
143
            $this->load();
144
        }
145
        try {
146
            $this->filesystem->remove(sprintf('%s%s%s', rtrim($this->directory, DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR, $key));
147
        } catch (\Exception $e) {
148
            throw new DestinationException(sprintf('Unable to remove backup "%s" from stream destination "%s".', $key, $this->directory), 0, $e);
149
        }
150
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function all()
157
    {
158
        if (is_null($this->backups)) {
159
            $this->load();
160
        }
161
162
        return $this->backups;
163
    }
164
165
    /**
166
     * Load backups from destination.
167
     *
168
     * @return BackupInterface[]
169
     */
170
    protected function load()
171
    {
172
        $this->backups = array();
173
174
        $backupDirectories = Finder::create()->in($this->directory)->depth(0)->directories()->sortByModifiedTime();
175
176
        foreach ($backupDirectories as $backupDirectory) {
177
178
            $backup = new Backup(basename($backupDirectory), array(), 0, filectime($backupDirectory), filemtime($backupDirectory));
179
180
            foreach ($backupFiles = Finder::create()->in($backupDirectory)->files() as $backupFile) {
181
182
                $backup->addFile(File::fromLocal($backupFile, $backupDirectory));
183
            }
184
185
            $this->backups[$backup->getName()] = $backup;
186
        }
187
    }
188
189
    /**
190
     * Remove empty directories from destination.
191
     *
192
     * @param $backupDirectory
193
     */
194
    protected function removeEmptyDirectories($backupDirectory)
195
    {
196
        foreach ($dirs = Finder::create()->directories()->in($backupDirectory)->depth(0) as $dir) {
197
198
            if (Finder::create()->files()->in($dir)->count() > 0) {
199
                $this->removeEmptyDirectories($dir);
200
            } else {
201
                $this->filesystem->remove($dir);
202
            }
203
        }
204
    }
205
}