PluginProbe
WP-Stateless – Google Cloud Storage / 3.0.1
WP-Stateless – Google Cloud Storage v3.0.1
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / lib / classes / class-gs-client.php

class-gs-client.php in WP-Stateless – Google Cloud Storage 3.0.1, at lib/classes/class-gs-client.php

512 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * GS API Client
4 *
5 * @since 0.2.0
6 * @author peshkov@UD
7 */
8 namespace wpCloud\StatelessMedia {
9
10 use wpCloud\StatelessMedia\Google_Client;
11 use Google_Service_Storage;
12 use WP_Error;
13 use Exception;
14 use Google_Service_Storage_ObjectAccessControl;
15 use Google_Auth_AssertionCredentials;
16
17 if( !class_exists( 'wpCloud\StatelessMedia\GS_Client' ) ) {
18
19 final class GS_Client {
20
21 /**
22 * Singleton object
23 *
24 * @var \wpCloud\StatelessMedia\GS_Client
25 */
26 private static $instance;
27
28 /**
29 * Google Client manager
30 *
31 * @var \wpCloud\StatelessMedia\Google_Client\Google_Client $client
32 */
33 public $client;
34
35 /**
36 * Google Storage Service manager
37 *
38 * @var \Google_Service_Storage $service
39 */
40 public $service;
41
42 /**
43 * Google Storage Bucket
44 *
45 * @var
46 */
47 private $bucket;
48
49 /**
50 * @var
51 */
52 private $temp_objects = array();
53
54 /**
55 * Constructor.
56 * Must not be called directly.
57 *
58 * @param $args
59 * @author peshkov@UD
60 */
61 protected function __construct( $args ) {
62 global $current_blog;
63 $this->bucket = $args[ 'bucket' ];
64 $this->key_json = json_decode($args['key_json'], 1);
65
66 // May be Loading Google SDK....
67 if( !class_exists( '\wpCloud\StatelessMedia\Google_Client\Google_Client' ) ) {
68 include_once( ud_get_stateless_media()->path('lib/Google/vendor/autoload.php', 'dir') );
69 }
70
71 /* Initialize our client */
72 $this->client = new \wpCloud\StatelessMedia\Google_Client\Google_Client();
73
74 // We're supporting Google SDK 1.X version since
75 // The plugins which also are using Google SDK may have its old version
76 // what may cause conflicts
77 //
78 if( version_compare( $this->client->getLibraryVersion(), '2.0', '<' ) ) {
79 // We should set the warning about potential issue
80 // If Google SDK has different version with already included
81 $this->_setWarning();
82
83 $wp_upload_dir = wp_upload_dir();
84 $dir = $wp_upload_dir[ 'path' ];
85 $filename = md5( wp_generate_password() ) . '.tmp';
86 $path = wp_normalize_path( $dir . '/' .$filename );
87 @file_put_contents( $path, json_encode( $this->key_json ) );
88 $cred = $this->client->loadServiceAccountJson( $path, ['https://www.googleapis.com/auth/devstorage.full_control'] );
89 $this->client->setAssertionCredentials($cred);
90 if ($this->client->getAuth()->isAccessTokenExpired()) {
91 $this->client->getAuth()->refreshTokenWithAssertion($cred);
92 }
93 @unlink( $path );
94 } else {
95 // May be delete warning transient if it was set
96 $this->_deleteWarning();
97 $this->client->setAuthConfig($this->key_json);
98 }
99
100 if( isset( $current_blog ) && isset( $current_blog->domain ) ) {
101 $this->client->setApplicationName( $current_blog->domain );
102 } else {
103 $this->client->setApplicationName( urlencode( str_replace( array( 'http://', 'https://' ), '', get_bloginfo( 'url' ) ) ) );
104 }
105
106 $this->client->setScopes(['https://www.googleapis.com/auth/devstorage.full_control']);
107
108 // May be Loading Google SDK. Because some bad plugins may load their Google SDK with not included Google_Service_Storage.
109 if( !class_exists( 'Google_Service_Storage' ) ) {
110 include_once( ud_get_stateless_media()->path('lib/Google/vendor/autoload.php', 'dir') );
111 }
112
113 /* Now, Initialize our Google Storage Service */
114 $this->service = new \wpCloud\StatelessMedia\Google_Client\Google_Service_Storage( $this->client );
115
116 }
117
118 /**
119 * Wrapper for listObjects()
120 */
121 public function list_objects( $options = array() ) {
122
123 $options = wp_parse_args( $options, array(
124 'delimiter' => '',
125 'maxResults' => 1000,
126 'pageToken' => '',
127 'prefix' => '',
128 'projection' => 'noAcl',
129 'versions' => false
130 ) );
131
132 return $this->service->objects->listObjects( $this->bucket, $options );
133 }
134
135 /**
136 * List all items page by page of maxResults
137 * @param $bucket
138 * @param array $options
139 * @return mixed
140 */
141 public function list_all_objects( $options = array() ) {
142
143 $options = wp_parse_args( $options, array(
144 'delimiter' => '',
145 'maxResults' => 1000,
146 'pageToken' => '',
147 'prefix' => '',
148 'projection' => 'noAcl',
149 'versions' => false
150 ) );
151
152 $response = $this->service->objects->listObjects( $this->bucket, $options );
153
154 $this->temp_objects = array_merge( $this->temp_objects, $response->getItems() );
155
156 if ( !empty( $response->nextPageToken ) ) {
157 $options['pageToken'] = $response->nextPageToken;
158 return $this->list_all_objects( $this->bucket, $options );
159 } else {
160 return $this->temp_objects;
161 }
162 }
163
164 /**
165 * Add/Update Media Object to Bucket
166 *
167 * https://stackoverflow.com/questions/26872851/resumable-uploading-to-google-cloud-storage-using-php-api
168 *
169 * @author peshkov@UD
170 * @param array $args
171 * @return bool
172 */
173 public function add_media( $args = array() ) {
174 try {
175
176 @set_time_limit( -1 );
177
178 $args = wp_parse_args( $args, array(
179 'use_root' => true,
180 'force' => false,
181 'name' => false,
182 'absolutePath' => false,
183 'mimeType' => 'image/jpeg',
184 'metadata' => array(),
185 'is_webp' => '',
186 ) );
187
188 /* Be sure file exists. */
189 if( !file_exists( $args['absolutePath'] ) ) {
190 return new \WP_Error( 'sm_error', __( 'Unable to locate file on disk', ud_get_stateless_media()->domain ) );
191 }
192
193 $use_wildcards = Utility::is_use_wildcards();
194
195 /* Set default name if parameter was not passed. */
196 if( empty( $name ) || $use_wildcards ) {
197 $name = basename( $args['name'] );
198 }
199
200 $object_id = isset( $args['metadata']['object-id'] ) ? $args['metadata']['object-id'] : (isset( $args['metadata']['child-of'] ) ? $args['metadata']['child-of'] : "");
201 $object_size = isset( $args['metadata']['size'] ) ? $args['metadata']['size'] : "";
202
203 $args['name'] = apply_filters( 'wp_stateless_file_name', $args['name'], $args['use_root'], $object_id, $object_size, $use_wildcards );
204 $args = apply_filters('wp_stateless_add_media_args', $args);
205 $name = $args['name'];
206
207 // If media exists we just return it
208 if ( !$args['force'] && $media = $this->media_exists( $name ) ) {
209 if($media->getCacheControl() != $args['cacheControl']){
210 $media->setCacheControl($args['cacheControl']);
211 $media = $this->service->objects->patch($this->bucket, $name, $media);
212 }
213 return get_object_vars( $media );
214 }
215
216 $media = new Google_Client\Google_Service_Storage_StorageObject();
217 $media->setName($name);
218 $media->setMetadata($args['metadata']);
219
220 if (isset($args['cacheControl'])) {
221 $media->setCacheControl($args['cacheControl']);
222 }
223
224 if (isset($args['contentEncoding'])) {
225 $media->setContentEncoding($args['contentEncoding']);
226 }
227
228 if (isset($args['contentDisposition'])) {
229 $media->getContentDisposition($args['contentDisposition']);
230 }
231
232 $file_size = filesize($args['absolutePath']);
233 $chunkSizeBytes = $this->get_chunk_size();
234
235 $this->client->setDefer(true);
236
237 $filetoupload = array('name' => $name, 'uploadType' => 'resumable');
238 $request = $this->service->objects->insert($this->bucket, $media, $filetoupload);
239 $uploader = new Google_Client\Google_Http_MediaFileUpload($this->client, $request, $args['mimeType'], null, true, $chunkSizeBytes);
240 $uploader->setFileSize($file_size);
241 $handle = fopen($args['absolutePath'], "rb");
242
243 $status = false;
244 while (!$status && !feof($handle)) {
245 $chunk = fread($handle, $chunkSizeBytes);
246 $status = $uploader->nextChunk($chunk);
247 }
248
249 $media = false;
250 if($status != false) {
251 $media = $status;
252 }
253
254 fclose($handle);
255 // Reset to the client to execute requests immediately in the future.
256 $this->client->setDefer(false);
257 $this->mediaInsertACL($name, $media, $args);
258 } catch( Exception $e ) {
259 return new WP_Error( 'sm_error', $e->getMessage() );
260 }
261 return get_object_vars( $media );
262 }
263
264 /**
265 *
266 */
267 public function get_chunk_size(){
268 if(defined('WP_STATELESS_MEDIA_UPLOAD_CHUNK_SIZE')){
269 return WP_STATELESS_MEDIA_UPLOAD_CHUNK_SIZE;
270 }
271 $memory_limit = ini_get('memory_limit');
272 if($memory_limit){
273 $memory_limit = Utility::convert_to_byte($memory_limit);
274 $memory_get_usage = memory_get_usage();
275 $free_memory = $memory_limit - $memory_get_usage;
276 }
277 else{
278 $free_memory = 10 * 1024 * 1024; // Default chunk size 10MB
279 }
280
281 return $free_memory * 0.8;
282 }
283
284 /**
285 *
286 *
287 */
288 public function mediaInsertACL($name, $media = array(), $agrs = array()){
289 /* Make Media Public READ for all on success */
290 if (!empty($name)) {
291 $acl = new \wpCloud\StatelessMedia\Google_Client\Google_Service_Storage_ObjectAccessControl();
292 $acl->setEntity('allUsers');
293 $acl->setRole('READER');
294 $acl = apply_filters( 'wp_stateless_media_acl', $acl, $name, $media, $agrs );
295 $this->service->objectAccessControls->insert($this->bucket, $name, $acl);
296 }
297 }
298
299 /**
300 * get or save media file
301 * @param $path
302 * @param bool $save
303 * @param bool $save_path
304 * @return bool|\Google_Service_Storage_StorageObject|int
305 */
306 public function get_media( $path, $save = false, $save_path = false ) {
307 try {
308 $media = $this->service->objects->get($this->bucket, $path);
309 } catch ( \Exception $e ) {
310 return false;
311 }
312
313 if ( empty( $media->id ) ) return false;
314
315 if ( $save && $save_path ) {
316 if ( !file_exists( $_dir = dirname( $save_path ) ) ) {
317 wp_mkdir_p( $_dir );
318 }
319 return $this->client->getHttpClient()->get($media->getMediaLink(), ['save_to' => $save_path] )->getStatusCode();
320 }
321
322 return $media;
323 }
324
325 /**
326 * get or save media file
327 * @param $path
328 * @param bool $save
329 * @param bool $save_path
330 * @return bool|\Google_Service_Storage_StorageObject|int
331 */
332 public function copy_media( $path, $new_path ) {
333 try {
334 $media = $this->service->objects->get($this->bucket, $path);
335 $media = $this->service->objects->copy($this->bucket, $path, $this->bucket, $new_path, $media);
336 $this->mediaInsertACL($new_path, $media);
337 } catch ( \Exception $e ) {
338 return false;
339 }
340
341 if ( empty( $media->id ) ) return false;
342
343 return $media;
344 }
345
346 /**
347 * get or save media file
348 * @param $path
349 * @param bool $save
350 * @param bool $save_path
351 * @return bool|\Google_Service_Storage_StorageObject|int
352 */
353 public function move_media( $path, $new_path ) {
354 try {
355 $media = $this->copy_media($path, $new_path);
356 $this->remove_media($path);
357 } catch ( \Exception $e ) {
358 return false;
359 }
360
361 if ( empty( $media->id ) ) return false;
362
363 return $media;
364 }
365
366 /**
367 * Check if media exists
368 * @param $path
369 * @return bool
370 */
371 public function media_exists( $path ) {
372 try {
373 $media = $this->service->objects->get($this->bucket, $path);
374 // Here we wanted to check if access allowed, but noticed it actually sets this ACL... Leaving it as is. @author korotkov@ud
375 $this->service->objectAccessControls->get($this->bucket, $path, 'allUsers');
376 } catch ( \Exception $e ) {
377 return false;
378 }
379
380 if ( empty( $media->id ) ) return false;
381 return $media;
382 }
383
384 /**
385 * Fired for every file remove action
386 *
387 * @author peshkov@UD
388 * @param string $name
389 * @param string $id
390 * @param boolean $use_root
391 * @param string $size
392 * @param boolean $is_webp
393 * @return bool
394 */
395 public function remove_media( $name, $id = "", $use_root = true, $size = "", $is_webp = false ) {
396 try {
397 $name = apply_filters( 'wp_stateless_file_name', $name, $use_root, $id, $size, false );
398 if ( $is_webp && substr( $name, -4) != "webp") $name .= ".webp";
399
400 $this->service->objects->delete( $this->bucket, $name );
401 } catch( Exception $e ) {
402 return new WP_Error( 'sm_error', $e->getMessage() );
403 }
404 return true;
405 }
406
407 /**
408 * Tests connection to Google Storage
409 * by trying to get passed bucket's data.
410 *
411 * @author peshkov@UD
412 */
413 public function is_connected() {
414 try {
415 $this->service->buckets->get( $this->bucket );
416 } catch( Exception $e ) {
417 return $e;
418 }
419 return true;
420 }
421
422 /**
423 * Determine if instance already exists and Return Instance
424 *
425 * @param array $args
426 *
427 * $args
428 * @param string client_id
429 * @param string service_account_name
430 * @param string key_file_path
431 *
432 * @author peshkov@UD
433 * @return \wpCloud\StatelessMedia\GS_Client
434 */
435 public static function get_instance( $args ) {
436 if( null === self::$instance ) {
437
438 try {
439
440 if( empty( $args[ 'bucket' ] ) ) {
441 throw new Exception( __( '<b>Bucket</b> parameter must be provided.' ) );
442 }
443
444 $json = "{}";
445
446 if ( !empty( $args[ 'key_json' ] ) ) {
447 $json = json_decode($args['key_json']);
448 }
449
450 if( !$json || !property_exists($json, 'private_key') ){
451 throw new Exception( __( '<b>Service Account JSON</b> is invalid.' ) );
452 }
453
454 self::$instance = new self( $args );
455 } catch( Exception $e ) {
456 return new WP_Error( 'sm_error', $e->getMessage() );
457 }
458 }
459 return self::$instance;
460 }
461
462 /**
463 * Set warning about potential conflict with Google SDK
464 *
465 * @since 2.0.1
466 */
467 private function _setWarning() {
468
469 $reflector = new \ReflectionClass('Google_Client');
470 $pluginBasename = wp_normalize_path( plugin_basename( $reflector->getFileName() ) );
471
472 // Check if get_plugins() function exists. This is required on the front end of the
473 // site, since it is in a file that is normally only loaded in the admin.
474 if ( ! function_exists( 'get_plugins' ) ) {
475 require_once ABSPATH . 'wp-admin/includes/plugin.php';
476 }
477
478 $pluginBasenameParts = explode( '/', $pluginBasename );
479 $pluginName = __( "UNDEFINED", ud_get_stateless_media()->domain );
480
481 foreach( get_plugins() as $path => $meta ) {
482 if( strpos( $path, trailingslashit( $pluginBasenameParts[0] ) ) === 0 ) {
483 $pluginName = $meta['Name'];
484 }
485 };
486
487 $error = sprintf( __( "%s plugin may have potential Google SDK version conflicts with %s plugin. %s is using Google SDK %s, when %s loads old Google SDK version %s.", ud_get_stateless_media()->domain ),
488 "<b>" . 'WP-Stateless' . "</b>",
489 "<b>" . $pluginName . "</b>",
490 'WP-Stateless',
491 "<b>v2.0</b>",
492 $pluginName,
493 "<b>v" . \wpCloud\StatelessMedia\Google_Client\Google_Client::LIBVER . "</b>"
494 );
495
496 set_transient( "wp_stateless_google_sdk_conflict", $error );
497 }
498
499 /**
500 * Removes Warning if it exists
501 *
502 */
503 private function _deleteWarning() {
504 delete_transient( "wp_stateless_google_sdk_conflict" );
505 }
506
507 }
508
509 }
510
511 }
512