Skip to content

Commit 4212914

Browse files
committed
Presets with descriptors
- presets are now connected to srcSet descriptors - providing a descriptor in methods `FileInfoInterface::srcSet()` and `LinkGeneratorInterface::srcSet()` is now optional, the descriptor is automatically resolved from used preset - added configuration option `disable_signature_on_known_modifiers` - known modifier paths are now resolved during DI container build time
1 parent 57d73ed commit 4212914

39 files changed

Lines changed: 644 additions & 210 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SixtyEightPublishers\ImageStorage\Bridge\Nette\DI\Config;
6+
7+
final class PresetConfig
8+
{
9+
/** @var array<string, numeric|string|bool> */
10+
public array $modifiers = [];
11+
12+
/** @var array<int> */
13+
public array $w = [];
14+
15+
/** @var list<int|float> */
16+
public array $x = [];
17+
18+
public int|null $defaultW = null;
19+
20+
public int|float|null $defaultX = null;
21+
}

src/Bridge/Nette/DI/Config/StorageConfig.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ final class StorageConfig
2121
/** @var array<string, string> */
2222
public array $no_image_patterns;
2323

24-
/** @var array<string, array<string, scalar>> */
24+
/** @var array<string, PresetConfig> */
2525
public array $presets;
2626

2727
/** @var array<Statement> */

src/Bridge/Nette/DI/ImageStorageExtension.php

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use SixtyEightPublishers\ImageStorage\Bridge\Nette\Application\ImageServerPresenter;
3232
use SixtyEightPublishers\ImageStorage\Bridge\Nette\Application\ImageServerRoute;
3333
use SixtyEightPublishers\ImageStorage\Bridge\Nette\DI\Config\ImageStorageConfig;
34+
use SixtyEightPublishers\ImageStorage\Bridge\Nette\DI\Config\PresetConfig;
3435
use SixtyEightPublishers\ImageStorage\Bridge\Nette\DI\Config\StorageConfig;
3536
use SixtyEightPublishers\ImageStorage\Bridge\Nette\ImageServer\ResponseFactory;
3637
use SixtyEightPublishers\ImageStorage\Bridge\Symfony\Console\Configurator\CleanCommandConfigurator;
@@ -52,11 +53,14 @@
5253
use SixtyEightPublishers\ImageStorage\LinkGenerator\LinkGeneratorInterface;
5354
use SixtyEightPublishers\ImageStorage\Modifier;
5455
use SixtyEightPublishers\ImageStorage\Modifier\Applicator;
56+
use SixtyEightPublishers\ImageStorage\Modifier\Codec\Codec;
57+
use SixtyEightPublishers\ImageStorage\Modifier\Codec\PresetCodec;
5558
use SixtyEightPublishers\ImageStorage\Modifier\Collection\ModifierCollection;
5659
use SixtyEightPublishers\ImageStorage\Modifier\Collection\ModifierCollectionFactoryInterface;
5760
use SixtyEightPublishers\ImageStorage\Modifier\Facade\ModifierFacadeFactory;
5861
use SixtyEightPublishers\ImageStorage\Modifier\Facade\ModifierFacadeFactoryInterface;
5962
use SixtyEightPublishers\ImageStorage\Modifier\Facade\ModifierFacadeInterface;
63+
use SixtyEightPublishers\ImageStorage\Modifier\Preset\Preset;
6064
use SixtyEightPublishers\ImageStorage\Modifier\Preset\PresetCollection;
6165
use SixtyEightPublishers\ImageStorage\Modifier\Preset\PresetCollectionFactoryInterface;
6266
use SixtyEightPublishers\ImageStorage\Modifier\Validator;
@@ -65,13 +69,22 @@
6569
use SixtyEightPublishers\ImageStorage\Persistence\ImagePersister;
6670
use SixtyEightPublishers\ImageStorage\Persistence\ImagePersisterInterface;
6771
use SixtyEightPublishers\ImageStorage\Resource\ResourceFactory;
72+
use SixtyEightPublishers\ImageStorage\Responsive\Descriptor\WDescriptor;
73+
use SixtyEightPublishers\ImageStorage\Responsive\Descriptor\XDescriptor;
6874
use SixtyEightPublishers\ImageStorage\Responsive\SrcSetGeneratorFactoryInterface;
75+
use SixtyEightPublishers\ImageStorage\Security\KnownModifiers;
6976
use SixtyEightPublishers\ImageStorage\Security\SignatureStrategy;
7077
use SixtyEightPublishers\ImageStorage\Security\SignatureStrategyInterface;
7178
use function array_diff;
79+
use function array_fill_keys;
7280
use function array_keys;
81+
use function array_map;
82+
use function array_merge;
83+
use function array_unique;
7384
use function assert;
7485
use function is_array;
86+
use function is_string;
87+
use function is_subclass_of;
7588
use function sprintf;
7689

