Completed
Push — master ( 567a90...f50758 )
by Sebastian
02:46
created

GoogleDrive::setupAuthFiles()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 13
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
crap 12
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
     * @var \Google_Client
34
     */
35
    private $client;
36
37
    /**
38
     * Google json secret file.
39
     *
40
     * @var string
41
     */
42
    private $secret;
43
44
    /**
45
     * Google json credentials file.
46
     *
47
     * @var string
48
     */
49
    private $access;
50
51
    /**
52
     * Google drive parent folder id.
53
     *
54
     * @var string
55
     */
56
    private $parent;
57
58
    /**
59
     * Upload chunk size.
60
     *
61
     * @var int
62
     */
63
    private $chunkSize = 1 * 1024 * 1024;
64
65
    /**
66
     * (non-PHPDoc)
67
     *
68
     * @see    \phpbu\App\Backup\Sync::setup()
69
     * @param  array $config
70
     * @throws \phpbu\App\Backup\Sync\Exception
71
     */
72
    public function setup(array $config)
73
    {
74
        if (!class_exists('\\Google_Client')) {
75
            throw new Exception('google api client not loaded: use composer to install "google/apiclient"');
76
        }
77
        if (!Util\Arr::isSetAndNotEmptyString($config, 'secret')) {
78
            throw new Exception('google secret json file is mandatory');
79
        }
80
        if (!Util\Arr::isSetAndNotEmptyString($config, 'access')) {
81
            throw new Exception('google credentials json file is mandatory');
82
        }
83
        $this->setupAuthFiles($config);
84
        $this->parent = Util\Arr::getValue($config, 'parentId');
85
    }
86
87
    /**
88
     * Make sure both google authentication files exist and determine absolute path to them.
89
     *
90
     * @param  array $config
91
     * @throws \phpbu\App\Backup\Sync\Exception
92
     */
93
    private function setupAuthFiles(array $config)
94
    {
95
        $secret = Util\Path::toAbsolutePath($config['secret'], Configuration::getWorkingDirectory());
96
        if (!file_exists($secret)) {
97
            throw new Exception(sprintf('google secret json file not found at %s', $secret));
98
        }
99
        $access = Util\Path::toAbsolutePath($config['access'], Configuration::getWorkingDirectory());
100
        if (!file_exists($access)) {
101
            throw new Exception(sprintf('google credentials json file not found at %s', $access));
102
        }
103
        $this->secret = $secret;
104
        $this->access = $access;
105
    }
106
107
    /**
108
     * Execute the Sync
109
     *
110
     * @see    \phpbu\App\Backup\Sync::sync()
111
     * @param  \phpbu\App\Backup\Target $target
112
     * @param  \phpbu\App\Result        $result
113
     * @throws \phpbu\App\Backup\Sync\Exception
114
     */
115
    public function sync(Target $target, Result $result)
116
    {
117
        try {
118
            $status    = false;
119
            $apiResult = false;
120
            $apiFile   = $this->createFile($target);
121
            $client    = $this->createClient();
122
            $client->setDefer(true);
123
124
            $service = new GDriveService($client);
125
            $request = $service->files->create($apiFile);
126
            $stream  = $this->createUploadStream($client, $request, $target);
127
            $handle  = fopen($target->getPathname(), "rb");
128
            while (!$status && !feof($handle)) {
129
                $chunk  = fread($handle, $this->chunkSize);
130
                $status = $stream->nextChunk($chunk);
131
            }
132
            fclose($handle);
133
            $client->setDefer(false);
134
135
            /** @var \Google_Service_Drive_DriveFile $apiResult */
136
            if ($status != false) {
137
                $apiResult = $status;
138
            }
139
140
            $this->cleanup($target, $result);
141
142
        } catch (\Exception $e) {
143
            throw new Exception($e->getMessage(), null, $e);
144
        }
145
        $result->debug(sprintf('upload: done: %s', $apiResult->getId()));
146
    }
147
148
    /**
149
     * Simulate the sync execution.
150
     *
151
     * @param \phpbu\App\Backup\Target $target
152
     * @param \phpbu\App\Result        $result
153
     */
154
    public function simulate(Target $target, Result $result)
155
    {
156
        $result->debug('sync backup to google drive' . PHP_EOL);
157
158
        $this->isSimulation = true;
159
        $this->simulateRemoteCleanup($target, $result);
160
    }
161
162
    /**
163
     * Create google api client.
164
     *
165
     * @return \Google_Client
166
     * @throws \Google_Exception
167
     */
168
    protected function createClient() : GClient
169
    {
170
        if (!$this->client) {
171
            $this->client = new GClient();
172
            $this->client->setApplicationName('phpbu');
173
            $this->client->setScopes(GDrive::DRIVE);
174
            $this->client->setAuthConfig($this->secret);
175
            $this->client->setAccessType('offline');
176
            $this->client->setAccessToken($this->getAccessToken());
177
178
            if ($this->client->isAccessTokenExpired()) {
179
                $this->client->fetchAccessTokenWithRefreshToken($this->client->getRefreshToken());
180
                $this->updateAccessToken($this->client->getAccessToken());
181
            }
182
        }
183
        return $this->client;
184
    }
185
186
    /**
187
     * Create google api file.
188
     *
189
     * @param  \phpbu\App\Backup\Target $target
190
     * @return \Google_Service_Drive_DriveFile
191
     */
192
    protected function createFile(Target $target) : GFile
193
    {
194
        $file = new GFile();
195
        $file->setName($target->getFilename());
196
        $file->setParents([$this->parent]);
197
198
        return $file;
199
    }
200
201
    /**
202
     * Create google api file deferred upload.
203
     *
204
     * @param  \Google_Client                     $client
205
     * @param  \Psr\Http\Message\RequestInterface $request
206
     * @param  \phpbu\App\Backup\Target           $target
207
     * @return \Google_Http_MediaFileUpload
208
     * @throws \phpbu\App\Exception
209
     */
210
    protected function createUploadStream(GClient $client, RequestInterface $request, Target $target) : GStream
211
    {
212
        $media = new GStream(
213
            $client,
214
            $request,
215
            'application/octet-stream',
216
            null,
217
            true,
218
            $this->chunkSize
219
        );
220
        $media->setFileSize($target->getSize());
221
222
        return $media;
223
    }
224
225
    /**
226
     * Return google credentials.
227
     *
228
     * @return array
229
     */
230
    private function getAccessToken() : array
231
    {
232
        return json_decode(file_get_contents($this->access), true);
233
    }
234
235
    /**
236
     * Update the access token in the google credentials file.
237
     *
238
     * @param  array $accessToken
239
     * @return void
240
     */
241
    private function updateAccessToken(array $accessToken)
242
    {
243
        file_put_contents($this->access, json_encode($accessToken));
244
    }
245
246
    /**
247
     * Creates collector for remote cleanup.
248
     *
249
     * @param  \phpbu\App\Backup\Target $target
250
     * @return \phpbu\App\Backup\Collector
251
     * @throws \Google_Exception
252
     */
253
    protected function createCollector(Target $target): Collector
254
    {
255
        return new Collector\GoogleDrive($target, new Path($this->parent), $this->createClient());
256
    }
257
}
258