初始上传

This commit is contained in:
2026-04-04 17:27:12 +08:00
parent 4d80d28eb4
commit b7e11774ee
11191 changed files with 1588469 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
<?php
namespace Intervention\Image\Commands;
use Intervention\Image\Commands\Argument;
abstract class AbstractCommand
{
/**
* Arguments of command
*
* @var array
*/
public $arguments;
/**
* Output of command
*
* @var mixed
*/
protected $output;
/**
* Executes current command on given image
*
* @param \Intervention\Image\Image $image
* @return mixed
*/
abstract public function execute($image);
/**
* Creates new command instance
*
* @param array $arguments
*/
public function __construct($arguments)
{
$this->arguments = $arguments;
}
/**
* Creates new argument instance from given argument key
*
* @param int $key
* @return \Intervention\Image\Commands\Argument
*/
public function argument($key)
{
return new Argument($this, $key);
}
/**
* Returns output data of current command
*
* @return mixed
*/
public function getOutput()
{
return $this->output ? $this->output : null;
}
/**
* Determines if current instance has output data
*
* @return boolean
*/
public function hasOutput()
{
return ! is_null($this->output);
}
/**
* Sets output data of current command
*
* @param mixed $value
*/
public function setOutput($value)
{
$this->output = $value;
}
}

View File

