From d9cca50de861a6f937e6dd804d08028a28db571e Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Sep 2016 13:50:28 +0200 Subject: [PATCH 01/19] --- .gitignore | 3 + composer.json | 3 +- doc/adapters/google-cloud-client-storage.md | 52 ++++ .../Adapter/GoogleCloudClientStorage.php | 283 ++++++++++++++++++ src/Gaufrette/Adapter/ResourceSupporter.php | 22 ++ .../Adapter/GoogleCloudClientStorageTest.php | 92 ++++++ .../GoogleCloudClientStorage.php.dist | 19 ++ 7 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 doc/adapters/google-cloud-client-storage.md create mode 100644 src/Gaufrette/Adapter/GoogleCloudClientStorage.php create mode 100644 src/Gaufrette/Adapter/ResourceSupporter.php create mode 100644 tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php create mode 100644 tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist diff --git a/.gitignore b/.gitignore index 81c6ed309..ab721fa47 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ tests/adapters/* bin/* !bin/configure_test_env.sh doc/.couscous +/nbproject/private/ +nbproject/* +tests/Gaufrette/Functional/adapters/* \ No newline at end of file diff --git a/composer.json b/composer.json index 16f8f000c..8a0e1c44d 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ } ], "require": { - "php": ">=5.4" + "php": ">=5.4", + "google/cloud": "^0.7.1" }, "require-dev": { "aws/aws-sdk-php": "~2", diff --git a/doc/adapters/google-cloud-client-storage.md b/doc/adapters/google-cloud-client-storage.md new file mode 100644 index 000000000..a8dac9ace --- /dev/null +++ b/doc/adapters/google-cloud-client-storage.md @@ -0,0 +1,52 @@ +--- +currentMenu: google-cloud-client-storage +--- + +# Google Cloud Client Storage + +This adapter requires an instance of Google\Cloud\Storage\StorageClient that has proper access rights to the bucket you want to use. + +For more details see: +http://googlecloudplatform.github.io/google-cloud-php/ +https://console.cloud.google.com/ + +## Example + +```php + 'your-project-id', + 'keyFilePath' => 'path/to/your/project/key.json' +)); + +# you can optionally set the directory in the bucket and the acl permissions for all uploaded files... +# by default the uploaded files are read/write by the owner only +# the example below gives read access to the uploaded files to anyone in the world + +$adapter = new GoogleCloudClientStorage($storage, 'bucket_name', + array( + 'directory' => 'bucket_directory', + 'acl' => array( + 'allUsers' => \Google\Cloud\Storage\Acl::ROLE_READER + ) + ) +); + +$key = 'myAmazingFile.txt'; + +# optional +$adapter->setMetadata($key, + array( + 'FileDescription' => 'This is my file. There are many like it, but this one is mine.' + ) +); + +$filesystem = new Filesystem($adapter); + +$filesystem->write($key, 'Uploaded at: '.date('Y-m-d @ H:i:s'), true); + +``` \ No newline at end of file diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php new file mode 100644 index 000000000..19d50ba18 --- /dev/null +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -0,0 +1,283 @@ + + */ +class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourceSupporter, ListKeysAware { + + protected $storageClient; + protected $bucket; + protected $bucketValidated; + protected $options = array(); + protected $metadata = array(); + protected $resource = array(); + + /** + * @param Google\Cloud\Storage\StorageClient $service Authenticated storage client class + * @param string $bucketName Name of the bucket + * @param array $options Options are: "directory" and "acl" (see https://cloud.google.com/storage/docs/access-control/lists) + */ + public function __construct(\Google\Cloud\Storage\StorageClient $storageClient, $bucketName, $options = array()) + { + $this->storageClient = $storageClient; + $this->setBucket($bucketName); + $this->options = array_replace_recursive( + array( + 'directory' => '', + 'acl' => array() + ), + $options + ); + } + + /** + * Get adapter options + * + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Set adapter options + * + * @param array $options + */ + public function setOptions($options) + { + $this->options = array_replace($this->options, $options); + } + + protected function computePath($key) + { + if (strlen($this->options['directory'])) + { + if (strcmp(substr($this->options['directory'], -1), '/') == 0) + { + return $this->options['directory'].$key; + } + return $this->options['directory'].'/'.$key; + } + return $key; + } + + protected function isBucket() + { + if ($this->bucketValidated === true) + { + return true; + } elseif (!$this->bucket->exists()) { + throw new \RuntimeException(sprintf('Bucket %s does not exist.', $this->bucket->name())); + } + $this->bucketValidated = true; + return true; + } + + public function setBucket($name) + { + $this->bucketValidated = null; + $this->bucket = $this->storageClient->bucket($name); + $this->isBucket(); + } + + public function getBucket() + { + return $this->bucket; + } + + /** + * {@inheritdoc} + */ + public function read($key) + { + $this->isBucket(); + $object = $this->bucket->object($this->computePath($key)); + $info = $object->info(); + $this->setResource($key, $info); + return $object->downloadAsString(); + } + + /** + * {@inheritdoc} + */ + public function write($key, $content) + { + $this->isBucket(); + + $options = array( + 'resumable' => true, + 'name' => $this->computePath($key), + 'metadata' => $this->getMetadata($key), + ); + + $object = $this->bucket->upload( + $content, + $options + ); + + $acl = $object->acl(); + foreach ($this->options['acl'] as $k => $v) + { + $acl->add($k, $v); + } + $object->update(array('metadata' => $this->getMetadata($key))); + + $info = $object->info(); + $this->setResource($key, $info); + return $info['size']; + } + + /** + * {@inheritdoc} + */ + public function exists($key) + { + $this->isBucket(); + $object = $this->bucket->object($this->computePath($key)); + if ($object->exists()) + { + return true; + } + return false; + } + + /** + * {@inheritdoc} + */ + public function isDirectory($key) + { + if ($this->exists($key . '/')) + { + return true; + } + return false; + } + + /** + * {@inheritdoc} + */ + public function listKeys($prefix = null) + { + $this->isBucket(); + $keys = array(); + if ($prefix === null) + { + $prefix = $this->options['directory']; + } else { + $prefix = $this->computePath($prefix); + } + foreach ($this->bucket->objects(array('prefix' => $prefix)) as $e) + { + $keys[] = $e->name(); + } + sort($keys); + return $keys; + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return $this->listKeys(); + } + + /** + * {@inheritdoc} + */ + public function mtime($key) + { + $this->isBucket(); + $object = $object = $this->bucket->object($this->computePath($key)); + $info = $object->info(); + $this->setResource($key, $info); + return strtotime($info['updated']); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $object = $this->bucket->object($this->computePath($key)); + $object->delete(); + return true; + } + + /** + * {@inheritdoc} + */ + public function rename($sourceKey, $targetKey) + { + # there is no support for rename currently in Google Cloud Client Library 0.7.1 - it will be added in v0.8 + return false; + /* + $metadata = $this->getMetadata($sourceKey); + + $sourceKey = $this->computePath($sourceKey); + $targetKey = $this->computePath($targetKey); + + $object = $this->bucket->object($sourceKey); + // try { + $info = $object->copy($sourceKey,$targetKey); + + var_dump($info); + // } catch (\Google\Cloud\Exception\GoogleException $e) { + return false; + // } + return true;*/ + } + + /** + * {@inheritdoc} + */ + public function setMetadata($key, $metadata) + { + $this->metadata[$key] = $metadata; + } + + /** + * {@inheritdoc} + */ + public function getMetadata($key) + { + return isset($this->metadata[$key]) ? $this->metadata[$key] : array(); + } + + /** + * {@inheritdoc} + */ + public function setResource($key, $data) + { + $this->resource[$key] = $data; + } + + /** + * {@inheritdoc} + */ + public function getResource($key, $name = null) + { + if (isset($this->resource[$key])) + { + if ($name) + { + return isset($this->resource[$key][$name]) ? $this->resource[$key][$name] : null; + } elseif (isset($this->resource[$key])) { + return isset($this->resource[$key]) ? $this->resource[$key] : array(); + } + } + return array(); + } +} \ No newline at end of file diff --git a/src/Gaufrette/Adapter/ResourceSupporter.php b/src/Gaufrette/Adapter/ResourceSupporter.php new file mode 100644 index 000000000..6cb6dc53a --- /dev/null +++ b/src/Gaufrette/Adapter/ResourceSupporter.php @@ -0,0 +1,22 @@ + + */ +interface ResourceSupporter +{ + /** + * @param string $key + * @param array $data + */ + public function setResource($key, $data); + + /** + * @param string $key + * @return array|string An array of values or single value. + */ + public function getResource($key, $name = null); +} \ No newline at end of file diff --git a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php new file mode 100644 index 000000000..cf874982b --- /dev/null +++ b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php @@ -0,0 +1,92 @@ + + */ +class GoogleCloudClientStorageTest extends FunctionalTestCase +{ + private $string = 'Yeah mate. No worries, I uploaded just fine. Meow!'; + + /** + * @test + * @group functional + * @group gccs + * + * @expectedException \RuntimeException + */ + public function shouldFailIfBucketIsNotAccessible() + { + /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ + $adapter = $this->filesystem->getAdapter(); + $options = $adapter->getOptions(); + $adapter->setBucket('meow_'.mt_rand()); + $adapter->setOptions($options); + } + + /** + * @test + * @group functional + * @group gccs + */ + public function shouldListBucketContent() + { + /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ + $adapter = $this->filesystem->getAdapter(); + $options = $adapter->getOptions(); + + $adapter->setOptions(array('directory' => 'Phat')); + $this->assertEquals(strlen($this->string), $this->filesystem->write('Cat.txt', $this->string, true)); + $keys = $this->filesystem->keys(); + $this->assertEquals($keys[0], 'Phat/Cat.txt'); + $this->filesystem->delete('Cat.txt'); + $adapter->setOptions($options); + } + + /** + * @test + * @group functional + * @group gccs + */ + public function shouldWriteAndReadFile() + { + /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ + $adapter = $this->filesystem->getAdapter(); + $options = $adapter->getOptions(); + $adapter->setOptions(array('directory' => 'Phat')); + $this->assertEquals(strlen($this->string), $this->filesystem->write('Cat.txt', $this->string, true)); + $this->assertEquals(strlen($this->string), $this->filesystem->write('Phatter/Cat.txt', $this->string, true)); + + $this->assertEquals($this->string, $this->filesystem->read('Cat.txt')); + $this->assertEquals($this->string, $this->filesystem->read('Phatter/Cat.txt')); + + $this->filesystem->delete('Cat.txt'); + $this->filesystem->delete('Phatter/Cat.txt'); + $adapter->setOptions($options); + } + + /** + * @test + * @group functional + * @group gccs + */ + public function shouldWriteAndReadFileMetadata() + { + /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ + $adapter = $this->filesystem->getAdapter(); + $options = $adapter->getOptions(); + $file = 'PhatCat/Cat.txt'; + $adapter->setMetadata($file, array('OhMy' => 'I am a cat file!')); + $this->assertEquals(strlen($this->string), $this->filesystem->write($file, $this->string, true)); + $info = $adapter->getMetadata($file); + $this->assertEquals($info['OhMy'], 'I am a cat file!'); + $this->filesystem->delete($file); + $adapter->setOptions($options); + } +} \ No newline at end of file diff --git a/tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist b/tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist new file mode 100644 index 000000000..006c283dc --- /dev/null +++ b/tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist @@ -0,0 +1,19 @@ + 'your-project-id', + 'keyFilePath' => 'path/to/your/project/key.json' +)); + +$bucket_name = 'bucket_name'; + +$adapter = new Gaufrette\Adapter\GoogleCloudClientStorage($storage, $bucket_name, + array( + 'directory' => '', + 'acl' => array( + 'allUsers' => \Google\Cloud\Storage\Acl::ROLE_READER + ) + ) +); + +return $adapter; \ No newline at end of file From f6520feaf65d67ee7133291d48a37e2e07ad5ac8 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Sep 2016 13:55:01 +0200 Subject: [PATCH 02/19] --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 8a0e1c44d..5091c4c31 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,7 @@ } ], "require": { - "php": ">=5.4", - "google/cloud": "^0.7.1" + "php": ">=5.4" }, "require-dev": { "aws/aws-sdk-php": "~2", @@ -39,7 +38,8 @@ "phpunit/phpunit": "3.7.*", "microsoft/windowsazure": "dev-master", "mikey179/vfsStream": "~1.2.0", - "league/flysystem": "~1.0" + "league/flysystem": "~1.0", + "google/cloud": "^0.7.1" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", From d7ec673067d498aeb66e874507df6e065ec30684 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Sep 2016 13:59:52 +0200 Subject: [PATCH 03/19] --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ab721fa47..148272177 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ bin/* doc/.couscous /nbproject/private/ nbproject/* -tests/Gaufrette/Functional/adapters/* \ No newline at end of file +tests/Gaufrette/Functional/adapters/*.php \ No newline at end of file From 8060ad657043a1667d9d330725e4d60ac34dd745 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Sep 2016 14:02:01 +0200 Subject: [PATCH 04/19] --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 148272177..e5063836a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,5 @@ tests/adapters/* bin/* !bin/configure_test_env.sh doc/.couscous -/nbproject/private/ nbproject/* tests/Gaufrette/Functional/adapters/*.php \ No newline at end of file From 759b856f69efa2de50b63304fb0763b15fdb8442 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Sep 2016 14:02:44 +0200 Subject: [PATCH 05/19] --- .../Adapter/GoogleCloudClientStorage.php | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 19d50ba18..775b3543a 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -221,23 +221,8 @@ public function delete($key) */ public function rename($sourceKey, $targetKey) { - # there is no support for rename currently in Google Cloud Client Library 0.7.1 - it will be added in v0.8 + # there is no support for rename currently in Google Cloud Client Library 0.7.1 - it will be added in v0.8 any time now return false; - /* - $metadata = $this->getMetadata($sourceKey); - - $sourceKey = $this->computePath($sourceKey); - $targetKey = $this->computePath($targetKey); - - $object = $this->bucket->object($sourceKey); - // try { - $info = $object->copy($sourceKey,$targetKey); - - var_dump($info); - // } catch (\Google\Cloud\Exception\GoogleException $e) { - return false; - // } - return true;*/ } /** From c0de2c7110064938ac9a6348e7c848c271674ec4 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Sat, 10 Sep 2016 12:44:23 +0200 Subject: [PATCH 06/19] - added functional "rename" (now supported by Google Cloud Client Library 0.8.0) - general cleanup - added extra unit tests --- composer.json | 2 +- .../Adapter/GoogleCloudClientStorage.php | 126 +++++++++++++----- src/Gaufrette/Adapter/ResourceSupporter.php | 22 --- src/Gaufrette/Adapter/ResourcesSupporter.php | 28 ++++ .../Adapter/GoogleCloudClientStorageTest.php | 43 +++++- 5 files changed, 166 insertions(+), 55 deletions(-) delete mode 100644 src/Gaufrette/Adapter/ResourceSupporter.php create mode 100644 src/Gaufrette/Adapter/ResourcesSupporter.php diff --git a/composer.json b/composer.json index 5091c4c31..335559563 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "microsoft/windowsazure": "dev-master", "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", - "google/cloud": "^0.7.1" + "google/cloud": "^0.8.0" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 775b3543a..27826db32 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -4,6 +4,7 @@ use Gaufrette\Adapter; use Gaufrette\Adapter\MetadataSupporter; +use Gaufrette\Adapter\ResourcesSupporter; use Gaufrette\Adapter\ListKeysAware; /** @@ -13,14 +14,14 @@ * @package Gaufrette * @author Lech Buszczynski */ -class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourceSupporter, ListKeysAware { +class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware { protected $storageClient; protected $bucket; protected $bucketValidated; - protected $options = array(); - protected $metadata = array(); - protected $resource = array(); + protected $options = array(); + protected $metadata = array(); + protected $resources = array(); /** * @param Google\Cloud\Storage\StorageClient $service Authenticated storage client class @@ -105,7 +106,7 @@ public function read($key) $this->isBucket(); $object = $this->bucket->object($this->computePath($key)); $info = $object->info(); - $this->setResource($key, $info); + $this->setResources($key, $info); return $object->downloadAsString(); } @@ -122,21 +123,21 @@ public function write($key, $content) 'metadata' => $this->getMetadata($key), ); - $object = $this->bucket->upload( + $this->bucket->upload( $content, $options ); - - $acl = $object->acl(); - foreach ($this->options['acl'] as $k => $v) - { - $acl->add($k, $v); - } - $object->update(array('metadata' => $this->getMetadata($key))); - $info = $object->info(); - $this->setResource($key, $info); - return $info['size']; + $this->updateKeyProperties($key, + array( + 'acl' => $this->options['acl'], + 'metadata' => $this->getMetadata($key) + ) + ); + + $size = $this->getResourceByName($key, 'size'); + + return $size === null ? false : $size; } /** @@ -202,7 +203,7 @@ public function mtime($key) $this->isBucket(); $object = $object = $this->bucket->object($this->computePath($key)); $info = $object->info(); - $this->setResource($key, $info); + $this->setResources($key, $info); return strtotime($info['updated']); } @@ -211,6 +212,7 @@ public function mtime($key) */ public function delete($key) { + $this->isBucket(); $object = $this->bucket->object($this->computePath($key)); $object->delete(); return true; @@ -221,7 +223,34 @@ public function delete($key) */ public function rename($sourceKey, $targetKey) { - # there is no support for rename currently in Google Cloud Client Library 0.7.1 - it will be added in v0.8 any time now + $this->isBucket(); + + $metadata = $this->getMetadata($sourceKey); + + $sourceKey = $this->computePath($sourceKey); + $targetKey = $this->computePath($targetKey); + + $object = $this->bucket->object($sourceKey); + + $copiedObject = $object->copy($this->bucket, + array( + 'name' => $targetKey + ) + ); + + if ($copiedObject->exists()) + { + $this->updateKeyProperties($targetKey, + array( + 'acl' => $this->options['acl'], + 'metadata' => $this->getMetadata($sourceKey) + ) + ); + $this->setMetadata($targetKey, $this->getMetadata($sourceKey)); + $this->setMetadata($sourceKey, null); + $object->delete(); + return true; + } return false; } @@ -244,25 +273,60 @@ public function getMetadata($key) /** * {@inheritdoc} */ - public function setResource($key, $data) + public function setResources($key, $data) + { + $this->resources[$key] = $data; + } + + /** + * {@inheritdoc} + */ + public function getResources($key) { - $this->resource[$key] = $data; + return isset($this->resources[$key]) ? $this->resources[$key] : array(); } /** * {@inheritdoc} */ - public function getResource($key, $name = null) + public function getResourceByName($key, $resourceName) { - if (isset($this->resource[$key])) + return isset($this->resources[$key][$resourceName]) ? $this->resources[$key][$resourceName] : null; + } + + /** + * Sets ACL and metadata information. + * + * @param string $key + * @param array $options Can contain "acl" and/or "metadata" arrays. + * @return boolean + */ + protected function updateKeyProperties($key, $options = array()) + { + if ($this->exists($key) === false) { - if ($name) - { - return isset($this->resource[$key][$name]) ? $this->resource[$key][$name] : null; - } elseif (isset($this->resource[$key])) { - return isset($this->resource[$key]) ? $this->resource[$key] : array(); - } - } - return array(); - } + return false; + } + + $object = $this->bucket->object($this->computePath($key)); + + $properties = array_replace_recursive( + array( + 'acl' => array(), + 'metadata' => array() + ), $options + ); + + $acl = $object->acl(); + foreach ($properties['acl'] as $k => $v) + { + $acl->add($k, $v); + } + $object->update(array('metadata' => $properties['metadata'])); + + $info = $object->info(); + + $this->setResources($key, $info); + return true; + } } \ No newline at end of file diff --git a/src/Gaufrette/Adapter/ResourceSupporter.php b/src/Gaufrette/Adapter/ResourceSupporter.php deleted file mode 100644 index 6cb6dc53a..000000000 --- a/src/Gaufrette/Adapter/ResourceSupporter.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ -interface ResourceSupporter -{ - /** - * @param string $key - * @param array $data - */ - public function setResource($key, $data); - - /** - * @param string $key - * @return array|string An array of values or single value. - */ - public function getResource($key, $name = null); -} \ No newline at end of file diff --git a/src/Gaufrette/Adapter/ResourcesSupporter.php b/src/Gaufrette/Adapter/ResourcesSupporter.php new file mode 100644 index 000000000..a3b5e8dd5 --- /dev/null +++ b/src/Gaufrette/Adapter/ResourcesSupporter.php @@ -0,0 +1,28 @@ + + */ +interface ResourcesSupporter +{ + /** + * @param string $key + * @param array $data + */ + public function setResources($key, $data); + + /** + * @param string $key + * @return array Array of resource values. + */ + public function getResources($key); + + /** + * @param string $key + * @return array|string|null Array or string depending on resource structure. Null if resource does not exist. + */ + public function getResourceByName($key, $resourceName); +} \ No newline at end of file diff --git a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php index cf874982b..e2610b3e5 100644 --- a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php +++ b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php @@ -88,5 +88,46 @@ public function shouldWriteAndReadFileMetadata() $this->assertEquals($info['OhMy'], 'I am a cat file!'); $this->filesystem->delete($file); $adapter->setOptions($options); - } + } + + /** + * @test + * @group functional + * @group gccs + */ + public function shouldWriteAndRenameFile() + { + /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ + $adapter = $this->filesystem->getAdapter(); + $options = $adapter->getOptions(); + $file = 'Cat.txt'; + $adapter->setMetadata($file, array('OhMy' => 'I am a cat file!')); + $this->assertEquals(strlen($this->string), $this->filesystem->write($file, $this->string, true)); + $adapter->rename('Cat.txt', 'Kitten.txt'); + $this->assertEquals($adapter->getMetadata('Kitten.txt'), $adapter->getResourceByName('Kitten.txt', 'metadata')); + $this->filesystem->delete('Kitten.txt'); + $adapter->setOptions($options); + } + + /** + * @test + * @group functional + * @group gccs + */ + public function shouldWriteAndReadPublicFile() + { + /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ + $adapter = $this->filesystem->getAdapter(); + $options = $adapter->getOptions(); + $file = 'Cat.txt'; + $this->assertEquals(strlen($this->string), $this->filesystem->write($file, $this->string, true)); + + $public_link = sprintf('https://storage.googleapis.com/%s/Cat.txt', $adapter->getBucket()->name()); + + $headers = @get_headers($public_link); + $this->assertEquals($headers[0], 'HTTP/1.0 200 OK'); + $this->filesystem->delete('Cat.txt'); + $adapter->setOptions($options); + } + } \ No newline at end of file From d1e31ef30342c1499e0522865bb3dbab12128e0b Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Sat, 10 Sep 2016 12:47:52 +0200 Subject: [PATCH 07/19] - added functional "rename" (now supported by Google Cloud Client Library 0.8.0) - general cleanup - added extra unit tests --- src/Gaufrette/Adapter/GoogleCloudClientStorage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 27826db32..42d0ba4e5 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -215,6 +215,7 @@ public function delete($key) $this->isBucket(); $object = $this->bucket->object($this->computePath($key)); $object->delete(); + $this->setMetadata($key, null); return true; } From 342b0154da74e03a7dff52d483d06fd1d39251a2 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Thu, 2 Feb 2017 13:42:52 +0100 Subject: [PATCH 08/19] --- composer.json | 2 +- .../Adapter/GoogleCloudClientStorage.php | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 335559563..39b9b39b2 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "microsoft/windowsazure": "dev-master", "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", - "google/cloud": "^0.8.0" + "google/cloud": ">=0.20.1" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 42d0ba4e5..628cb4229 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -1,5 +1,4 @@ */ -class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware { - +class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware +{ protected $storageClient; protected $bucket; protected $bucketValidated; @@ -267,7 +266,15 @@ public function setMetadata($key, $metadata) * {@inheritdoc} */ public function getMetadata($key) - { + { + if (!isset($this->metadata[$key]) && $this->exists($key)) + { + $data = $this->bucket->object($key)->info(); + if (isset($data['metadata'])) + { + $this->metadata[$key] = $data['metadata']; + } + } return isset($this->metadata[$key]) ? $this->metadata[$key] : array(); } From 924a41ff7420404e1ddc76a43391a0a2c71fcd32 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Thu, 2 Feb 2017 13:42:52 +0100 Subject: [PATCH 09/19] - fix for reading metadata from GCP files --- composer.json | 2 +- .../Adapter/GoogleCloudClientStorage.php | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 335559563..39b9b39b2 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "microsoft/windowsazure": "dev-master", "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", - "google/cloud": "^0.8.0" + "google/cloud": ">=0.20.1" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 42d0ba4e5..628cb4229 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -1,5 +1,4 @@ */ -class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware { - +class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware +{ protected $storageClient; protected $bucket; protected $bucketValidated; @@ -267,7 +266,15 @@ public function setMetadata($key, $metadata) * {@inheritdoc} */ public function getMetadata($key) - { + { + if (!isset($this->metadata[$key]) && $this->exists($key)) + { + $data = $this->bucket->object($key)->info(); + if (isset($data['metadata'])) + { + $this->metadata[$key] = $data['metadata']; + } + } return isset($this->metadata[$key]) ? $this->metadata[$key] : array(); } From 294abce25c59f90a60959b7695ed997ad0992bdd Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Thu, 2 Feb 2017 14:10:22 +0100 Subject: [PATCH 10/19] - fix for reading metadata from GCP files --- src/Gaufrette/Adapter/GoogleCloudClientStorage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 628cb4229..76b155881 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -337,4 +337,4 @@ protected function updateKeyProperties($key, $options = array()) $this->setResources($key, $info); return true; } -} \ No newline at end of file +} From 30fd12e30594f03d584baf3c0016e1f6289ceb2f Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 31 Mar 2017 11:56:19 +0200 Subject: [PATCH 11/19] Merge origin/master --- composer.json | 5 +++-- doc/adapters/google-cloud-storage.md | 2 ++ .../Adapter/GoogleCloudClientStorageTest.php | 13 ++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 39b9b39b2..9e2c0670a 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "microsoft/windowsazure": "dev-master", "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", - "google/cloud": ">=0.20.1" + "google/cloud-storage": "dev-master" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", @@ -58,7 +58,8 @@ "ext-curl": "*", "ext-mbstring": "*", "ext-mongo": "*", - "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters" + "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters", + "google/cloud-storage": "to use Google Cloud Client Library" }, "autoload": { "psr-0": { "Gaufrette": "src/" } diff --git a/doc/adapters/google-cloud-storage.md b/doc/adapters/google-cloud-storage.md index 7f1f219ce..6203b2080 100644 --- a/doc/adapters/google-cloud-storage.md +++ b/doc/adapters/google-cloud-storage.md @@ -9,6 +9,8 @@ To use the GoogleCloudStorage adapter you will need to create a connection using (https://console.developers.google.com/). You can then create the `\Google_Service_Storage` which is required for the GoogleCloudStorage adapter. +Install with: composer require google/cloud-storage + ## Example ```php diff --git a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php index e2610b3e5..4492b56f9 100644 --- a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php +++ b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php @@ -41,11 +41,10 @@ public function shouldListBucketContent() $adapter = $this->filesystem->getAdapter(); $options = $adapter->getOptions(); - $adapter->setOptions(array('directory' => 'Phat')); - $this->assertEquals(strlen($this->string), $this->filesystem->write('Cat.txt', $this->string, true)); + $this->assertEquals(strlen($this->string), $this->filesystem->write('Phat/Cat.txt', $this->string, true)); $keys = $this->filesystem->keys(); $this->assertEquals($keys[0], 'Phat/Cat.txt'); - $this->filesystem->delete('Cat.txt'); + $this->filesystem->delete('Phat/Cat.txt'); $adapter->setOptions($options); } @@ -59,14 +58,14 @@ public function shouldWriteAndReadFile() /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ $adapter = $this->filesystem->getAdapter(); $options = $adapter->getOptions(); - $adapter->setOptions(array('directory' => 'Phat')); - $this->assertEquals(strlen($this->string), $this->filesystem->write('Cat.txt', $this->string, true)); + //$adapter->setOptions(array('directory' => 'Phat')); + $this->assertEquals(strlen($this->string), $this->filesystem->write('Phat/Cat.txt', $this->string, true)); $this->assertEquals(strlen($this->string), $this->filesystem->write('Phatter/Cat.txt', $this->string, true)); - $this->assertEquals($this->string, $this->filesystem->read('Cat.txt')); + $this->assertEquals($this->string, $this->filesystem->read('Phat/Cat.txt')); $this->assertEquals($this->string, $this->filesystem->read('Phatter/Cat.txt')); - $this->filesystem->delete('Cat.txt'); + $this->filesystem->delete('Phat/Cat.txt'); $this->filesystem->delete('Phatter/Cat.txt'); $adapter->setOptions($options); } From e4bec1b2f9e7c245ae86290139f0eeb0975189ab Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 31 Mar 2017 12:26:01 +0200 Subject: [PATCH 12/19] Merge origin/master --- .gitignore | 15 ++++++++------- composer.json | 19 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index e5063836a..62261505b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,11 @@ +bin/* +!bin/tests +!bin/tests-all +doc/.couscous +tests/adapters/* +!tests/adapters/*.dist vendor/ +.env composer.lock phpunit.xml -tests/adapters/* -!tests/adapters/*.dist -bin/* -!bin/configure_test_env.sh -doc/.couscous -nbproject/* -tests/Gaufrette/Functional/adapters/*.php \ No newline at end of file +nbproject/* \ No newline at end of file diff --git a/composer.json b/composer.json index 9e2c0670a..04f4c645c 100644 --- a/composer.json +++ b/composer.json @@ -19,16 +19,19 @@ "repositories": [ { "type": "pear", - "url": "http://pear.php.net" + "url": "https://pear.php.net" } ], "require": { "php": ">=5.4" }, + "conflict": { + "microsoft/windowsazure": "<0.4.3" + }, "require-dev": { - "aws/aws-sdk-php": "~2", + "aws/aws-sdk-php": "^2.4.12||~3", "amazonwebservices/aws-sdk-for-php": "1.5.*", - "rackspace/php-opencloud" : "1.9.*", + "rackspace/php-opencloud" : "^1.9.2", "google/apiclient": "~1.1.3", "phpspec/phpspec": "~2.4", "phpseclib/phpseclib": "^2.0", @@ -36,9 +39,9 @@ "dropbox-php/dropbox-php": "*", "herzult/php-ssh": "*", "phpunit/phpunit": "3.7.*", - "microsoft/windowsazure": "dev-master", "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", + "mongodb/mongodb": "~1.1.4", "google/cloud-storage": "dev-master" }, "suggest": { @@ -57,8 +60,9 @@ "ext-apc": "to use the APC adapter", "ext-curl": "*", "ext-mbstring": "*", - "ext-mongo": "*", + "ext-mongodb": "*", "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters", + "mongodb/mongodb": "*", "google/cloud-storage": "to use Google Cloud Client Library" }, "autoload": { @@ -68,8 +72,5 @@ "branch-alias": { "dev-master": "0.4.x-dev" } - }, - "config": { - "bin-dir": "bin" } -} +} \ No newline at end of file From 743d1ed59f9060c1076f35353b100c684fa91449 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 31 Mar 2017 12:26:30 +0200 Subject: [PATCH 13/19] Merge origin/master --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 62261505b..1d480e39d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,4 @@ tests/adapters/* vendor/ .env composer.lock -phpunit.xml -nbproject/* \ No newline at end of file +phpunit.xml \ No newline at end of file From a12adfa972869139a3cf3d0189f4f78cedd0f2e1 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 2 Jun 2017 13:25:36 +0200 Subject: [PATCH 14/19] Fixed merges --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 2c4d55154..c4790d3eb 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", "mongodb/mongodb": "~1.1.4" + }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", "dropbox-php/dropbox-php": "to use the Dropbox adapter", @@ -60,8 +61,7 @@ "ext-mbstring": "*", "ext-mongodb": "*", "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters", - "mongodb/mongodb": "*", - "google/cloud-storage": "to use Google Cloud Client + "mongodb/mongodb": "*" }, "autoload": { "psr-0": { "Gaufrette": "src/" } From 58da1477f66c3166dadb21565a18372434dfd9ab Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 2 Jun 2017 18:02:51 +0200 Subject: [PATCH 15/19] Work in progress for requested changes --- .gitignore | 1 + composer.json | 6 +- phpunit.xml.dist | 6 ++ .../Adapter/GoogleCloudClientStorage.php | 66 ++++++-------- .../Adapter/GoogleCloudClientStorageTest.php | 85 +++++++++++-------- .../GoogleCloudClientStorage.php.dist | 19 ----- 6 files changed, 87 insertions(+), 96 deletions(-) delete mode 100644 tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist diff --git a/.gitignore b/.gitignore index bbe943201..a2a62f01f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ vendor/ composer.lock phpunit.xml +/nbproject/* \ No newline at end of file diff --git a/composer.json b/composer.json index c4790d3eb..4a9a44cd9 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ "phpunit/phpunit": "3.7.*", "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", - "mongodb/mongodb": "~1.1.4" + "mongodb/mongodb": "~1.1.4", + "google/cloud-storage": "dev-master" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", @@ -61,7 +62,8 @@ "ext-mbstring": "*", "ext-mongodb": "*", "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters", - "mongodb/mongodb": "*" + "mongodb/mongodb": "*", + "google/cloud-storage": "to use Google Cloud Client Library" }, "autoload": { "psr-0": { "Gaufrette": "src/" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d3101efee..5dce30713 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -38,6 +38,12 @@ + + + + diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 76b155881..24d972877 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -38,6 +38,7 @@ public function __construct(\Google\Cloud\Storage\StorageClient $storageClient, ), $options ); + $this->options['directory'] = rtrim($this->options['directory'], '/'); } /** @@ -60,14 +61,10 @@ public function setOptions($options) $this->options = array_replace($this->options, $options); } - protected function computePath($key) + protected function computePath($key = null) { if (strlen($this->options['directory'])) { - if (strcmp(substr($this->options['directory'], -1), '/') == 0) - { - return $this->options['directory'].$key; - } return $this->options['directory'].'/'.$key; } return $key; @@ -133,9 +130,8 @@ public function write($key, $content) 'metadata' => $this->getMetadata($key) ) ); - + $size = $this->getResourceByName($key, 'size'); - return $size === null ? false : $size; } @@ -146,11 +142,7 @@ public function exists($key) { $this->isBucket(); $object = $this->bucket->object($this->computePath($key)); - if ($object->exists()) - { - return true; - } - return false; + return $object->exists(); } /** @@ -158,11 +150,7 @@ public function exists($key) */ public function isDirectory($key) { - if ($this->exists($key . '/')) - { - return true; - } - return false; + return $this->exists($this->computePath(rtrim($key, '/')).'/'); } /** @@ -171,14 +159,9 @@ public function isDirectory($key) public function listKeys($prefix = null) { $this->isBucket(); - $keys = array(); - if ($prefix === null) - { - $prefix = $this->options['directory']; - } else { - $prefix = $this->computePath($prefix); - } - foreach ($this->bucket->objects(array('prefix' => $prefix)) as $e) + $keys = array(); + + foreach ($this->bucket->objects(array('prefix' => $this->computePath($prefix))) as $e) { $keys[] = $e->name(); } @@ -225,16 +208,14 @@ public function rename($sourceKey, $targetKey) { $this->isBucket(); - $metadata = $this->getMetadata($sourceKey); - - $sourceKey = $this->computePath($sourceKey); - $targetKey = $this->computePath($targetKey); - - $object = $this->bucket->object($sourceKey); + $pathedSourceKey = $this->computePath($sourceKey); + $pathedTargetKey = $this->computePath($targetKey); + + $object = $this->bucket->object($pathedSourceKey); $copiedObject = $object->copy($this->bucket, array( - 'name' => $targetKey + 'name' => $pathedTargetKey ) ); @@ -259,7 +240,7 @@ public function rename($sourceKey, $targetKey) */ public function setMetadata($key, $metadata) { - $this->metadata[$key] = $metadata; + $this->metadata[$this->computePath($key)] = $metadata; } /** @@ -267,15 +248,16 @@ public function setMetadata($key, $metadata) */ public function getMetadata($key) { - if (!isset($this->metadata[$key]) && $this->exists($key)) + $pathedKey = $this->computePath($key); + if (!isset($this->metadata[$pathedKey]) && $this->exists($pathedKey)) { - $data = $this->bucket->object($key)->info(); + $data = $this->bucket->object($pathedKey)->info(); if (isset($data['metadata'])) { - $this->metadata[$key] = $data['metadata']; + $this->metadata[$pathedKey] = $data['metadata']; } } - return isset($this->metadata[$key]) ? $this->metadata[$key] : array(); + return isset($this->metadata[$pathedKey]) ? $this->metadata[$pathedKey] : array(); } /** @@ -283,7 +265,7 @@ public function getMetadata($key) */ public function setResources($key, $data) { - $this->resources[$key] = $data; + $this->resources[$this->computePath($key)] = $data; } /** @@ -291,7 +273,8 @@ public function setResources($key, $data) */ public function getResources($key) { - return isset($this->resources[$key]) ? $this->resources[$key] : array(); + $pathedKey = $this->computePath($key); + return isset($this->resources[$pathedKey]) ? $this->resources[$pathedKey] : array(); } /** @@ -299,7 +282,8 @@ public function getResources($key) */ public function getResourceByName($key, $resourceName) { - return isset($this->resources[$key][$resourceName]) ? $this->resources[$key][$resourceName] : null; + $pathedKey = $this->computePath($key); + return isset($this->resources[$pathedKey][$resourceName]) ? $this->resources[$pathedKey][$resourceName] : null; } /** @@ -315,7 +299,7 @@ protected function updateKeyProperties($key, $options = array()) { return false; } - + $object = $this->bucket->object($this->computePath($key)); $properties = array_replace_recursive( diff --git a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php index 4492b56f9..3534d36fb 100644 --- a/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php +++ b/tests/Gaufrette/Functional/Adapter/GoogleCloudClientStorageTest.php @@ -1,18 +1,48 @@ */ + +namespace Gaufrette\Functional\Adapter; + class GoogleCloudClientStorageTest extends FunctionalTestCase -{ - private $string = 'Yeah mate. No worries, I uploaded just fine. Meow!'; +{ + private $string = 'Yeah mate. No worries, I uploaded just fine. Meow!'; + private $directory = 'tests'; + + public function setUp() + { + $gccs_project_id = getenv('GCCS_PROJECT_ID'); + $gccs_bucket_name = getenv('GCCS_BUCKET_NAME'); + $gccs_json_key_file_path = getenv('GCCS_JSON_KEY_FILE_PATH'); + + if (empty($gccs_project_id) || empty($gccs_bucket_name) || empty($gccs_json_key_file_path)) + { + $this->markTestSkipped('Required enviroment variables are not defined.'); + } elseif (!is_readable($gccs_json_key_file_path)) { + $this->markTestSkipped(sprintf('Cannot read JSON key file from "%s".', $gccs_json_key_file_path)); + } + + $storage = new \Google\Cloud\Storage\StorageClient( + array( + 'projectId' => $gccs_project_id, + 'keyFilePath' => $gccs_json_key_file_path + ) + ); + + $adapter = new \Gaufrette\Adapter\GoogleCloudClientStorage($storage, $gccs_bucket_name, + array( + 'directory' => $this->directory, + 'acl' => array( + 'allUsers' => \Google\Cloud\Storage\Acl::ROLE_READER + ) + ) + ); + + $this->filesystem = new \Gaufrette\Filesystem($adapter); + } /** * @test @@ -25,9 +55,7 @@ public function shouldFailIfBucketIsNotAccessible() { /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ $adapter = $this->filesystem->getAdapter(); - $options = $adapter->getOptions(); $adapter->setBucket('meow_'.mt_rand()); - $adapter->setOptions($options); } /** @@ -37,15 +65,11 @@ public function shouldFailIfBucketIsNotAccessible() */ public function shouldListBucketContent() { - /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ - $adapter = $this->filesystem->getAdapter(); - $options = $adapter->getOptions(); - $this->assertEquals(strlen($this->string), $this->filesystem->write('Phat/Cat.txt', $this->string, true)); - $keys = $this->filesystem->keys(); - $this->assertEquals($keys[0], 'Phat/Cat.txt'); - $this->filesystem->delete('Phat/Cat.txt'); - $adapter->setOptions($options); + $keys = $this->filesystem->keys(); + $file = $this->directory ? $this->directory.'/Phat/Cat.txt' : 'Phat/Cat.txt'; + $this->assertTrue(in_array($file, $keys)); + $this->filesystem->delete('Phat/Cat.txt'); } /** @@ -55,10 +79,6 @@ public function shouldListBucketContent() */ public function shouldWriteAndReadFile() { - /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ - $adapter = $this->filesystem->getAdapter(); - $options = $adapter->getOptions(); - //$adapter->setOptions(array('directory' => 'Phat')); $this->assertEquals(strlen($this->string), $this->filesystem->write('Phat/Cat.txt', $this->string, true)); $this->assertEquals(strlen($this->string), $this->filesystem->write('Phatter/Cat.txt', $this->string, true)); @@ -67,7 +87,6 @@ public function shouldWriteAndReadFile() $this->filesystem->delete('Phat/Cat.txt'); $this->filesystem->delete('Phatter/Cat.txt'); - $adapter->setOptions($options); } /** @@ -79,14 +98,12 @@ public function shouldWriteAndReadFileMetadata() { /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ $adapter = $this->filesystem->getAdapter(); - $options = $adapter->getOptions(); $file = 'PhatCat/Cat.txt'; $adapter->setMetadata($file, array('OhMy' => 'I am a cat file!')); $this->assertEquals(strlen($this->string), $this->filesystem->write($file, $this->string, true)); $info = $adapter->getMetadata($file); $this->assertEquals($info['OhMy'], 'I am a cat file!'); $this->filesystem->delete($file); - $adapter->setOptions($options); } /** @@ -98,14 +115,12 @@ public function shouldWriteAndRenameFile() { /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ $adapter = $this->filesystem->getAdapter(); - $options = $adapter->getOptions(); $file = 'Cat.txt'; $adapter->setMetadata($file, array('OhMy' => 'I am a cat file!')); $this->assertEquals(strlen($this->string), $this->filesystem->write($file, $this->string, true)); $adapter->rename('Cat.txt', 'Kitten.txt'); $this->assertEquals($adapter->getMetadata('Kitten.txt'), $adapter->getResourceByName('Kitten.txt', 'metadata')); $this->filesystem->delete('Kitten.txt'); - $adapter->setOptions($options); } /** @@ -117,16 +132,18 @@ public function shouldWriteAndReadPublicFile() { /** @var \Gaufrette\Adapter\GoogleCloudClientStorage $adapter */ $adapter = $this->filesystem->getAdapter(); - $options = $adapter->getOptions(); $file = 'Cat.txt'; $this->assertEquals(strlen($this->string), $this->filesystem->write($file, $this->string, true)); - $public_link = sprintf('https://storage.googleapis.com/%s/Cat.txt', $adapter->getBucket()->name()); - + if ($this->directory) + { + $public_link = sprintf('https://storage.googleapis.com/%s/%s/Cat.txt', $adapter->getBucket()->name(), $this->directory); + } else { + $public_link = sprintf('https://storage.googleapis.com/%s/Cat.txt', $adapter->getBucket()->name()); + } + $headers = @get_headers($public_link); $this->assertEquals($headers[0], 'HTTP/1.0 200 OK'); $this->filesystem->delete('Cat.txt'); - $adapter->setOptions($options); - } - + } } \ No newline at end of file diff --git a/tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist b/tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist deleted file mode 100644 index 006c283dc..000000000 --- a/tests/Gaufrette/Functional/adapters/GoogleCloudClientStorage.php.dist +++ /dev/null @@ -1,19 +0,0 @@ - 'your-project-id', - 'keyFilePath' => 'path/to/your/project/key.json' -)); - -$bucket_name = 'bucket_name'; - -$adapter = new Gaufrette\Adapter\GoogleCloudClientStorage($storage, $bucket_name, - array( - 'directory' => '', - 'acl' => array( - 'allUsers' => \Google\Cloud\Storage\Acl::ROLE_READER - ) - ) -); - -return $adapter; \ No newline at end of file From 13cfbca4a2099616a759cd8bfa05533264e20abd Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Jun 2017 12:42:01 +0200 Subject: [PATCH 16/19] Requested changes fixes --- src/Gaufrette/Adapter/ResourcesSupporter.php | 28 -------------------- 1 file changed, 28 deletions(-) delete mode 100644 src/Gaufrette/Adapter/ResourcesSupporter.php diff --git a/src/Gaufrette/Adapter/ResourcesSupporter.php b/src/Gaufrette/Adapter/ResourcesSupporter.php deleted file mode 100644 index a3b5e8dd5..000000000 --- a/src/Gaufrette/Adapter/ResourcesSupporter.php +++ /dev/null @@ -1,28 +0,0 @@ - - */ -interface ResourcesSupporter -{ - /** - * @param string $key - * @param array $data - */ - public function setResources($key, $data); - - /** - * @param string $key - * @return array Array of resource values. - */ - public function getResources($key); - - /** - * @param string $key - * @return array|string|null Array or string depending on resource structure. Null if resource does not exist. - */ - public function getResourceByName($key, $resourceName); -} \ No newline at end of file From e5eb7af6a789a97ad66e7cd852f7193806e119c7 Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Jun 2017 12:43:05 +0200 Subject: [PATCH 17/19] Requested changes fixes --- composer.json | 2 +- doc/adapters/google-cloud-client-storage.md | 22 ++++++++++++++---- phpunit.xml.dist | 3 +-- .../Adapter/GoogleCloudClientStorage.php | 23 ++++++++++++------- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 4a9a44cd9..2f2a6dcbf 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", "mongodb/mongodb": "~1.1.4", - "google/cloud-storage": "dev-master" + "google/cloud-storage": "~1.0" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", diff --git a/doc/adapters/google-cloud-client-storage.md b/doc/adapters/google-cloud-client-storage.md index a8dac9ace..1b023d40f 100644 --- a/doc/adapters/google-cloud-client-storage.md +++ b/doc/adapters/google-cloud-client-storage.md @@ -10,6 +10,14 @@ For more details see: http://googlecloudplatform.github.io/google-cloud-php/ https://console.cloud.google.com/ +In order to get started: + +1) Create a project in [Google Cloud Platform](https://console.cloud.google.com/). +2) Create a bucket for the project in Storage. +3) Create a Service Account in IAM & Admin section that can write access the bucket, download its key.json file. + +**At all times make sure you keep your key.json file private and nobody can access it from the Internet.** + ## Example ```php @@ -23,9 +31,11 @@ $storage = new StorageClient(array( 'keyFilePath' => 'path/to/your/project/key.json' )); -# you can optionally set the directory in the bucket and the acl permissions for all uploaded files... -# by default the uploaded files are read/write by the owner only -# the example below gives read access to the uploaded files to anyone in the world +# You can optionally set the directory in the bucket and the acl permissions for all uploaded files... +# By default Cloud Storage applies the bucket's default object ACL to the object (uploaded file). +# The example below gives read access to the uploaded files to anyone in the world +# Note that the public URL of the file IS NOT the bucket's file url, +# see https://cloud.google.com/storage/docs/access-public-data for details $adapter = new GoogleCloudClientStorage($storage, 'bucket_name', array( @@ -49,4 +59,8 @@ $filesystem = new Filesystem($adapter); $filesystem->write($key, 'Uploaded at: '.date('Y-m-d @ H:i:s'), true); -``` \ No newline at end of file +``` + +Here you can find some more info regarding ACL: +* [Creating and Managing Access Control Lists (ACLs)](https://cloud.google.com/storage/docs/access-control/create-manage-lists) +* [Access Control Lists (ACLs)](https://cloud.google.com/storage/docs/access-control/lists) \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5dce30713..ee1d1b47e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -42,8 +42,7 @@ - + --> diff --git a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php index 24d972877..229119f26 100644 --- a/src/Gaufrette/Adapter/GoogleCloudClientStorage.php +++ b/src/Gaufrette/Adapter/GoogleCloudClientStorage.php @@ -3,7 +3,6 @@ use Gaufrette\Adapter; use Gaufrette\Adapter\MetadataSupporter; -use Gaufrette\Adapter\ResourcesSupporter; use Gaufrette\Adapter\ListKeysAware; /** @@ -13,7 +12,7 @@ * @package Gaufrette * @author Lech Buszczynski */ -class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware +class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ListKeysAware { protected $storageClient; protected $bucket; @@ -101,9 +100,13 @@ public function read($key) { $this->isBucket(); $object = $this->bucket->object($this->computePath($key)); - $info = $object->info(); - $this->setResources($key, $info); - return $object->downloadAsString(); + try { + $info = $object->info(); + $this->setResources($key, $info); + return $object->downloadAsString(); + } catch (\Exception $e) { + return false; + } } /** @@ -195,9 +198,13 @@ public function mtime($key) public function delete($key) { $this->isBucket(); - $object = $this->bucket->object($this->computePath($key)); - $object->delete(); - $this->setMetadata($key, null); + try { + $object = $this->bucket->object($this->computePath($key)); + $object->delete(); + $this->setMetadata($key, null); + } catch (\Exception $e) { + return false; + } return true; } From c4657522de943f7dd075edc70e996d7a191c137f Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Fri, 9 Jun 2017 12:50:24 +0200 Subject: [PATCH 18/19] Fixed composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 232f6740e..2b773cd70 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", "mongodb/mongodb": "~1.1.4", - "google/cloud-storage": "~1.0" + "google/cloud-storage": "~1.0", "microsoft/windowsazure": "~0.4", "microsoft/azure-storage": "~0.15.0", "akeneo/phpspec-skip-example-extension": "~1.2" @@ -64,7 +64,7 @@ "ext-mbstring": "*", "ext-mongodb": "*", "mongodb/mongodb": "*", - "google/cloud-storage": "to use Google Cloud Client Library" + "google/cloud-storage": "to use Google Cloud Client Library", "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters" }, "autoload": { From f86eb53f62d4803d32b1df5b0eeb5f58542be56b Mon Sep 17 00:00:00 2001 From: lbuszczynski Date: Thu, 26 Oct 2017 11:56:28 +0200 Subject: [PATCH 19/19] clean-up --- .gitignore | 2 +- composer.json | 4 ++-- phpunit.xml.dist | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a2a62f01f..54a203878 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,5 @@ vendor/ .env composer.lock phpunit.xml - +.idea/ /nbproject/* \ No newline at end of file diff --git a/composer.json b/composer.json index 2b773cd70..617b68bf9 100644 --- a/composer.json +++ b/composer.json @@ -41,10 +41,10 @@ "mikey179/vfsStream": "~1.2.0", "league/flysystem": "~1.0", "mongodb/mongodb": "~1.1.4", - "google/cloud-storage": "~1.0", "microsoft/windowsazure": "~0.4", "microsoft/azure-storage": "~0.15.0", - "akeneo/phpspec-skip-example-extension": "~1.2" + "akeneo/phpspec-skip-example-extension": "~1.2", + "google/cloud-storage": "^1.1" }, "suggest": { "knplabs/knp-gaufrette-bundle": "to use with Symfony2", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fa95dd995..1fb94ee2c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -42,7 +42,8 @@ + --> +