7790
final class ImageStorageExtension extends CompilerExtension implements FileStorageDefinitionFactoryInterface
@@ -120,7 +133,17 @@ public function getConfigSchema(): Schema
120133
'no_image_patterns' => Expect::arrayOf('string', 'string')
121134
->default([]),
122135
'presets' => Expect::arrayOf(
123-
Expect::arrayOf(Expect::scalar(), 'string'),
136+
Expect::structure([
137+
'modifiers' => Expect::arrayOf(Expect::scalar(), 'string'),
138+
'w' => Expect::listOf(Expect::int())->default([]),
139+
'x' => Expect::listOf(Expect::anyOf(Expect::int(), Expect::float()))->default([]),
140+
'defaultW' => Expect::int()->nullable(),
141+
'defaultX' => Expect::anyOf(Expect::int(), Expect::float())->nullable(),
142+
])->castTo(PresetConfig::class)
143+
->assert(
144+
handler: static fn (PresetConfig $preset): bool => !([] !== $preset->w && [] !== $preset->x),
145+
description: 'A preset cannot have both "w" and "x" properties.',
146+
),
124147
'string',
125148
)->default([]),
126149

@@ -329,13 +352,30 @@ public function createFileStorage(string $name, FileStorageConfig $config): Serv
329352
->setFactory(Config::class, [$config->config])
330353
->setAutowired(false);
331354

355+
$presets = array_map(
356+
callback: static fn (PresetConfig $preset): Statement => new Statement(Preset::class, [
357+
'modifiers' => $preset->modifiers,
358+
'descriptor' => match (true) {
359+
[] !== $preset->w => new Statement(WDescriptor::class, $preset->w),
360+
[] !== $preset->x => new Statement(XDescriptor::class, $preset->x),
361+
default => null,
362+
},
363+
'defaultDescriptorValue' => match (true) {
364+
[] !== $preset->w => $preset->defaultW,
365+
[] !== $preset->x => $preset->defaultX,
366+
default => null,
367+
},
368+
]),
369+
array: $imageStorageConfig->presets,
370+
);
371+
332372
$builder->addDefinition($this->prefix('modifier_facade.' . $name))
333373
->setType(ModifierFacadeInterface::class)
334374
->setFactory(new Statement([$this->prefix('@modifiers.modifier_facade_factory'), 'create'], [
335375
new Reference($this->prefix('config.' . $name)),
336376
]))
337377
->addSetup('setModifiers', [$imageStorageConfig->modifiers])
338-
->addSetup('setPresets', [$imageStorageConfig->presets])
378+
->addSetup('setPresets', [$presets])
339379
->addSetup('setApplicators', [$imageStorageConfig->applicators])
340380
->addSetup('setValidators', [$imageStorageConfig->validators])
341381
->setAutowired(false);
@@ -354,7 +394,13 @@ public function createFileStorage(string $name, FileStorageConfig $config): Serv
354394
$signatureStrategyDefinition = $builder->addDefinition($this->prefix('signature_strategy.' . $name))
355395
->setType(SignatureStrategyInterface::class)
356396
->setFactory(SignatureStrategy::class, [
357-
new Reference($this->prefix('config.' . $name)),
397+
'config' => new Reference($this->prefix('config.' . $name)),
398+
'knownModifiers' => new Statement(KnownModifiers::class, [
399+
'list' => $this->buildKnownModifiers(
400+
config: $config,
401+
extConfig: $imageStorageConfig,
402+
),
403+
]),
358404
])
359405
->setAutowired(false);
360406
}
@@ -506,4 +552,92 @@ private function registerImageServerPresenter(): void
506552