@@ -0,0 +1,225 @@
<?php
namespace Intervention\Image\Commands;
use Intervention\Image\Exception\InvalidArgumentException;
class Argument
{
/**
* Command with arguments
*
* @var AbstractCommand
*/
public $command;
/**
* Key of argument in array
*
* @var int
*/
public $key;
/**
* Creates new instance from given command and key
*
* @param AbstractCommand $command
* @param int $key
*/
public function __construct(AbstractCommand $command, $key = 0)
{
$this->command = $command;
$this->key = $key;
}
/**
* Returns name of current arguments command
*
* @return string
*/
public function getCommandName()
{
preg_match("/\\\\([\w]+)Command$/", get_class($this->command), $matches);
return isset($matches[1]) ? lcfirst($matches[1]).'()' : 'Method';
}
/**
* Returns value of current argument
*
* @param mixed $default
* @return mixed
*/
public function value($default = null)
{
$arguments = $this->command->arguments;
if (is_array($arguments)) {
return isset($arguments[$this->key]) ? $arguments[$this->key] : $default;
}
return $default;
}
/**
* Defines current argument as required
*
* @return \Intervention\Image\Commands\Argument
*/
public function required()
{
if ( ! array_key_exists($this->key, $this->command->arguments)) {
throw new InvalidArgumentException(
sprintf("Missing argument %d for %s", $this->key + 1, $this->getCommandName())
);
}
return $this;
}
/**
* Determines that current argument must be of given type
*
* @return \Intervention\Image\Commands\Argument
*/
public function type($type)
{
$valid = true;
$value = $this->value();
if ($value === null) {
return $this;
}
switch (strtolower($type)) {
case 'bool':
case 'boolean':
$valid = \is_bool($value);
$message = '%s accepts only boolean values as argument %d.';
break;
case 'int':
case 'integer':
$valid = \is_int($value);
$message = '%s accepts only integer values as argument %d.';
break;
case 'num':
case 'numeric':
$valid = is_numeric($value);
$message = '%s accepts only numeric values as argument %d.';
break;
case 'str':
case 'string':
$valid = \is_string($value);
$message = '%s accepts only string values as argument %d.';
break;
case 'array':
$valid = \is_array($value);
$message = '%s accepts only array as argument %d.';
break;
case 'closure':
$valid = is_a($value, '\Closure');
$message = '%s accepts only Closure as argument %d.';
break;
case 'digit':
$valid = $this->isDigit($value);
$message = '%s accepts only integer values as argument %d.';
break;
}
if (! $valid) {
$commandName = $this->getCommandName();
$argument = $this->key + 1;
if (isset($message)) {
$message = sprintf($message, $commandName, $argument);
} else {
$message = sprintf('Missing argument for %d.', $argument);
}
throw new InvalidArgumentException(
$message
);
}
return $this;
}
/**
* Determines that current argument value must be numeric between given values
*
* @return \Intervention\Image\Commands\Argument
*/
public function between($x, $y)
{
$value = $this->type('numeric')->value();
if (is_null($value)) {
return $this;
}
$alpha = min($x, $y);
$omega = max($x, $y);
if ($value < $alpha || $value > $omega) {
throw new InvalidArgumentException(
sprintf('Argument %d must be between %s and %s.', $this->key, $x, $y)
);
}
return $this;
}
/**
* Determines that current argument must be over a minimum value
*
* @return \Intervention\Image\Commands\Argument
*/
public function min($value)
{
$v = $this->type('numeric')->value();
if (is_null($v)) {
return $this;
}
if ($v < $value) {
throw new InvalidArgumentException(
sprintf('Argument %d must be at least %s.', $this->key, $value)
);
}
return $this;
}
/**
* Determines that current argument must be under a maxiumum value
*
* @return \Intervention\Image\Commands\Argument
*/
public function max($value)
{
$v = $this->type('numeric')->value();
if (is_null($v)) {
return $this;
}
if ($v > $value) {
throw new InvalidArgumentException(
sprintf('Argument %d may not be greater than %s.', $this->key, $value)
);
}
return $this;
}
/**
* Checks if value is "PHP" integer (120 but also 120.0)
*
* @param mixed $value
* @return boolean
*/
private function isDigit($value)
{
return is_numeric($value) ? intval($value) == $value : false;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Intervention\Image\Commands;
class ChecksumCommand extends AbstractCommand
{
/**
* Calculates checksum of given image
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$colors = [];
$size = $image->getSize();
for ($x=0; $x <= ($size->width-1); $x++) {
for ($y=0; $y <= ($size->height-1); $y++) {
$colors[] = $image->pickColor($x, $y, 'array');
}
}
$this->setOutput(md5(serialize($colors)));
return true;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Intervention\Image\Commands;
use Closure;
class CircleCommand extends AbstractCommand
{
/**
* Draw a circle centered on given image
*
* @param \Intervention\Image\image $image
* @return boolean
*/
public function execute($image)
{
$diameter = $this->argument(0)->type('numeric')->required()->value();
$x = $this->argument(1)->type('numeric')->required()->value();
$y = $this->argument(2)->type('numeric')->required()->value();
$callback = $this->argument(3)->type('closure')->value();
$circle_classname = sprintf('\Intervention\Image\%s\Shapes\CircleShape',
$image->getDriver()->getDriverName());
$circle = new $circle_classname($diameter);
if ($callback instanceof Closure) {
$callback($circle);
}
$circle->applyToImage($image, $x, $y);
return true;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Intervention\Image\Commands;
use Closure;
class EllipseCommand extends AbstractCommand
{
/**
* Draws ellipse on given image
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$width = $this->argument(0)->type('numeric')->required()->value();
$height = $this->argument(1)->type('numeric')->required()->value();
$x = $this->argument(2)->type('numeric')->required()->value();
$y = $this->argument(3)->type('numeric')->required()->value();
$callback = $this->argument(4)->type('closure')->value();
$ellipse_classname = sprintf('\Intervention\Image\%s\Shapes\EllipseShape',
$image->getDriver()->getDriverName());
$ellipse = new $ellipse_classname($width, $height);
if ($callback instanceof Closure) {
$callback($ellipse);
}
$ellipse->applyToImage($image, $x, $y);
return true;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Intervention\Image\Commands;
use Intervention\Image\Exception\NotReadableException;
use Intervention\Image\Exception\NotSupportedException;
class ExifCommand extends AbstractCommand
{
/**
* Read Exif data from the given image
*
* Note: Windows PHP Users - in order to use this method you will need to
* enable the mbstring and exif extensions within the php.ini file.
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
if (!function_exists('exif_read_data')) {
throw new NotSupportedException(
"Reading Exif data is not supported by this PHP installation."
);
}
$key = $this->argument(0)->value();
// try to read exif data from image file
try {
$data = @exif_read_data($image->dirname . '/' . $image->basename);
if (!is_null($key) && is_array($data)) {
$data = array_key_exists($key, $data) ? $data[$key] : false;
}
} catch (\Exception $e) {
throw new NotReadableException(
sprintf(
"Cannot read the Exif data from the filename (%s) provided ",
$image->dirname . '/' . $image->basename
),
$e->getCode(),
$e
);
}
$this->setOutput($data);
return true;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Intervention\Image\Commands;
use Intervention\Image\Exception\NotSupportedException;
class IptcCommand extends AbstractCommand
{
/**
* Read Iptc data from the given image
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
if ( ! function_exists('iptcparse')) {
throw new NotSupportedException(
"Reading Iptc data is not supported by this PHP installation."
);
}
$key = $this->argument(0)->value();
$info = [];
@getimagesize($image->dirname .'/'. $image->basename, $info);
$data = [];
if (array_key_exists('APP13', $info)) {
$iptc = iptcparse($info['APP13']);
if (is_array($iptc)) {
$data['DocumentTitle'] = isset($iptc["2#005"][0]) ? $iptc["2#005"][0] : null;
$data['Urgency'] = isset($iptc["2#010"][0]) ? $iptc["2#010"][0] : null;
$data['Category'] = isset($iptc["2#015"][0]) ? $iptc["2#015"][0] : null;
$data['Subcategories'] = isset($iptc["2#020"][0]) ? $iptc["2#020"][0] : null;
$data['Keywords'] = isset($iptc["2#025"][0]) ? $iptc["2#025"] : null;
$data['SpecialInstructions'] = isset($iptc["2#040"][0]) ? $iptc["2#040"][0] : null;
$data['CreationDate'] = isset($iptc["2#055"][0]) ? $iptc["2#055"][0] : null;
$data['CreationTime'] = isset($iptc["2#060"][0]) ? $iptc["2#060"][0] : null;
$data['AuthorByline'] = isset($iptc["2#080"][0]) ? $iptc["2#080"][0] : null;
$data['AuthorTitle'] = isset($iptc["2#085"][0]) ? $iptc["2#085"][0] : null;
$data['City'] = isset($iptc["2#090"][0]) ? $iptc["2#090"][0] : null;
$data['SubLocation'] = isset($iptc["2#092"][0]) ? $iptc["2#092"][0] : null;
$data['State'] = isset($iptc["2#095"][0]) ? $iptc["2#095"][0] : null;
$data['Country'] = isset($iptc["2#101"][0]) ? $iptc["2#101"][0] : null;
$data['OTR'] = isset($iptc["2#103"][0]) ? $iptc["2#103"][0] : null;
$data['Headline'] = isset($iptc["2#105"][0]) ? $iptc["2#105"][0] : null;
$data['Source'] = isset($iptc["2#110"][0]) ? $iptc["2#110"][0] : null;
$data['PhotoSource'] = isset($iptc["2#115"][0]) ? $iptc["2#115"][0] : null;
$data['Copyright'] = isset($iptc["2#116"][0]) ? $iptc["2#116"][0] : null;
$data['Caption'] = isset($iptc["2#120"][0]) ? $iptc["2#120"][0] : null;
$data['CaptionWriter'] = isset($iptc["2#122"][0]) ? $iptc["2#122"][0] : null;
}
}
if (! is_null($key) && is_array($data)) {
$data = array_key_exists($key, $data) ? $data[$key] : false;
}
$this->setOutput($data);
return true;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Intervention\Image\Commands;
use Closure;
class LineCommand extends AbstractCommand
{
/**
* Draws line on given image
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$x1 = $this->argument(0)->type('numeric')->required()->value();
$y1 = $this->argument(1)->type('numeric')->required()->value();
$x2 = $this->argument(2)->type('numeric')->required()->value();
$y2 = $this->argument(3)->type('numeric')->required()->value();
$callback = $this->argument(4)->type('closure')->value();
$line_classname = sprintf('\Intervention\Image\%s\Shapes\LineShape',
$image->getDriver()->getDriverName());
$line = new $line_classname($x2, $y2);
if ($callback instanceof Closure) {
$callback($line);
}
$line->applyToImage($image, $x1, $y1);
return true;
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Intervention\Image\Commands;
class OrientateCommand extends AbstractCommand
{
/**
* Correct image orientation according to Exif data
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
switch ($image->exif('Orientation')) {
case 2:
$image->flip();
break;
case 3:
$image->rotate(180);
break;
case 4:
$image->rotate(180)->flip();
break;
case 5:
$image->rotate(270)->flip();
break;
case 6:
$image->rotate(270);
break;
case 7:
$image->rotate(90)->flip();
break;
case 8:
$image->rotate(90);
break;
}
return true;
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Intervention\Image\Commands;
use Closure;
use Intervention\Image\Exception\InvalidArgumentException;
class PolygonCommand extends AbstractCommand
{
/**
* Draw a polygon on given image
*
* @param \Intervention\Image\image $image
* @return boolean
*/
public function execute($image)
{
$points = $this->argument(0)->type('array')->required()->value();
$callback = $this->argument(1)->type('closure')->value();
$vertices_count = count($points);
// check if number if coordinates is even
if ($vertices_count % 2 !== 0) {
throw new InvalidArgumentException(
"The number of given polygon vertices must be even."
);
}
if ($vertices_count < 6) {
throw new InvalidArgumentException(
"You must have at least 3 points in your array."
);
}
$polygon_classname = sprintf('\Intervention\Image\%s\Shapes\PolygonShape',
$image->getDriver()->getDriverName());
$polygon = new $polygon_classname($points);
if ($callback instanceof Closure) {
$callback($polygon);
}
$polygon->applyToImage($image);
return true;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Intervention\Image\Commands;
use GuzzleHttp\Psr7\Response;
class PsrResponseCommand extends AbstractCommand
{
/**
* Builds PSR7 compatible response. May replace "response" command in
* some future.
*
* Method will generate binary stream and put it inside PSR-7
* ResponseInterface. Following code can be optimized using native php
* streams and more "clean" streaming, however drivers has to be updated
* first.
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$format = $this->argument(0)->value();
$quality = $this->argument(1)->between(0, 100)->value();
//Encoded property will be populated at this moment
$stream = $image->stream($format, $quality);
$mimetype = finfo_buffer(
finfo_open(FILEINFO_MIME_TYPE),
$image->getEncoded()
);
$this->setOutput(new Response(
200,
[
'Content-Type' => $mimetype,
'Content-Length' => strlen($image->getEncoded())
],
$stream
));
return true;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Intervention\Image\Commands;
use Closure;
class RectangleCommand extends AbstractCommand
{
/**
* Draws rectangle on given image
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$x1 = $this->argument(0)->type('numeric')->required()->value();
$y1 = $this->argument(1)->type('numeric')->required()->value();
$x2 = $this->argument(2)->type('numeric')->required()->value();
$y2 = $this->argument(3)->type('numeric')->required()->value();
$callback = $this->argument(4)->type('closure')->value();
$rectangle_classname = sprintf('\Intervention\Image\%s\Shapes\RectangleShape',
$image->getDriver()->getDriverName());
$rectangle = new $rectangle_classname($x1, $y1, $x2, $y2);
if ($callback instanceof Closure) {
$callback($rectangle);
}
$rectangle->applyToImage($image, $x1, $y1);
return true;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Intervention\Image\Commands;
use Intervention\Image\Response;
class ResponseCommand extends AbstractCommand
{
/**
* Builds HTTP response from given image
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$format = $this->argument(0)->value();
$quality = $this->argument(1)->between(0, 100)->value();
$response = new Response($image, $format, $quality);
$this->setOutput($response->make());
return true;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Intervention\Image\Commands;
class StreamCommand extends AbstractCommand
{
/**
* Builds PSR7 stream based on image data. Method uses Guzzle PSR7
* implementation as easiest choice.
*
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$format = $this->argument(0)->value();
$quality = $this->argument(1)->between(0, 100)->value();
$this->setOutput(\GuzzleHttp\Psr7\stream_for(
$image->encode($format, $quality)->getEncoded()
));
return true;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Intervention\Image\Commands;
use Closure;
class TextCommand extends AbstractCommand
{
/**
* Write text on given image
* @param \Intervention\Image\Image $image
* @return boolean
*/
public function execute($image)
{
$text = $this->argument(0)->required()->value();
$x = $this->argument(1)->type('numeric')->value(0);
$y = $this->argument(2)->type('numeric')->value(0);
$callback = $this->argument(3)->type('closure')->value();
$fontclassname = sprintf('\Intervention\Image\%s\Font',
$image->getDriver()->getDriverName());
$font = new $fontclassname($text);
if ($callback instanceof Closure) {
$callback($font);
}
$font->applyToImage($image, $x, $y);
return true;
}
}