Skip to content

Commit

Permalink
Added ImageManipulator class
Browse files Browse the repository at this point in the history
Uses the Intervention Image library to create variants of media
  • Loading branch information
frasmage committed Oct 3, 2020
1 parent d6a880c commit 35607b4
Show file tree
Hide file tree
Showing 21 changed files with 1,190 additions and 262 deletions.
32 changes: 16 additions & 16 deletions phpunit.xml
@@ -1,27 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
executionOrder="random"
resolveDependencies="true"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix="Test.php">./tests/integration/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
<php>
<ini name="display_errors" value="true"/>
</php>
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">./src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix="Test.php">./tests/Integration/</directory>
</testsuite>
</testsuites>
<php>
<ini name="display_errors" value="true"/>
</php>
</phpunit>
28 changes: 28 additions & 0 deletions src/Exceptions/ImageManipulationException.php
@@ -0,0 +1,28 @@
<?php


namespace Plank\Mediable\Exceptions;

class ImageManipulationException extends \Exception
{
public static function invalidMediaType(?string $type): self
{
return new self(
"Cannot manipulate media with an aggregate type other than 'image', got '{$type}'."
);
}

public static function unknownVariant(string $variantName): self
{
return new self(
"Unknown variant '{$variantName}'."
);
}

public static function unknownOutputFormat(): self
{
return new self(
"Unable to determine valid output format for file."
);
}
}
177 changes: 177 additions & 0 deletions src/ImageManipulation.php
@@ -0,0 +1,177 @@
<?php

namespace Plank\Mediable;

class ImageManipulation
{
public const FORMAT_JPG = 'jpg';
public const FORMAT_PNG = 'png';
public const FORMAT_GIF = 'gif';
public const FORMAT_TIFF = 'tif';
public const FORMAT_BMP = 'bmp';
public const FORMAT_WEBP = 'webp';

public const VALID_IMAGE_FORMATS = [
self::FORMAT_JPG,
self::FORMAT_PNG,
self::FORMAT_GIF,
self::FORMAT_TIFF,
self::FORMAT_BMP
];

public const MIME_TYPE_MAP = [
self::FORMAT_JPG => 'image/jpeg',
self::FORMAT_PNG => 'image/png',
self::FORMAT_GIF => 'image/gif',
self::FORMAT_TIFF => 'image/tiff',
self::FORMAT_BMP => 'image/bmp',
self::FORMAT_WEBP => 'image/webp'
];

/** @var callable */
private $callback;

/** @var string|null */
private $outputFormat;

/** @var int */
private $outputQuality = 90;

/** @var callable|null */
private $beforeSave;

public function __construct(callable $callback)
{
$this->callback = $callback;
}

public static function make(callable $callback)
{
return new self($callback);
}

/**
* @return \Closure
*/
public function getCallback(): \Closure
{
return $this->callback;
}

/**
* @return int
*/
public function getOutputQuality(): int
{
return $this->outputQuality;
}

/**
* @param int $outputQuality
* @return $this
*/
public function setOutputQuality(int $outputQuality): self
{
$this->outputQuality = min(100, max(0, $outputQuality));

return $this;
}

/**
* @return string|null
*/
public function getOutputFormat(): ?string
{
return $this->outputFormat;
}

/**
* @param string|null $outputFormat
* @return $this
*/
public function setOutputFormat(?string $outputFormat): self
{
$this->outputFormat = $outputFormat;

return $this;
}

/**
* @return $this
*/
public function toJpegFormat(): self
{
$this->setOutputFormat(self::FORMAT_JPG);

return $this;
}

/**
* @return $this
*/
public function toPngFormat(): self
{
$this->setOutputFormat(self::FORMAT_PNG);

return $this;
}

/**
* @return $this
*/
public function toGifFormat(): self
{
$this->setOutputFormat(self::FORMAT_GIF);

return $this;
}

/**
* @return $this
*/
public function toTiffFormat(): self
{
$this->setOutputFormat(self::FORMAT_TIFF);

return $this;
}

/**
* @return $this
*/
public function toBmpFormat(): self
{
$this->setOutputFormat(self::FORMAT_BMP);

return $this;
}

/**
* @return $this
*/
public function toWebpFormat(): self
{
$this->setOutputFormat(self::FORMAT_WEBP);

return $this;
}

/**
* @return callable
*/
public function getBeforeSave(): ?callable
{
return $this->beforeSave;
}

/**
* @param callable $beforeSave
* @return $this
*/
public function beforeSave(callable $beforeSave): self
{
$this->beforeSave = $beforeSave;

return $this;
}
}
142 changes: 142 additions & 0 deletions src/ImageManipulator.php
@@ -0,0 +1,142 @@
<?php

