Skip to content

Commit 70fb544

Browse files
committed
feat: add new streamwrapper to access private bucket
1 parent 0dcdf83 commit 70fb544

24 files changed

+584
-274
lines changed

grumphp.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ grumphp:
3333
- 'pluggable.php'
3434
- 'src/Autoloader.php'
3535
- 'src/Configuration'
36-
- 'src/CloudStorage/CloudStorageStreamWrapper.php'
36+
- 'src/CloudStorage/AbstractCloudStorageStreamWrapper.php'
3737
- 'src/Email/Email.php'
3838
- 'src/ObjectCache/AbstractPersistentObjectCache.php'
3939
- 'src/Subscriber/ContentDeliveryNetworkPageCachingSubscriber.php'

src/Attachment/ImagickImageEditor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function save($destfilename = null, $mime_type = null)
6969
*/
7070
protected function _save($image, $filename = null, $mime_type = null)
7171
{
72-
// Imagick by default can't handle cloudstorage:// paths so have it save the file locally.
72+
// Imagick by default can't handle ymir-public:// paths so have it save the file locally.
7373
if (is_string($filename) && $this->fileManager->isInUploadsDirectory($filename)) {
7474
$filename = $this->fileManager->getTempFilePath($filename);
7575
}

src/CloudStorage/CloudStorageStreamWrapper.php renamed to src/CloudStorage/AbstractCloudStorageStreamWrapper.php

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,8 @@
1818
*
1919
* @see https://www.php.net/manual/en/class.streamwrapper.php
2020
*/
21-
class CloudStorageStreamWrapper
21+
abstract class AbstractCloudStorageStreamWrapper
2222
{
23-
/**
24-
* Name of the protocol used by the wrapper.
25-
*
26-
* @var string
27-
*/
28-
public const PROTOCOL = 'cloudstorage';
29-
3023
/**
3124
* The resource handled by the stream which is set by PHP.
3225
*
@@ -39,69 +32,77 @@ class CloudStorageStreamWrapper
3932
*
4033
* @var \ArrayObject|null
4134
*/
42-
private $cache;
35+
protected $cache;
4336

4437
/**
4538
* The cloud storage objects retrieved with "dir_opendir".
4639
*
4740
* @var \ArrayIterator|null
4841
*/
49-
private $openedDirectoryObjects;
42+
protected $openedDirectoryObjects;
5043

5144
/**
5245
* The path when "dir_opendir" was called.
5346
*
5447
* @var string|null
5548
*/
56-
private $openedDirectoryPath;
49+
protected $openedDirectoryPath;
5750

5851
/**
5952
* The prefix used to get the cloud storage objects with "dir_opendir".
6053
*
6154
* @var string|null
6255
*/
63-
private $openedDirectoryPrefix;
56+
protected $openedDirectoryPrefix;
6457

6558
/**
6659
* Mode used when the stream was opened.
6760
*
6861
* @var string
6962
*/
70-
private $openedStreamMode;
63+
protected $openedStreamMode;
7164

7265
/**
7366
* The key for the cloud storage object opened by "stream_open".
7467
*
7568
* @var string
7669
*/
77-
private $openedStreamObjectKey;
70+
protected $openedStreamObjectKey;
7871

7972
/**
8073
* The resource containing the cloud storage object opened by "stream_open".
8174
*
8275
* @var resource|null
8376
*/
84-
private $openedStreamObjectResource;
77+
protected $openedStreamObjectResource;
78+
79+
/**
80+
* Get the protocol used by the stream wrapper.
81+
*/
82+
public static function getProtocol(): string
83+
{
84+
throw new \RuntimeException('Must overrider "getProtocol" method');
85+
}
8586

8687
/**
8788
* Register the cloud storage stream wrapper.
8889
*/
8990
public static function register(CloudStorageClientInterface $client, \ArrayObject $cache = null)
9091
{
91-
if (in_array(self::PROTOCOL, stream_get_wrappers())) {
92-
stream_wrapper_unregister(self::PROTOCOL);
92+
if (in_array(static::getProtocol(), stream_get_wrappers())) {
93+
stream_wrapper_unregister(static::getProtocol());
9394
}
9495

95-
stream_wrapper_register(self::PROTOCOL, self::class, STREAM_IS_URL);
96+
stream_wrapper_register(static::getProtocol(), static::class, STREAM_IS_URL);
9697

9798
$defaultOptions = stream_context_get_options(stream_context_get_default());
9899

99-
$defaultOptions[self::PROTOCOL]['client'] = $client;
100+
$defaultOptions[static::getProtocol()]['client'] = $client;
100101

101102
if ($cache instanceof \ArrayObject) {
102-
$defaultOptions[self::PROTOCOL]['cache'] = $cache;
103-
} elseif (!isset($defaultOptions[self::PROTOCOL]['cache'])) {
104-
$defaultOptions[self::PROTOCOL]['cache'] = new \ArrayObject();
103+
$defaultOptions[static::getProtocol()]['cache'] = $cache;
104+
} elseif (!isset($defaultOptions[static::getProtocol()]['cache'])) {
105+
$defaultOptions[static::getProtocol()]['cache'] = new \ArrayObject();
105106
}
106107

107108
stream_context_set_default($defaultOptions);
@@ -312,7 +313,7 @@ public function stream_flush()
312313

313314
$this->getClient()->putObject($this->openedStreamObjectKey, stream_get_contents($this->openedStreamObjectResource), $this->getMimetype());
314315

315-
$this->removeCacheValue(self::PROTOCOL.'://'.$this->openedStreamObjectKey);
316+
$this->removeCacheValue(static::getProtocol().'://'.$this->openedStreamObjectKey);
316317
});
317318
}
318319

@@ -356,7 +357,7 @@ public function stream_open(string $path, string $mode): bool
356357
// Remove the cache value in case we interacted with the file before using something
357358
// like "file_exists". If we don't write to the file, there won't be any cache busting
358359
// so this is the only opportunity to do so.
359-
$this->removeCacheValue(self::PROTOCOL.'://'.$this->openedStreamObjectKey);
360+
$this->removeCacheValue(static::getProtocol().'://'.$this->openedStreamObjectKey);
360361
}
361362

362363
$this->openedStreamObjectResource = fopen('php://temp', 'r+');
@@ -649,8 +650,8 @@ private function getOptions(): array
649650
$context = stream_context_get_options($this->context);
650651
}
651652

652-
$context = $context[self::PROTOCOL] ?? [];
653-
$default = $default[self::PROTOCOL] ?? [];
653+
$context = $context[static::getProtocol()] ?? [];
654+
$default = $default[static::getProtocol()] ?? [];
654655

655656
return $context + $default;
656657
}
@@ -737,7 +738,7 @@ private function parseMode(string $key, string $mode): string
737738
*/
738739
private function parsePath(string $path): string
739740
{
740-
$protocol = self::PROTOCOL.'://';
741+
$protocol = static::getProtocol().'://';
741742

742743
if (0 !== strpos($path, $protocol)) {
743744
throw new \InvalidArgumentException(sprintf('Invalid protocol for "%s"', $path));
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Ymir WordPress plugin.
7+
*
8+
* (c) Carl Alexander <support@ymirapp.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Ymir\Plugin\CloudStorage;
15+
16+
class PrivateCloudStorageStreamWrapper extends AbstractCloudStorageStreamWrapper
17+
{
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public static function getProtocol(): string
22+
{
23+
return 'ymir-private';
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Ymir WordPress plugin.
7+
*
8+
* (c) Carl Alexander <support@ymirapp.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Ymir\Plugin\CloudStorage;
15+
16+
class PublicCloudStorageStreamWrapper extends AbstractCloudStorageStreamWrapper
17+
{
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public static function getProtocol(): string
22+
{
23+
return 'ymir-public';
24+
}
25+
}

src/Configuration/CloudProviderConfiguration.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ public function modify(Container $container)
4646
$container['cloud_provider_secret'] = $container->service(function () {
4747
return getenv('AWS_SECRET_ACCESS_KEY') ?: (defined('YMIR_CLOUD_PROVIDER_SECRET') ? YMIR_CLOUD_PROVIDER_SECRET : '');
4848
});
49-
$container['cloud_provider_store'] = $container->service(function () {
50-
return getenv('YMIR_PUBLIC_STORE') ?: (defined('YMIR_CLOUD_PROVIDER_STORE') ? YMIR_CLOUD_PROVIDER_STORE : '');
49+
$container['cloud_provider_private_store'] = $container->service(function () {
50+
return getenv('YMIR_PRIVATE_STORE') ?: (defined('YMIR_CLOUD_PROVIDER_PRIVATE_STORE') ? YMIR_CLOUD_PROVIDER_PRIVATE_STORE : '');
51+
});
52+
$container['cloud_provider_public_store'] = $container->service(function () {
53+
return getenv('YMIR_PUBLIC_STORE') ?: (defined('YMIR_CLOUD_PROVIDER_PUBLIC_STORE') ? YMIR_CLOUD_PROVIDER_PUBLIC_STORE : '');
5154
});
5255
}
5356
}

src/Configuration/CloudStorageConfiguration.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
namespace Ymir\Plugin\Configuration;
1515

1616
use Ymir\Plugin\CloudProvider\Aws\S3Client;
17-
use Ymir\Plugin\CloudStorage\CloudStorageStreamWrapper;
17+
use Ymir\Plugin\CloudStorage\PrivateCloudStorageStreamWrapper;
18+
use Ymir\Plugin\CloudStorage\PublicCloudStorageStreamWrapper;
1819
use Ymir\Plugin\DependencyInjection\Container;
1920
use Ymir\Plugin\DependencyInjection\ContainerConfigurationInterface;
2021

@@ -28,9 +29,13 @@ class CloudStorageConfiguration implements ContainerConfigurationInterface
2829
*/
2930
public function modify(Container $container)
3031
{
31-
$container['cloud_storage_client'] = $container->service(function (Container $container) {
32-
return new S3Client($container['ymir_http_client'], $container['cloud_provider_store'], $container['cloud_provider_key'], $container['cloud_provider_region'], $container['cloud_provider_secret']);
32+
$container['private_cloud_storage_client'] = $container->service(function (Container $container) {
33+
return new S3Client($container['ymir_http_client'], $container['cloud_provider_private_store'], $container['cloud_provider_key'], $container['cloud_provider_region'], $container['cloud_provider_secret']);
3334
});
34-
$container['cloud_storage_protocol'] = CloudStorageStreamWrapper::PROTOCOL.'://';
35+
$container['private_cloud_storage_protocol'] = PrivateCloudStorageStreamWrapper::getProtocol().'://';
36+
$container['public_cloud_storage_client'] = $container->service(function (Container $container) {
37+
return new S3Client($container['ymir_http_client'], $container['cloud_provider_public_store'], $container['cloud_provider_key'], $container['cloud_provider_region'], $container['cloud_provider_secret']);
38+
});
39+
$container['public_cloud_storage_protocol'] = PublicCloudStorageStreamWrapper::getProtocol().'://';
3540
}
3641
}

src/Configuration/EventManagementConfiguration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function modify(Container $container)
3737
$container['priority_subscribers'] = $container->service(function (Container $container) {
3838
return [
3939
new Subscriber\BedrockSubscriber($container['ymir_project_type']),
40-
new Subscriber\UploadsSubscriber($container['content_directory'], $container['content_url'], $container['cloud_storage_protocol'], $container['upload_url'], $container['upload_limit']),
40+
new Subscriber\UploadsSubscriber($container['content_directory'], $container['content_url'], $container['public_cloud_storage_protocol'], $container['upload_url'], $container['upload_limit']),
4141
];
4242
});
4343

src/Configuration/RestApiConfiguration.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public function modify(Container $container)
3030
$container['rest_namespace'] = 'ymir/v1';
3131
$container['rest_endpoints'] = $container->service(function (Container $container) {
3232
return [
33-
new RestApi\CreateAttachmentEndpoint($container['cloud_storage_client'], $container['console_client'], $container['uploads_basedir'], $container['uploads_baseurl'], $container['force_async_attachment_creation']),
34-
new RestApi\GetFileDetailsEndpoint($container['cloud_storage_client'], $container['uploads_path'], $container['uploads_subdir']),
33+
new RestApi\CreateAttachmentEndpoint($container['public_cloud_storage_client'], $container['console_client'], $container['uploads_basedir'], $container['uploads_baseurl'], $container['force_async_attachment_creation']),
34+
new RestApi\GetFileDetailsEndpoint($container['public_cloud_storage_client'], $container['uploads_path'], $container['uploads_subdir']),
3535
];
3636
});
3737
}

src/Plugin.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
namespace Ymir\Plugin;
1515

16-
use Ymir\Plugin\CloudStorage\CloudStorageStreamWrapper;
16+
use Ymir\Plugin\CloudStorage\PrivateCloudStorageStreamWrapper;
17+
use Ymir\Plugin\CloudStorage\PublicCloudStorageStreamWrapper;
1718
use Ymir\Plugin\Console\CommandInterface;
1819
use Ymir\Plugin\DependencyInjection\Container;
1920

@@ -125,7 +126,8 @@ public function load()
125126
$this->container['plugin_dir_url'] = plugin_dir_url($this->filePath);
126127
$this->container['plugin_relative_path'] = '/'.trim(str_replace($this->container['root_directory'], '', plugin_dir_path($this->filePath)), '/');
127128

128-
CloudStorageStreamWrapper::register($this->container['cloud_storage_client']);
129+
PrivateCloudStorageStreamWrapper::register($this->container['private_cloud_storage_client']);
130+
PublicCloudStorageStreamWrapper::register($this->container['public_cloud_storage_client']);
129131

130132
foreach ($this->container['local_commands'] as $command) {
131133
$this->registerCommand($command);

0 commit comments

Comments
 (0)