Files
WPS3Media/vendor/Aws3/Aws/QueryCompatibleInputMiddleware.php
Malin 3248cbb029 feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)
Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.

Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
  - Provider key: s3compatible
  - Reads user-configured endpoint URL from settings
  - Uses path-style URL access (required by most S3-compatible services)
  - Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
    AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
  - Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
2026-03-03 12:30:18 +01:00

205 lines
5.9 KiB
PHP

<?php
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws;
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Api\ListShape;
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Api\MapShape;
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Api\Service;
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Api\Shape;
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Api\StructureShape;
use Closure;
/**
* Inspects command input values and casts them to their modeled type.
* This covers query compatible services which have migrated from query
* to JSON wire protocols.
*
* @internal
*/
class QueryCompatibleInputMiddleware
{
/** @var callable */
private $nextHandler;
/** @var Service */
private $service;
/** @var CommandInterface */
private $command;
/**
* Create a middleware wrapper function.
*
* @param Service $service
* @return Closure
*/
public static function wrap(Service $service) : Closure
{
return static function (callable $handler) use($service) {
return new self($handler, $service);
};
}
public function __construct(callable $nextHandler, Service $service)
{
$this->service = $service;
$this->nextHandler = $nextHandler;
}
public function __invoke(CommandInterface $cmd)
{
$this->command = $cmd;
$nextHandler = $this->nextHandler;
$op = $this->service->getOperation($cmd->getName());
$inputMembers = $op->getInput()->getMembers();
$input = $cmd->toArray();
foreach ($input as $param => $value) {
if (isset($inputMembers[$param])) {
$shape = $inputMembers[$param];
$this->processInput($value, $shape, [$param]);
}
}
return $nextHandler($this->command);
}
/**
* Recurses a given input shape. if a given scalar input does not match its
* modeled type, it is cast to its modeled type.
*
* @param $input
* @param $shape
* @param array $path
*
* @return void
*/
private function processInput($input, $shape, array $path) : void
{
switch ($shape->getType()) {
case 'structure':
$this->processStructure($input, $shape, $path);
break;
case 'list':
$this->processList($input, $shape, $path);
break;
case 'map':
$this->processMap($input, $shape, $path);
break;
default:
$this->processScalar($input, $shape, $path);
}
}
/**
* @param array $input
* @param StructureShape $shape
* @param array $path
*
* @return void
*/
private function processStructure(array $input, StructureShape $shape, array $path) : void
{
foreach ($input as $param => $value) {
if ($shape->hasMember($param)) {
$memberPath = \array_merge($path, [$param]);
$this->processInput($value, $shape->getMember($param), $memberPath);
}
}
}
/**
* @param array $input
* @param ListShape $shape
* @param array $path
*
* @return void
*/
private function processList(array $input, ListShape $shape, array $path) : void
{
foreach ($input as $param => $value) {
$memberPath = \array_merge($path, [$param]);
$this->processInput($value, $shape->getMember(), $memberPath);
}
}
/**
* @param array $input
* @param MapShape $shape
* @param array $path
*
* @return void
*/
private function processMap(array $input, MapShape $shape, array $path) : void
{
foreach ($input as $param => $value) {
$memberPath = \array_merge($path, [$param]);
$this->processInput($value, $shape->getValue(), $memberPath);
}
}
/**
* @param $input
* @param Shape $shape
* @param array $path
*
* @return void
*/
private function processScalar($input, Shape $shape, array $path) : void
{
$expectedType = $shape->getType();
if (!$this->isModeledType($input, $expectedType)) {
\trigger_error("The provided type for `" . \implode(' -> ', $path) . "` value was `" . (\gettype($input) === 'double' ? 'float' : \gettype($input)) . "`." . " The modeled type is `{$expectedType}`.", \E_USER_WARNING);
$value = $this->castValue($input, $expectedType);
$this->changeValueAtPath($path, $value);
}
}
/**
* Modifies command in place
*
* @param array $path
* @param $newValue
*
* @return void
*/
private function changeValueAtPath(array $path, $newValue) : void
{
$commandRef =& $this->command;
foreach ($path as $segment) {
if (!isset($commandRef[$segment])) {
return;
}
$commandRef =& $commandRef[$segment];
}
$commandRef = $newValue;
}
/**
* @param $value
* @param $type
*
* @return bool
*/
private function isModeledType($value, $type) : bool
{
switch ($type) {
case 'string':
return \is_string($value);
case 'integer':
case 'long':
return \is_int($value);
case 'float':
return \is_float($value);
default:
return \true;
}
}
/**
* @param $value
* @param $type
*
* @return float|int|mixed|string
*/
private function castValue($value, $type)
{
switch ($type) {
case 'integer':
return (int) $value;
case 'long':
return $value + 0;
case 'float':
return (float) $value;
case 'string':
return (string) $value;
default:
return $value;
}
}
}