namespace Plank\Mediable;

use Illuminate\Filesystem\FilesystemManager;
use Intervention\Image\ImageManager;
use Plank\Mediable\Exceptions\ImageManipulationException;
use Psr\Http\Message\StreamInterface;

class ImageManipulator
{
/**
* @var ImageManager
*/
private $imageManager;

/**
* @var ImageManipulation[]
*/
private $variantManipulations = [];

/**
* @var FilesystemManager
*/
private $filesystem;

public function __construct(ImageManager $imageManager, FilesystemManager $filesystem)
{
$this->imageManager = $imageManager;
$this->filesystem = $filesystem;
}

public function addVariantManipulation(
string $variantName,
ImageManipulation $manipulation
) {
$this->variantManipulations[$variantName] = $manipulation;
}

/**
* @param ImageManipulation $manipulation
* @param Media $media
* @return StreamInterface
* @throws ImageManipulationException
*/
public function createVariant(string $variantName, Media $media): Media
{
if ($media->aggregate_type != Media::TYPE_IMAGE) {
throw ImageManipulationException::invalidMediaType($media->aggregate_type);
}

$manipulation = $this->getVariantManipulation($variantName);

$outputFormat = $this->determineOutputFormat($manipulation, $media);
$image = $this->imageManager->make($media->stream());

$callback = $manipulation->getCallback();
$callback($image);

$outputStream = $image->stream(
$outputFormat,
$manipulation->getOutputQuality()
);

$modelClass = config('mediable.model');
/** @var Media $newMedia */
$newMedia = new $modelClass();
$newMedia->disk = $media->disk;
$newMedia->directory = $media->directory;
$newMedia->filename = sprintf('%s-%s', $media->filename, $variantName);
$newMedia->extension = $outputFormat;
$newMedia->mime_type = $this->getMimeTypeForOutputFormat($outputFormat);
$newMedia->aggregate_type = Media::TYPE_IMAGE;
$newMedia->size = $outputStream->getSize();

if ($beforeSave = $manipulation->getBeforeSave()) {
$beforeSave($newMedia);
}

$this->filesystem->disk($newMedia->disk)
->writeStream($newMedia->getDiskPath(), $outputStream->detach());

$newMedia->save();

return $newMedia;
}

/**
* @param string $variantName
* @return ImageManipulation
* @throws ImageManipulationException
*/
private function getVariantManipulation(string $variantName): ImageManipulation
{
if (isset($this->variantManipulations[$variantName])) {
return $this->variantManipulations[$variantName];
}

throw ImageManipulationException::unknownVariant($variantName);
}

private function getMimeTypeForOutputFormat(string $outputFormat): string
{
return ImageManipulation::MIME_TYPE_MAP[$outputFormat];
}

/**
* @param ImageManipulation $manipulation
* @param Media $media
* @return string
* @throws ImageManipulationException
*/
private function determineOutputFormat(
ImageManipulation $manipulation,
Media $media
): string {
if ($format = $manipulation->getOutputFormat()) {
return $format;
}

// attempt to infer the format from the mime type
$mime = strtolower($media->mime_type);
$format = array_search($mime, ImageManipulation::MIME_TYPE_MAP);
if ($format !== false) {
return $format;
}

// attempt to infer the format from the file extension
$extension = strtolower($media->extension);
if (in_array($extension, ImageManipulation::VALID_IMAGE_FORMATS)) {
return $extension;
}
if ($extension === 'jpeg') {
return ImageManipulation::FORMAT_JPG;
}
if ($extension === 'tiff') {
return ImageManipulation::FORMAT_TIFF;
}

throw ImageManipulationException::unknownOutputFormat();
}
}

0 comments on commit 35607b4

Please sign in to comment.