Skip to content

Instantly share code, notes, and snippets.

@vakata
Last active March 17, 2023 10:13
Show Gist options
  • Save vakata/de39ea4bceff0d224e2e05553fd56fae to your computer and use it in GitHub Desktop.
Save vakata/de39ea4bceff0d224e2e05553fd56fae to your computer and use it in GitHub Desktop.
Google Cloud Storage mini-class
<?php
// depends on vakata/jwt (any other jwt should work too)
// composer require vakata/jwt
/*
// auth.json contains ServiceAccountCredentials
// the only required fields are client_email and private_key
$uploader = GoogleCloudStorage::fromFile('auth.json', '<bucket-name>');
var_dump($uploader->listBucket());
$uploader->uploadFile('composer.lock'); // uploads with the same name
var_dump($uploader->listBucket());
$uploader->deleteFile('composer.lock');
var_dump($uploader->listBucket());
$uploader->uploadDirectory('test', ''); // uploads dir contents in bucket root
var_dump($uploader->listBucket());
foreach ($uploader->listBucket() as $v) {
$uploader->deleteFile($v);
}
var_dump($uploader->listBucket());
*/
class GoogleCloudStorage
{
const BASEURI = 'https://storage.googleapis.com';
protected string $bucket;
protected string $token;
public static function fromFile(string $path, string $defaultBucket = ""): self
{
$auth = json_decode(file_get_contents($path), true);
return self::fromKey($auth['client_email'], $auth['private_key'], $defaultBucket);
}
public static function fromKey(string $email, string $key, string $defaultBucket = ""): self
{
$token = new \vakata\jwt\JWT(
[
"iss" => $email,
"exp" => time() + 1800,
"iat" => time(),
"scope" => implode(
' ',
[
"https://www.googleapis.com/auth/iam",
"https://www.googleapis.com/auth/devstorage.full_control"
]
),
"sub" => $email,
],
'RS256'
);
$token->sign($key);
return new self($token->toString(), $defaultBucket);
}
public function __construct(string $token, string $defaultBucket = "")
{
$this->token = $token;
$this->bucket = $defaultBucket;
}
public function uploadFile(string $path, ?string $name = null, ?string $bucket = null): void
{
$path = realpath($path);
$name = $name ?? basename($path);
$bucket = $bucket ?? $this->bucket;
file_get_contents(
self::BASEURI . '/upload/storage/v1/b/' . $bucket . '/o?uploadType=media&name=' . $name,
false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => '' .
'Content-Type: ' . (mime_content_type($path) ?: 'application/octet-stream') . "\r\n" .
'Authorization: Bearer ' . $this->token . "\r\n",
'content' => file_get_contents($path)
]
])
);
}
public function uploadDirectory(string $path, ?string $name = null, ?string $bucket = null)
{
$path = realpath($path);
$name = $name ?? basename($path);
$bucket = $bucket ?? $this->bucket;
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(
$path,
\FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO
),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $k => $object) {
if ($object->isFile()) {
$this->uploadFile($k, ltrim($name . str_replace('\\', '/', substr($k, strlen($path))), '/'), $bucket);
}
}
}
public function listBucket(?string $bucket = null): array
{
$bucket = $bucket ?? $this->bucket;
$json = file_get_contents(
self::BASEURI . '/storage/v1/b/' . $bucket . '/o',
false,
stream_context_create([
'http' => [
'header' => 'Authorization: Bearer ' . $this->token
]
])
);
$json = json_decode($json, true);
$temp = [];
foreach ($json['items'] ?? [] as $o) {
$temp[$o['id']] = $o['name'];
}
return $temp;
}
public function deleteFile(string $name, ?string $bucket = null): void
{
$bucket = $bucket ?? $this->bucket;
file_get_contents(
self::BASEURI . '/storage/v1/b/' . $bucket . '/o/' . urlencode($name),
false,
stream_context_create([
'http' => [
'method' => 'DELETE',
'header' => 'Authorization: Bearer ' . $this->token
]
])
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment