tobento / service-picture
Picture support for PHP applications.
Requires
- php: >=8.0
- league/mime-type-detection: ^1.15
- psr/log: ^1.1 || ^2.0 || ^3.0
- tobento/service-dir: ^1.0
- tobento/service-filesystem: ^1.0
- tobento/service-imager: ^1.0
- tobento/service-tag: ^1.0.5
Requires (Dev)
- guzzlehttp/psr7: ^2.7
- monolog/monolog: ^2.3
- phpunit/phpunit: ^9.5
- vimeo/psalm: ^4.0
README
The Picture Service provides interfaces for creating and rendering a picture for PHP applications. It comes with a default implementation but you may create specific implementation to fit your requirements.
Table of Contents
Getting started
Add the latest version of the picture service project running this command.
composer require tobento/service-picture
Requirements
- PHP 8.0 or greater
Highlights
- Framework-agnostic, will work with any project
- Decoupled design
Documentation
Picture Creator
The picture creator creates images based on the given definition and returns a Created Picture holding the images to be stored wherever you like.
Create Picture Creator
The picture creator uses the Imager Service to create images.
use Tobento\Service\Imager\InterventionImage\ImagerFactory; use Tobento\Service\Picture\PictureCreator; $pictureCreator = new PictureCreator( imager: (new ImagerFactory())->createImager(), );
In Detail
use Psr\Log\LoggerInterface; use Tobento\Service\Imager\Action; use Tobento\Service\Imager\InterventionImage\ImagerFactory; use Tobento\Service\Picture\Exception\ResourceSizeException; use Tobento\Service\Picture\PictureCreator; use Tobento\Service\Picture\PictureCreatorInterface; $pictureCreator = new PictureCreator( imager: (new ImagerFactory())->createImager(), // You may define imager actions which are allowed only. // If empty array all are allowed if not in disallowedActions. allowedActions: [ Action\Greyscale::class, ], // You may define imager actions which are not allowed and will be skipped: disallowedActions: [ Action\Colorize::class, ], // You may adjust the supported mime types: supportedMimeTypes: [ 'image/png', 'image/jpeg', 'image/gif', 'image/webp', ], // You may set an upsize limit for resizing images: upsize: 1.2, // default is null (unlimited) // You may skip smaller sized images (except img src) // from generating at all to prevent unsharp images. skipSmallerSizedSrc: true, // false is default // You may verify sizes. If one of the defined image size // is too small, a ResourceSizeException will be thrown. verifySizes: true, // false is default // You may pass a logger for debugging: logger: null, // LoggerInterface ); var_dump($pictureCreator instanceof PictureCreatorInterface); // bool(true)
Picture Creating
Create Picture From Stream
Use the createFromStream
method to create a picture from a PSR-7 stream:
use Psr\Http\Message\StreamInterface; use Tobento\Service\Picture\CreatedPictureInterface; use Tobento\Service\Picture\DefinitionInterface; use Tobento\Service\Picture\PictureInterface; $createdPicture = $pictureCreator->createFromStream( stream: $stream, // StreamInterface definition: $definition, // DefinitionInterface ); var_dump($createdPicture instanceof CreatedPictureInterface); // bool(true) var_dump($createdPicture instanceof PictureInterface); // bool(true)
Check out the Definition section to learn more about definitions.
You may check out the Picture section to learn more about the default PictureInterface::class
implementation.
Create Picture From Resource
Use the createFromResource
method to create a picture from any resource implementing the ResourceInterface
interface:
use Tobento\Service\Imager\ResourceInterface; use Tobento\Service\Picture\CreatedPictureInterface; use Tobento\Service\Picture\DefinitionInterface; use Tobento\Service\Picture\PictureInterface; $createdPicture = $pictureCreator->createFromResource( resource: $resource, // ResourceInterface definition: $definition, // DefinitionInterface ); var_dump($createdPicture instanceof CreatedPictureInterface); // bool(true) var_dump($createdPicture instanceof PictureInterface); // bool(true)
Check out the Imager Service - Resource section to learn more about it.
Check out the Definition section to learn more about definitions.
You may check out the Picture section to learn more about the default PictureInterface::class
implementation.
Supported Resources
Created Picture
The created picture holds all created images which you can use to store the images in the way you need it.
use Tobento\Service\Imager\Response\Encoded; use Tobento\Service\Picture\CreatedPictureInterface; use Tobento\Service\Picture\PictureInterface; var_dump($createdPicture instanceof CreatedPictureInterface); // bool(true) var_dump($createdPicture instanceof PictureInterface); // bool(true) foreach($createdPicture->srces() as $src) { var_dump($src instanceof SrcInterface); // bool(true) // The encoded image which was created: var_dump($src->encoded() instanceof Encoded); // bool(true) }
Check out the Encoded Response to learn more about it.
Definition
Array Definition
The array definition is the default picture definition.
use Tobento\Service\Picture\Definition\ArrayDefinition; use Tobento\Service\Picture\DefinitionInterface; use Tobento\Service\Picture\PictureInterface; use Tobento\Service\Picture\Src; $definition = new ArrayDefinition(name: 'product:main', definition: [ 'img' => [ 'src' => [600, 600], // [width, height] // or width only, height will be resized keeping ratio 'src' => [600], // or height only, width will be resized keeping ratio 'src' => [null, 600], // or 'src' => [null, null], // keeps resource dimensions. // or using Src class 'src' => new Src( width: 600, // or null height: 600, // or null // you may set a mime type to convert to: mimeType: 'image/webp', // you may define quality and actions: options: ['quality' => 90, 'actions' => ['crop' => [300, 200, 10, 15]]] ), // img attributes: 'alt' => 'Alternative Text', 'class' => 'foo', 'data-foo' => 'foo', 'sizes' => '(max-width: 600px) 480px, 800px', 'loading' => 'lazy', 'srcset' => [ '480w' => [480], // same definition as img src '800w' => [800], ], ], // You may define any sources: 'sources' => [ [ 'media' => '(min-width: 800px)', 'srcset' => [ '' => [1200, 600], ], 'type' => 'image/webp', ], [ 'media' => '(max-width: 600px)', 'srcset' => [ '' => [600, 600], ], 'type' => 'image/webp', 'width' => '600', 'height' => '600', ], ], // Attributes for the picture tag: 'attributes' => [ 'class' => 'foo', ], // Options: 'options' => [ // You may define a global quality if not specified individually with the Src class: 'quality' => [ 'image/jpeg' => 70, 'image/webp' => 70, ], // You may convert all png to jpeg types if not defined individually: 'convert' => ['image/png' => 'image/jpeg'], // You may define imager actions used if not specified individually with the Src class: 'actions' => [ 'greyscale' => [], 'gamma' => ['gamma' => 5.5], ], ], ]); var_dump($definition instanceof DefinitionInterface); // bool(true) // Definition to picture: var_dump($definition->toPicture() instanceof PictureInterface); // bool(true)
Example with pixel density descriptors:
use Tobento\Service\Picture\Definition\ArrayDefinition; $definition = new ArrayDefinition(name: 'logo', definition: [ 'img' => [ 'src' => [250], 'srcset' => [ '' => [250], '2x' => [500], ], ], ]);
Picture Definition
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\Definition\PictureDefinition; use Tobento\Service\Picture\DefinitionInterface; use Tobento\Service\Picture\Picture; use Tobento\Service\Picture\PictureInterface; use Tobento\Service\Picture\Source; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\Src; use Tobento\Service\Picture\Srcset; $definition = new PictureDefinition( name: 'product', picture: new Picture( img: new Img( src: new Src( width: 600, // or null height: 600, // or null // you may set a mime type to convert to: mimeType: 'image/webp', // you may define quality and actions: options: ['quality' => 90, 'actions' => ['crop' => [300, 200, 10, 15]]], ), // you may define a srcset: srcset: new Srcset( new Src(width: 480, descriptor: '480w'), new Src(width: 800, descriptor: '800w'), ), attributes: [ 'sizes' => '(max-width: 600px) 480px, 800px', 'loading' => 'lazy', 'class' => 'foo', ], ), sources: new Sources( new Source( srcset: new Srcset( new Src(width: 1200, height: 600), ), attributes: [ 'media' => '(min-width: 800px)', 'type' => 'image/webp', ], ), new Source( srcset: new Srcset( new Src(width: 600, height: 600), ), attributes: [ 'media' => '(max-width: 600px)', 'type' => 'image/webp', ], ), ), attributes: [ 'class' => 'foo', ], options: [ // You may define a global quality if not specified individually with the Src class: 'quality' => [ 'image/jpeg' => 70, 'image/webp' => 70, ], // You may convert all png to jpeg types if not defined individually: 'convert' => ['image/png' => 'image/jpeg'], // You may define imager actions used if not specified individually with the Src class: 'actions' => [ 'greyscale' => [], 'gamma' => ['gamma' => 5.5], ], ], ) ); var_dump($definition instanceof DefinitionInterface); // bool(true) // Definition to picture: var_dump($definition->toPicture() instanceof PictureInterface); // bool(true)
Definitions
You may use the following definitions classes to add, filter and get definitions from.
Default Definitions
use Tobento\Service\Picture\Definition\ArrayDefinition; use Tobento\Service\Picture\DefinitionInterface; use Tobento\Service\Picture\Definitions\Definitions; use Tobento\Service\Picture\DefinitionsInterface; $definitions = new Definitions( 'product', // a definitions name new ArrayDefinition('product-main', ['img' => ['src' => [50]]]), // ... DefinitionInterface ); var_dump($definitions instanceof DefinitionsInterface); // bool(true)
Json Files Definitions
The JsonFilesDefinitions
class loads definitions from JSON files within the given directories.
use Tobento\Service\Dir\Dir; use Tobento\Service\Dir\Dirs; use Tobento\Service\Picture\Definition\ArrayDefinition; use Tobento\Service\Picture\DefinitionInterface; use Tobento\Service\Picture\Definitions\JsonFilesDefinitions; use Tobento\Service\Picture\DefinitionsInterface; $definitions = new JsonFilesDefinitions( name: 'product', // a definitions name dirs: new Dirs(new Dir('dir/private/picture-definitions/')) ); var_dump($definitions instanceof DefinitionsInterface); // bool(true)
Example Directory:
private/
picture-definitions/
product.json
...
Example Json Definition:
{
"img": {
"src": {
"width": 300,
"height": null,
"descriptor": null,
"mimeType": "image/png",
"url": null,
"path": null,
"options": []
},
"srcset": [
{
"width": 200
}
],
"attributes": {
"key": "value"
}
},
"sources": [
{
"srcset": [
{
"width": 200,
"height": null,
"descriptor": null,
"url": null,
"path": null,
"options": []
}
],
"attributes": {
"key": "value"
}
}
],
"attributes": {
"key": "value"
},
"options": {
"key": "value"
}
}
Stack Definitions
use Tobento\Service\Picture\Definition\ArrayDefinition; use Tobento\Service\Picture\Definitions\Definitions; use Tobento\Service\Picture\Definitions\StackDefinitions; use Tobento\Service\Picture\DefinitionsInterface; $definitions = new StackDefinitions( 'name', // a definitions name new Definitions('product', new ArrayDefinition('product-main', ['img' => ['src' => [50]]])), // ... DefinitionsInterface ); var_dump($definitions instanceof DefinitionsInterface); // bool(true)
Picture
The picture class may be used to create, store or render the picture.
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\ImgInterface; use Tobento\Service\Picture\Picture; use Tobento\Service\Picture\PictureInterface; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\SourcesInterface; use Tobento\Service\Picture\Src; $picture = new Picture( img: new Img( src: new Src(), ), sources: new Sources(), attributes: [], options: [], ); var_dump($picture instanceof PictureInterface); // bool(true) // returns the img: var_dump($picture->img() instanceof ImgInterface); // bool(true) // returns a new instance with the given img: $picture = $picture->withImg(new Img(new Src())); // returns the sources: var_dump($picture->sources() instanceof SourcesInterface); // bool(true) // returns a new instance with the given sources: $picture = $picture->withSources(new Sources()); // returns the attributes: $attributes = $picture->attributes(); // returns a new instance with the given attributes: $picture = $picture->withAttributes([]); // returns the options: $options = $picture->options(); // returns a new instance with the given options: $picture = $picture->withOptions([]);
Srces
The srces
method returns a Generator with all collected Src
classes:
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\Picture; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\Src; use Tobento\Service\Picture\SrcInterface; $picture = new Picture( img: new Img( src: new Src(), ), sources: new Sources(), ); foreach($picture->srces() as $src) { var_dump($src instanceof SrcInterface); // bool(true) }
toTag
The toTag
method returns a new created picture tag.
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\Picture; use Tobento\Service\Picture\PictureTagInterface; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\Src; $picture = new Picture( img: new Img( src: new Src(), ), sources: new Sources(), ); var_dump($picture->toTag() instanceof PictureTagInterface); // bool(true)
Check out the Picture Tag section to learn more about the default PictureTagInterface::class
.
jsonSerialize
The jsonSerialize
method serializes the object to a value that can be serialized natively by json_encode()
.
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\Picture; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\Src; $picture = new Picture( img: new Img( src: new Src(), ), sources: new Sources(), ); $jsonSerialized = $picture->jsonSerialize();
Img
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\ImgInterface; use Tobento\Service\Picture\Src; use Tobento\Service\Picture\SrcInterface; use Tobento\Service\Picture\Srcset; use Tobento\Service\Picture\SrcsetInterface; $img = new Img( src: new Src(), // SrcInterface srcset: new Srcset(), // null|SrcsetInterface attributes: [], ); var_dump($img instanceof ImgInterface); // bool(true) // returns the src: var_dump($img->src() instanceof SrcInterface); // bool(true) // returns a new instance with the given src: $img = $img->withSrc(new Src()); // returns the srcset or null if none: var_dump($img->srcset() instanceof SrcsetInterface); // bool(true) // returns a new instance with the given srcset: $img = $img->withSrcset(new Srcset()); // returns the attributes: $attributes = $img->attributes(); // returns a new instance with the given attributes: $img = $img->withAttributes([]); // json serialize: $jsonSerialized = $img->jsonSerialize();
Sources
use Tobento\Service\Picture\Source; use Tobento\Service\Picture\SourceInterface; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\SourcesInterface; use Tobento\Service\Picture\Srcset; use Tobento\Service\Picture\SrcInterface; $sources = new Sources( new Source(new Srcset()), new Source(new Srcset()), ); var_dump($sources instanceof SourcesInterface); // bool(true) // iterate sources: foreach($sources as $source) { var_dump($source instanceof SourceInterface); // bool(true) } // or foreach($sources->all() as $source) {} // count sources: var_dump($sources->count()); // int(2) // returns all collected Src classes: foreach($sources->srces() as $src) { var_dump($src instanceof SrcInterface); // bool(true) } // json serialize: $jsonSerialized = $sources->jsonSerialize();
Source
use Tobento\Service\Picture\Source; use Tobento\Service\Picture\SourceInterface; use Tobento\Service\Picture\Srcset; use Tobento\Service\Picture\SrcsetInterface; $source = new Source( srcset: new Srcset(), attributes: [] ); var_dump($source instanceof SourceInterface); // bool(true) // returns the srcset or null if none: var_dump($source->srcset() instanceof SrcsetInterface); // bool(true) // returns a new instance with the given srcset: $source = $source->withSrcset(new Srcset()); // returns the attributes: $attributes = $source->attributes(); // returns a new instance with the given attributes: $source = $source->withAttributes([]); // json serialize: $jsonSerialized = $source->jsonSerialize();
Srcset
use Tobento\Service\Picture\Src; use Tobento\Service\Picture\SrcInterface; use Tobento\Service\Picture\Srcset; use Tobento\Service\Picture\SrcsetInterface; $srcset = new Srcset( new Src(width: 200, descriptor: '1x'), new Src(width: 400, descriptor: '2x'), ); var_dump($srcset instanceof SrcsetInterface); // bool(true) // iterate srces: foreach($srcset as $src) { var_dump($src instanceof SrcInterface); // bool(true) } // or foreach($srcset->all() as $src) {} // count srces: var_dump($srcset->count()); // int(2) // json serialize: $jsonSerialized = $srcset->jsonSerialize();
Src
use Tobento\Service\Imager\Response\Encoded; use Tobento\Service\Picture\Src; use Tobento\Service\Picture\SrcInterface; $src = new Src( width: 200, // null|int height: 200, // null|int descriptor: '200w', // null|string, may be used for srcset mimeType: 'image/jpeg', // null|string url: null, // null|string path: null, // null|string encoded: null, // null|Encoded options: [], ); var_dump($src instanceof SrcInterface); // bool(true) // returns the width or null if none: var_dump($src->width()); // int(200) // returns a new instance with the given width: $src = $src->withWidth(300); // or null // returns the height or null if none: var_dump($src->height()); // int(200) // returns a new instance with the given height: $src = $src->withHeight(300); // or null // returns the descriptor or null if none: var_dump($src->descriptor()); // string(4) "200w" // returns a new instance with the given descriptor: $src = $src->withDescriptor('1x'); // or null // returns the mimeType or null if none: var_dump($src->mimeType()); // string(10) "image/jpeg" // returns a new instance with the given mimeType: $src = $src->withMimeType('image/gif'); // or null // returns the url or null if none: var_dump($src->url()); // NULL or string // returns a new instance with the given url: $src = $src->withUrl('https://example.com/image.jpg'); // or null // returns the path or null if none: var_dump($src->path()); // NULL or string // returns a new instance with the given path: $src = $src->withPath('path/image.jpg'); // or null // returns the encoded or null if none: var_dump($src->encoded()); // NULL or Encoded // returns a new instance with the given encoded: $src = $src->withEncoded(null); // or Encoded // returns the options: var_dump($src->options()); // array(0) { } // returns a new instance with the given options: $src = $src->withOptions([]); // json serialize: $jsonSerialized = $src->jsonSerialize();
Picture Factory
The picture factory may be used to create a picture.
use Tobento\Service\Picture\PictureFactory; use Tobento\Service\Picture\PictureFactoryInterface; $factory = new PictureFactory( // you may throw exceptions if an error occurs: throwOnError: true, // false is default ); var_dump($factory instanceof PictureFactoryInterface); // bool(true)
Create From Array
Use the createFromArray
method to create a picture from the given array:
use Tobento\Service\Picture\PictureFactory; use Tobento\Service\Picture\PictureInterface; $picture = (new PictureFactory())->createFromArray([ 'img' => [ 'src' => [ 'width' => 300, 'height' => null, 'descriptor' => '2x', // or null 'mimeType' => 'image/jpeg', // or null 'url' => null, 'path' => null, 'options' => [], ], 'srcset' => [ ['width' => 200], ], 'attributes' => ['key' => 'value'], ], 'sources' => [ [ 'srcset' => [ [ 'width' => 200, 'height' => null, 'descriptor' => null, 'url' => null, 'path' => null, 'options' => [], ], ], 'attributes' => ['key' => 'value'] ], ], 'attributes' => ['key' => 'value'], 'options' => ['key' => 'value'], ]); var_dump($picture instanceof PictureInterface); // bool(true)
Picture Tag Factory
The picture tag factory may be used to create picture tags.
use Tobento\Service\Picture\PictureTagFactory; use Tobento\Service\Picture\PictureTagFactoryInterface; $factory = new PictureTagFactory(); var_dump($factory instanceof PictureTagFactoryInterface); // bool(true)
Create From Picture
Use the createFromPicture
method to create a picture tag from the given picture:
use Tobento\Service\Picture\Img; use Tobento\Service\Picture\Picture; use Tobento\Service\Picture\PictureTagFactory; use Tobento\Service\Picture\PictureTagInterface; use Tobento\Service\Picture\Sources; use Tobento\Service\Picture\Src; $picture = new Picture( img: new Img( src: new Src(url: 'https://example.com/image.jpg'), ), sources: new Sources(), ); $pictureTag = (new PictureTagFactory())->createFromPicture($picture); var_dump($pictureTag instanceof PictureTagInterface); // bool(true)
Picture Tag
The picture tag renders the picture html tag.
use Tobento\Service\Picture\PictureTag; use Tobento\Service\Picture\PictureTagInterface; use Tobento\Service\Tag\Attributes; use Tobento\Service\Tag\Tag; use Tobento\Service\Tag\TagInterface; $picture = new PictureTag( new Tag(name: 'picture'), new Tag(name: 'img', attributes: new Attributes(['src' => 'image.jpg'])), new Tag(name: 'source', attributes: new Attributes(['srcset' => 'image.webp', 'type' => 'image/webp'])), new Tag(name: 'source', attributes: new Attributes(['srcset' => 'image.avif', 'type' => 'image/avif'])), ); var_dump($picture instanceof PictureTagInterface); // bool(true) var_dump($picture instanceof \Stringable); // bool(true) // returns the "picture" tag: var_dump($picture->tag() instanceof TagInterface); // bool(true) // returns a new instance with the given "picture" tag: $picture = $picture->withTag(new Tag('picture')); // returns the "img" tag: var_dump($picture->img() instanceof TagInterface); // bool(true) // returns a new instance with the given "img" tag: $picture = $picture->withImg(new Tag('img')); // returns the "source" tags: foreach($picture->sources() as $source) { var_dump($source instanceof TagInterface); // bool(true) } // returns a new instance with the given "source" tags: $picture = $picture->withSources( new Tag(name: 'source', attributes: new Attributes(['srcset' => 'image.webp', 'type' => 'image/webp'])), new Tag(name: 'source', attributes: new Attributes(['srcset' => 'image.avif', 'type' => 'image/avif'])), ); // returns a new instance with the given "picture" attribute: $picture = $picture->attr(name: 'data-foo', value: 'Foo'); // returns a new instance with the given "img" attribute: $picture = $picture->imgAttr(name: 'data-bar', value: 'Bar');
You may check out the Tag Service - Tag Interface section to learn more about the TagInterface::class
.
Render Picture
<?= $picture->render() ?> // or just <?= $picture ?>
Null Picture Tag
The null picture tag does not render any content at all and may be useful in some sitations.
use Tobento\Service\Picture\PictureTagInterface; use Tobento\Service\Picture\NullPictureTag; $picture = new NullPictureTag(); var_dump($picture instanceof PictureTagInterface); // bool(true) var_dump((string)$picture); // string(0) ""