Completed
Push — master ( eab519...be8cc6 )
by Sebastian
05:27
created

GoogleDrive   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 233
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 14

Test Coverage

Coverage 65.06%

Importance

Changes 0
Metric Value
wmc 21
lcom 2
cbo 14
dl 0
loc 233
ccs 54
cts 83
cp 0.6506
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A setup() 0 16 4
A setupAuthFiles() 0 13 3
A sync() 0 31 5
A simulate() 0 7 1
A createDriveService() 0 18 3
A createFile() 0 8 1
A createUploadStream() 0 14 1
A getAccessToken() 0 4 1
A updateAccessToken() 0 4 1
A createCollector() 0 4 1
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;
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);
131 2
            $handle    = fopen($target->getPathname(), "rb");
132 2
            while (!$status && !feof($handle)) {
133 2
                $chunk  = fread($handle, $this->chunkSize);
134 2
                $status = $stream->nextChunk($chunk);
135
            }
136 2
            fclose($handle);
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()));
144 2
            $this->cleanup($target, $result);
145
146 1
        } catch (\Exception $e) {
147 1
            throw new Exception($e->getMessage(), null, $e);
148
        }
149 2
    }
150
151
    /**
152
     * Simulate the sync execution.
153
     *
154
     * @param \phpbu\App\Backup\Target $target
155
     * @param \phpbu\App\Result        $result
156
     */
157 1
    public function simulate(Target $target, Result $result)
158
    {
159 1
        $result->debug('sync backup to google drive' . PHP_EOL);
160
161 1
        $this->isSimulation = true;
162 1
        $this->simulateRemoteCleanup($target, $result);
163 1
    }
164
165
    /**
166
     * Setup google api client and google drive service.
167
     *
168
     * @throws \Google_Exception
169
     */
170
    protected function createDriveService() : GDriveService
171
    {
172
        if (!$this->service) {
173
            $client = new GClient();
174
            $client->setApplicationName('phpbu');
175
            $client->setScopes(GDrive::DRIVE);
176
            $client->setAuthConfig($this->secret);
177
            $client->setAccessType('offline');
178
            $client->setAccessToken($this->getAccessToken());
179
180
            if ($client->isAccessTokenExpired()) {
181
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
182
                $this->updateAccessToken($client->getAccessToken());
183
            }
184
            $this->service = new GDriveService($client);
185
        }
186
        return $this->service;
187
    }
188
189
    /**
190
     * Create google api file.
191
     *
192
     * @param  \phpbu\App\Backup\Target $target
193
     * @return \Google_Service_Drive_DriveFile
194
     */
195 2
    protected function createFile(Target $target) : GFile
196
    {
197 2
        $file = new GFile();
198 2
        $file->setName($target->getFilename());
199 2
        $file->setParents([$this->parent]);
200
201 2
        return $file;
202
    }
203
204
    /**
205
     * Create google api file deferred upload.
206
     *
207
     * @param  \Google_Client                     $client
208
     * @param  \Psr\Http\Message\RequestInterface $request
209
     * @param  \phpbu\App\Backup\Target           $target
210
     * @return \Google_Http_MediaFileUpload
211
     * @throws \phpbu\App\Exception
212
     */
213
    protected function createUploadStream(GClient $client, RequestInterface $request, Target $target) : GStream
214
    {
215
        $media = new GStream(
216
            $client,
217
            $request,
218
            'application/octet-stream',
219
            null,
220
            true,
221
            $this->chunkSize
222
        );
223
        $media->setFileSize($target->getSize());
224
225
        return $media;
226
    }
227
228
    /**
229
     * Return google credentials.
230
     *
231
     * @return array
232
     */
233
    private function getAccessToken() : array
234
    {
235
        return json_decode(file_get_contents($this->access), true);
236
    }
237
238
    /**
239
     * Update the access token in the google credentials file.
240
     *
241
     * @param  array $accessToken
242
     * @return void
243
     */
244
    private function updateAccessToken(array $accessToken)
245
    {
246
        file_put_contents($this->access, json_encode($accessToken));
247
    }
248
249
    /**
250
     * Creates collector for remote cleanup.
251
     *
252
     * @param  \phpbu\App\Backup\Target $target
253
     * @return \phpbu\App\Backup\Collector
254
     * @throws \Google_Exception
255
     */
256 1
    protected function createCollector(Target $target): Collector
257
    {
258 1
        return new Collector\GoogleDrive($target, new Path($this->parent), $this->createDriveService());
259
    }
260
}
261