GoogleDrive::createDriveService()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 0
dl 0
loc 17
ccs 0
cts 13
cp 0
crap 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
namespace phpbu\App\Backup\Sync;
3
4
use Google_Client as GClient;
5
use Google_Http_MediaFileUpload as GStream;
6
use Google_Service_Drive as GDrive;
7
use Google_Service_Drive as GDriveService;
8
use Google_Service_Drive_DriveFile as GFile;
9
use phpbu\App\Backup\Collector;
10
use phpbu\App\Backup\Path;
11
use phpbu\App\Configuration;
12
use phpbu\App\Result;
13
use phpbu\App\Backup\Target;
14
use phpbu\App\Util;
15
use Psr\Http\Message\RequestInterface;
16
17
/**
18
 * Google Drive
19
 *
20
 * @package    phpbu
21
 * @subpackage Backup
22
 * @author     Sebastian Feldmann <[email protected]>
23
 * @copyright  Sebastian Feldmann <[email protected]>
24
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
25
 * @link       http://phpbu.de/
26
 * @since      Class available since Release 3.1.1
27
 */
28
class GoogleDrive implements Simulator
29
{
30
    use Cleanable;
0 ignored issues
show
introduced by
The trait phpbu\App\Backup\Sync\Cleanable requires some properties which are not provided by phpbu\App\Backup\Sync\GoogleDrive: $type, $options
Loading history...
31
32
    /**
33
     * Google drive service.
34
     *
35
     * @var \Google_Service_Drive
36
     */
37
    private $service;
38
39
    /**
40
     * Google json secret file.
41
     *
42
     * @var string
43
     */
44
    private $secret;
45
46
    /**
47
     * Google json credentials file.
48
     *
49
     * @var string
50
     */
51
    private $access;
52
53
    /**
54
     * Google drive parent folder id.
55
     *
56
     * @var string
57
     */
58
    private $parent;
59
60
    /**
61
     * Upload chunk size.
62
     *
63
     * @var int
64
     */
65
    private $chunkSize = 1 * 1024 * 1024;
66
67
    /**
68
     * (non-PHPDoc)
69
     *
70
     * @see    \phpbu\App\Backup\Sync::setup()
71
     * @param  array $config
72
     * @throws \phpbu\App\Exception
73
     */
74 9
    public function setup(array $config)
75
    {
76 9
        if (!class_exists('\\Google_Client')) {
77
            throw new Exception('google api client not loaded: use composer to install "google/apiclient"');
78
        }
79 9
        if (!Util\Arr::isSetAndNotEmptyString($config, 'secret')) {
80 1
            throw new Exception('google secret json file is mandatory');
81
        }
82 8
        if (!Util\Arr::isSetAndNotEmptyString($config, 'access')) {
83 1
            throw new Exception('google credentials json file is mandatory');
84
        }
85 7
        $this->setupAuthFiles($config);
86 5
        $this->parent = Util\Arr::getValue($config, 'parentId');
87
88 5
        $this->setUpCleanable($config);
89 5
    }
90
91
    /**
92
     * Make sure both google authentication files exist and determine absolute path to them.
93
     *
94
     * @param  array $config
95
     * @throws \phpbu\App\Backup\Sync\Exception
96
     */
97 7
    private function setupAuthFiles(array $config)
98
    {
99 7
        $secret = Util\Path::toAbsolutePath($config['secret'], Configuration::getWorkingDirectory());
100 7
        if (!file_exists($secret)) {
101 1
            throw new Exception(sprintf('google secret json file not found at %s', $secret));
102
        }
103 6
        $access = Util\Path::toAbsolutePath($config['access'], Configuration::getWorkingDirectory());
104 6
        if (!file_exists($access)) {
105 1
            throw new Exception(sprintf('google credentials json file not found at %s', $access));
106
        }
107 5
        $this->secret = $secret;
108 5
        $this->access = $access;
109 5
    }
110
111
    /**
112
     * Execute the Sync
113
     *
114
     * @see    \phpbu\App\Backup\Sync::sync()
115
     * @param  \phpbu\App\Backup\Target $target
116
     * @param  \phpbu\App\Result        $result
117
     * @throws \phpbu\App\Backup\Sync\Exception
118
     */
119 3
    public function sync(Target $target, Result $result)
120
    {
121
        try {
122 3
            $service = $this->createDriveService();
123 3
            $client  = $service->getClient();
124 2
            $client->setDefer(true);
125
126 2
            $status    = false;
127 2
            $apiResult = false;
128 2
            $apiFile   = $this->createFile($target);
129 2
            $request   = $service->files->create($apiFile);
130 2
            $stream    = $this->createUploadStream($client, $request, $target);
0 ignored issues
show
Bug introduced by
$request of type Google_Service_Drive_DriveFile is incompatible with the type Psr\Http\Message\RequestInterface expected by parameter $request of phpbu\App\Backup\Sync\Go...e::createUploadStream(). ( Ignorable by Annotation )

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

130
            $stream    = $this->createUploadStream($client, /** @scrutinizer ignore-type */ $request, $target);
Loading history...
131 2
            $handle    = fopen($target->getPathname(), "rb");
132 2
            while (!$status && !feof($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $handle of feof() does only seem to accept resource, 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

132
            while (!$status && !feof(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
133 2
                $chunk  = fread($handle, $this->chunkSize);
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $handle of fread() does only seem to accept resource, 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

133
                $chunk  = fread(/** @scrutinizer ignore-type */ $handle, $this->chunkSize);
Loading history...
134 2
                $status = $stream->nextChunk($chunk);
135
            }
136 2
            fclose($handle);
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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

136
            fclose(/** @scrutinizer ignore-type */ $handle);
Loading history...
137 2
            $client->setDefer(false);
138
139
            /** @var \Google_Service_Drive_DriveFile $apiResult */
140 2
            if ($status != false) {
141 2
                $apiResult = $status;
142
            }
143 2
            $result->debug(sprintf('upload: done: %s', $apiResult->getId()));
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Psr\Http\Message\ResponseInterface. ( Ignorable by Annotation )

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

143
            $result->debug(sprintf('upload: done: %s', $apiResult->/** @scrutinizer ignore-call */ getId()));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
144 2
            $this->cleanup($target, $result);
145 1
        } catch (\Exception $e) {
146 1
            throw new Exception($e->getMessage(), null, $e);
147
        }
148 2
    }
149
150
    /**
151
     * Simulate the sync execution.
152
     *
153
     * @param \phpbu\App\Backup\Target $target
154
     * @param \phpbu\App\Result        $result
155
     */
156 1
    public function simulate(Target $target, Result $result)
157
    {
158 1
        $result->debug('sync backup to google drive' . PHP_EOL);
159
160 1
        $this->isSimulation = true;
161 1
        $this->simulateRemoteCleanup($target, $result);
162 1
    }
163
164
    /**
165
     * Setup google api client and google drive service.
166
     *
167
     * @throws \Google_Exception
168
     */
169
    protected function createDriveService() : GDriveService
170
    {
171
        if (!$this->service) {
172
            $client = new GClient();
173
            $client->setApplicationName('phpbu');
174
            $client->setScopes(GDrive::DRIVE);
175
            $client->setAuthConfig($this->secret);
176
            $client->setAccessType('offline');
177
            $client->setAccessToken($this->getAccessToken());
178
179
            if ($client->isAccessTokenExpired()) {
180
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
181
                $this->updateAccessToken($client->getAccessToken());
182
            }
183
            $this->service = new GDriveService($client);
184
        }
185
        return $this->service;
186
    }
187
188
    /**
189
     * Create google api file.
190
     *
191
     * @param  \phpbu\App\Backup\Target $target
192
     * @return \Google_Service_Drive_DriveFile
193
     */
194 2
    protected function createFile(Target $target) : GFile
195
    {
196 2
        $file = new GFile();
197 2
        $file->setName($target->getFilename());
198 2
        $file->setParents([$this->parent]);
199
200 2
        return $file;
201
    }
202
203
    /**
204
     * Create google api file deferred upload.
205
     *
206
     * @param  \Google_Client                     $client
207
     * @param  \Psr\Http\Message\RequestInterface $request
208
     * @param  \phpbu\App\Backup\Target           $target
209
     * @return \Google_Http_MediaFileUpload
210
     * @throws \phpbu\App\Exception
211
     */
212
    protected function createUploadStream(GClient $client, RequestInterface $request, Target $target) : GStream
213
    {
214
        $media = new GStream(
215
            $client,
216
            $request,
217
            'application/octet-stream',
218
            null,
219
            true,
220
            $this->chunkSize
0 ignored issues
show
Bug introduced by
$this->chunkSize of type integer is incompatible with the type boolean expected by parameter $chunkSize of Google_Http_MediaFileUpload::__construct(). ( Ignorable by Annotation )

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

220
            /** @scrutinizer ignore-type */ $this->chunkSize
Loading history...
221
        );
222
        $media->setFileSize($target->getSize());
223
224
        return $media;
225
    }
226
227
    /**
228
     * Return google credentials.
229
     *
230
     * @return array
231
     */
232
    private function getAccessToken() : array
233
    {
234
        return json_decode(file_get_contents($this->access), true);
235
    }
236
237
    /**
238
     * Update the access token in the google credentials file.
239
     *
240
     * @param  array $accessToken
241
     * @return void
242
     */
243
    private function updateAccessToken(array $accessToken)
244
    {
245
        file_put_contents($this->access, json_encode($accessToken));
246
    }
247
248
    /**
249
     * Creates collector for remote cleanup.
250
     *
251
     * @param  \phpbu\App\Backup\Target $target
252
     * @return \phpbu\App\Backup\Collector
253
     * @throws \Google_Exception
254
     */
255 1
    protected function createCollector(Target $target): Collector
256
    {
257 1
        return new Collector\GoogleDrive($target, new Path($this->parent), $this->createDriveService());
258
    }
259
}
260