507553
$this->imageServerPresenterRegistered = true;
508554
}
555+
556+
/**
557+
* @return array<string, true>
558+
*/
559+
private function buildKnownModifiers(FileStorageConfig $config, StorageConfig $extConfig): array
560+
{
561+
$presets = array_map(
562+
callback: static function (PresetConfig $conf): Preset {
563+
$descriptor = match (true) {
564+
[] !== $conf->w => new WDescriptor(...$conf->w),
565+
[] !== $conf->x => new XDescriptor(...$conf->x),
566+
default => null,
567+
};
568+
569+
$defaultDescriptorValue = match (true) {
570+
[] !== $conf->w => $conf->defaultW,
571+
[] !== $conf->x => $conf->defaultX,
572+
default => null,
573+
};
574+
575+
return new Preset(
576+
modifiers: $conf->modifiers,
577+
descriptor: $descriptor,
578+
defaultDescriptorValue: $defaultDescriptorValue,
579+
);
580+
},
581+
array: $extConfig->presets,
582+
);
583+
584+
$modifiers = array_map(
585+
callback: static function (Statement $modifier): Modifier\ModifierInterface {
586+
$entity = $modifier->getEntity();
587+
$params = $modifier->arguments;
588+
assert(is_string($entity) && is_subclass_of($entity, Modifier\AbstractModifier::class) && is_array($params));
589+
590+
return new $entity(...$params);
591+
},
592+
array: $extConfig->modifiers,
593+
);
594+
595+
$presetsCollection = new PresetCollection();
596+
597+
foreach ($presets as $name => $preset) {
598+
$presetsCollection->add(presetAlias: $name, preset: $preset);
599+
}
600+
601+
$modifierCollection = new ModifierCollection();
602+
603+
foreach ($modifiers as $modifier) {
604+
$modifierCollection->add(modifier: $modifier);
605+
}
606+
607+
$cnf = new Config($config->config);
608+
609+
$codec = new PresetCodec(
610+
codec: new Codec(
611+
config: $cnf,
612+
modifierCollection: $modifierCollection,
613+
),
614+
config: $cnf,
615+
modifierCollection: $modifierCollection,
616+
presetCollection: $presetsCollection,
617+
);
618+
619+
$known = [];
620+
621+
foreach ($presets as $preset) {
622+
if (null === $preset->descriptor) {
623+
$known[] = $codec->modifiersToPath($preset->modifiers);
624+
625+
continue;
626+
}
627+
628+
$modifiers = $preset->modifiers;
629+
630+
foreach ($preset->descriptor->iterateModifiers($modifierCollection) as $mod) {
631+
$known[] = $codec->modifiersToPath(array_merge(
632+
$modifiers,
633+
$mod,
634+
));
635+
}
636+
}
637+
638+
return array_fill_keys(
639+
keys: array_unique($known),
640+
value: true,
641+
);
642+
}
509643
}

src/Config/Config.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ final class Config extends FileStorageConfig
1111
public const SIGNATURE_PARAMETER_NAME = 'signature_parameter_name';
1212
public const SIGNATURE_KEY = 'signature_key';
1313
public const SIGNATURE_ALGORITHM = 'signature_algorithm';
14+
public const DISABLE_SIGNATURE_ON_KNOWN_MODIFIERS = 'disable_signature_on_known_modifiers';
1415
public const MODIFIER_SEPARATOR = 'modifier_separator';
1516
public const MODIFIER_ASSIGNER = 'modifier_assigner';
1617
public const ALLOWED_PIXEL_DENSITY = 'allowed_pixel_density';
@@ -28,6 +29,7 @@ final class Config extends FileStorageConfig
2829
self::SIGNATURE_PARAMETER_NAME => '_s',
2930
self::SIGNATURE_KEY => null,
3031
self::SIGNATURE_ALGORITHM => 'sha256',
32+
self::DISABLE_SIGNATURE_ON_KNOWN_MODIFIERS => false,
3133
self::ALLOWED_PIXEL_DENSITY => [],
3234
self::ALLOWED_RESOLUTIONS => [],
3335
self::ALLOWED_QUALITIES => [],

src/FileInfo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function __construct(ImageLinkGeneratorInterface $linkGenerator, PathInfo
2020
parent::__construct($linkGenerator, $pathInfo, $imageStorageName);
2121
}
2222

23-
public function srcSet(DescriptorInterface $descriptor, bool $absolute = true): SrcSet
23+
public function srcSet(?DescriptorInterface $descriptor = null, bool $absolute = true): SrcSet
2424
{
2525
assert($this->linkGenerator instanceof ImageLinkGeneratorInterface);
2626

src/FileInfoInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010

1111
interface FileInfoInterface extends BaseFileInfoInterface, PathInfoInterface
1212
{
13-
public function srcSet(DescriptorInterface $descriptor, bool $absolute = true): SrcSet;
13+
public function srcSet(?DescriptorInterface $descriptor = null, bool $absolute = true): SrcSet;
1414
}

src/ImageStorage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public function resolveNoImage(string $path): ImagePathInfoInterface
9090
return $this->noImageResolver->resolveNoImage($path);
9191
}
9292

93-
public function srcSet(ImagePathInfoInterface $info, DescriptorInterface $descriptor, bool $absolute = true): SrcSet
93+
public function srcSet(ImagePathInfoInterface $info, ?DescriptorInterface $descriptor = null, bool $absolute = true): SrcSet
9494
{
9595
assert($this->linkGenerator instanceof ImageLinkGeneratorInterface);
9696

src/LinkGenerator/LinkGenerator.php

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use SixtyEightPublishers\ImageStorage\Responsive\SrcSetGeneratorFactoryInterface;
1818
use SixtyEightPublishers\ImageStorage\Security\SignatureStrategyInterface;
1919
use function assert;
20+
use function explode;
2021
use function is_string;
2122
use function sprintf;
2223

@@ -36,14 +37,16 @@ public function __construct(
3637
public function link(FilePathInfoInterface $pathInfo, bool $absolute = true): string
3738
{
3839
if (!$pathInfo instanceof ImagePathInfoInterface) {
39-
throw new InvalidArgumentException(sprintf(
40-
'Path info passed into the method %s() must be an instance of %s.',
41-
__METHOD__,
42-
ImagePathInfoInterface::class,
43-
));
40+
throw new InvalidArgumentException(
41+
message: sprintf(
42+
'Path info passed into the method %s() must be an instance of %s.',
43+
__METHOD__,
44+
ImagePathInfoInterface::class,
45+
),
46+
);
4447
}
4548

46-
if (null === $pathInfo->getModifiers()) {
49+
if (null === $pathInfo->getModifiers() || [] === $pathInfo->getModifiers()) {
4750
$pathInfo = $pathInfo->withModifiers(['original' => true]);
4851
}
4952

@@ -53,8 +56,14 @@ public function link(FilePathInfoInterface $pathInfo, bool $absolute = true): st
5356
);
5457
}
5558

56-
public function srcSet(ImagePathInfoInterface $info, DescriptorInterface $descriptor, bool $absolute = true): SrcSet
59+
public function srcSet(ImagePathInfoInterface $info, ?DescriptorInterface $descriptor = null, bool $absolute = true): SrcSet
5760
{
61+
if (null === $descriptor) {
62+
$descriptor = $this->resolveDescriptor(
63+
pathInfo: $info,
64+
);
65+
}
66+
5867
if (null === $this->srcSetGenerator) {
5968
$this->srcSetGenerator = $this->srcSetGeneratorFactory->create($this, $this->modifierFacade);
6069
}
@@ -79,9 +88,38 @@ protected function buildQueryParams(FilePathInfoInterface $pathInfo): array
7988
$signatureParameterName = $this->config[Config::SIGNATURE_PARAMETER_NAME];
8089
assert(is_string($signatureParameterName));
8190

82-
$params[$signatureParameterName] = $this->signatureStrategy->createToken($pathInfo->getPath());
91+
$token = $this->signatureStrategy->createToken($pathInfo->getPath());
92+
93+
if (null !== $token) {
94+
$params[$signatureParameterName] = $token;
95+
}
8396
}
8497

8598
return $params;
8699
}
100+
101+
private function resolveDescriptor(ImagePathInfoInterface $pathInfo): DescriptorInterface
102+
{
103+
$modifiers = $pathInfo->getModifiers();
104+
105+
if (is_string($modifiers)) {
106+
$presets = $this->modifierFacade->getPresetCollection();
107+
$assigner = $this->config[Config::MODIFIER_ASSIGNER];
108+
$assigner = empty($assigner) ? ':' : $assigner;
109+
[$presetAlias] = explode($assigner, $modifiers, 2);
110+
$preset = $presets->get(presetAlias: $presetAlias);
111+
112+
if (null !== $preset->descriptor) {
113+
return $preset->descriptor;
114+
}
115+
}
116+
117+
throw new InvalidArgumentException(
118+
message: sprintf(
119+
'Unable to resolve descriptor for path info %s. Descriptor must be provided to the method %s::srcSet() manually.',
120+
$pathInfo,
121+
__CLASS__,
122+
),
123+
);
124+
}
87125
}

src/LinkGenerator/LinkGeneratorInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
interface LinkGeneratorInterface extends BaseLinkGeneratorInterface
1414
{
15-
public function srcSet(PathInfoInterface $info, DescriptorInterface $descriptor, bool $absolute = true): SrcSet;
15+
public function srcSet(PathInfoInterface $info, ?DescriptorInterface $descriptor = null, bool $absolute = true): SrcSet;
1616

1717
public function getSignatureStrategy(): ?SignatureStrategyInterface;
1818
}

src/Modifier/AbstractModifier.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract class AbstractModifier implements ModifierInterface
1111
{
1212
protected ?string $alias = null;
1313

14-
public function __construct(?string $alias = null)
14+
final public function __construct(?string $alias = null)
1515
{
1616
if (null !== $alias) {
1717
$this->alias = $alias;

0 commit comments

Comments
 (0)