mirror of
https://github.com/ivuorinen/palette.git
synced 2026-01-26 03:24:08 +00:00
feat: php 8.3, phpunit 11 (#57)
* Upgrade runner php to 8.3, phpunit to 11 * Fix tests, tweaked the code, added missing ext- to composer.json * Fix exception related code smells
This commit is contained in:
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
12
.github/workflows/php.yml
vendored
12
.github/workflows/php.yml
vendored
@@ -2,21 +2,24 @@ name: PHP Composer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: ["master"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: shivammathur/setup-php@1a18b2267f80291a81ca1d33e7c851fe09e7dfc4
|
||||
with:
|
||||
php-version: "8.3"
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate --strict
|
||||
|
||||
@@ -32,8 +35,5 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress
|
||||
|
||||
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
|
||||
# Docs: https://getcomposer.org/doc/articles/scripts.md
|
||||
|
||||
- name: Run test suite
|
||||
run: composer run-script test
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
language: php
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 8.3
|
||||
- nightly
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 5.6
|
||||
- php: nightly
|
||||
fast_finish: true
|
||||
cache:
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"name": "ivuorinen/palette",
|
||||
"description": "Get most used colors from an image",
|
||||
"keywords": ["image", "colors", "palette", "psr-2"],
|
||||
"keywords": [
|
||||
"image",
|
||||
"colors",
|
||||
"palette",
|
||||
"psr-2"
|
||||
],
|
||||
"homepage": "https://github.com/ivuorinen/palette",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
@@ -16,22 +21,27 @@
|
||||
"issues": "https://github.com/ivuorinen/palette/issues"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "ivuorinen\\Palette\\": "src/" },
|
||||
"classmap": ["src/"]
|
||||
"psr-4": {
|
||||
"ivuorinen\\Palette\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-0": {
|
||||
"psr-4": {
|
||||
"ivuorinen\\Palette\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6.0"
|
||||
"php": "^8.3",
|
||||
"ext-gd": "*",
|
||||
"ext-exif": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "10.5.10",
|
||||
"squizlabs/php_codesniffer": "3.9.0"
|
||||
"phpunit/phpunit": "^11",
|
||||
"squizlabs/php_codesniffer": "^3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit"
|
||||
"test": "vendor/bin/phpunit",
|
||||
"lint": "vendor/bin/phpcs",
|
||||
"lint-fix": "vendor/bin/phpcbf"
|
||||
}
|
||||
}
|
||||
|
||||
12
phpunit.xml
12
phpunit.xml
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" colors="true" processIsolation="false" stopOnFailure="false" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>./src/</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" colors="true" processIsolation="false" stopOnFailure="false" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
|
||||
<testsuites>
|
||||
<testsuite name="Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>./src/</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
||||
14
src/Exceptions/GenericException.php
Normal file
14
src/Exceptions/GenericException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace ivuorinen\Palette\Exceptions;
|
||||
|
||||
use ErrorException;
|
||||
use Throwable;
|
||||
|
||||
class GenericException extends ErrorException
|
||||
{
|
||||
public function __construct(string $message, int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Palette
|
||||
* Parses image and returns most used colors
|
||||
@@ -36,6 +37,9 @@
|
||||
|
||||
namespace ivuorinen\Palette;
|
||||
|
||||
use GdImage;
|
||||
use ivuorinen\Palette\Exceptions\GenericException;
|
||||
|
||||
/**
|
||||
* Palette
|
||||
*
|
||||
@@ -46,19 +50,19 @@ namespace ivuorinen\Palette;
|
||||
class Palette
|
||||
{
|
||||
/** @var int Precision of palette, higher is more precise */
|
||||
public $precision;
|
||||
public int $precision;
|
||||
|
||||
/** @var int Number of colors to return */
|
||||
public $returnColors;
|
||||
public int $returnColors;
|
||||
|
||||
/** @var array Array of colors we use internally */
|
||||
public $colorsArray;
|
||||
public array $colorsArray;
|
||||
|
||||
/** @var string Full path to image file name */
|
||||
public $filename;
|
||||
public ?string $filename;
|
||||
|
||||
/** @var string Destination for .json-file, full path and filename */
|
||||
public $destination;
|
||||
public string $destination;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -71,7 +75,7 @@ class Palette
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($filename = null)
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
// Define shortcut to directory separator
|
||||
if (!defined('DS')) {
|
||||
@@ -100,10 +104,10 @@ class Palette
|
||||
* @return bool Returns true always
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run()
|
||||
public function run(): bool
|
||||
{
|
||||
if (empty($this->destination)) {
|
||||
throw new \ErrorException("No destination provided, can't save.");
|
||||
throw new GenericException("No destination provided, can't save.");
|
||||
}
|
||||
|
||||
$this->getPalette();
|
||||
@@ -119,16 +123,16 @@ class Palette
|
||||
* @return array|bool If we get array that has colors return the array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getPalette()
|
||||
public function getPalette(): array|bool
|
||||
{
|
||||
// We check for input
|
||||
if (empty($this->filename)) {
|
||||
throw new \ErrorException('Image was not provided');
|
||||
throw new GenericException('Image was not provided');
|
||||
}
|
||||
|
||||
// We check for readability
|
||||
if (!is_readable($this->filename)) {
|
||||
throw new \ErrorException("Image {$this->filename} is not readable");
|
||||
throw new GenericException("Image {$this->filename} is not readable");
|
||||
}
|
||||
|
||||
$this->colorsArray = $this->countColors();
|
||||
@@ -143,30 +147,26 @@ class Palette
|
||||
/**
|
||||
* countColors returns an array of colors in the image
|
||||
*
|
||||
* @return array|boolean Array of colors sorted by times used
|
||||
* @throws \ErrorException
|
||||
* @return array Array of colors sorted by times used
|
||||
* @throws GenericException
|
||||
*/
|
||||
private function countColors()
|
||||
private function countColors(): array
|
||||
{
|
||||
$this->precision = max(1, abs((int)$this->precision));
|
||||
$this->precision = max(1, abs($this->precision));
|
||||
$colors = array();
|
||||
|
||||
// Test for image type
|
||||
$img = $this->imageTypeToResource();
|
||||
|
||||
if (!$img && $img !== null) {
|
||||
throw new \ErrorException("Unable to open: {$this->filename}");
|
||||
}
|
||||
|
||||
// Get image size and check if it's really an image
|
||||
$size = @getimagesize($this->filename);
|
||||
|
||||
if ($size === false) {
|
||||
throw new \ErrorException("Unable to get image size data: {$this->filename}");
|
||||
throw new GenericException("Unable to get image size data: {$this->filename}");
|
||||
}
|
||||
|
||||
// This is pretty naive approach,
|
||||
// but looping through the image is only way I thought of
|
||||
// but looping through the image is the only way I thought of
|
||||
for ($x = 0; $x < $size[0]; $x += $this->precision) {
|
||||
for ($y = 0; $y < $size[1]; $y += $this->precision) {
|
||||
$thisColor = imagecolorat($img, $x, $y);
|
||||
@@ -195,23 +195,23 @@ class Palette
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function save()
|
||||
public function save(): bool
|
||||
{
|
||||
// Check for destination
|
||||
if (empty($this->destination)) {
|
||||
throw new \InvalidArgumentException('No destination given for save');
|
||||
}
|
||||
|
||||
// Check destination writability
|
||||
// Check destination for write permissions
|
||||
$this->checkDestination();
|
||||
|
||||
// Check for data we should write
|
||||
if (empty($this->colorsArray)) {
|
||||
throw new \ErrorException("Couldn't detect colors from image: {$this->filename}");
|
||||
throw new GenericException("Couldn't detect colors from image: {$this->filename}");
|
||||
}
|
||||
|
||||
// Encode for saving
|
||||
$colorsData = json_encode($this->colorsArray);
|
||||
$colorsData = json_encode($this->colorsArray, JSON_THROW_ON_ERROR);
|
||||
|
||||
// Save and return the result of save operation
|
||||
file_put_contents($this->destination, $colorsData);
|
||||
@@ -225,30 +225,30 @@ class Palette
|
||||
* Function takes $this->filename and returns
|
||||
* imagecreatefrom{gif|jpeg|png} for further processing
|
||||
*
|
||||
* @return resource Image resource based on content
|
||||
* @throws \ErrorException
|
||||
* @return GdImage Image resource based on content
|
||||
* @throws GenericException
|
||||
*/
|
||||
private function imageTypeToResource()
|
||||
private function imageTypeToResource(): GdImage
|
||||
{
|
||||
try {
|
||||
if (filesize($this->filename) < 12) {
|
||||
throw new \ErrorException('File size smaller than 12');
|
||||
throw new GenericException('File size smaller than 12');
|
||||
}
|
||||
$type = exif_imagetype($this->filename);
|
||||
} catch (\Exception $e) {
|
||||
throw new \ErrorException($e->getMessage());
|
||||
throw new GenericException($e->getMessage());
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case '1': // IMAGETYPE_GIF
|
||||
return @imagecreatefromgif($this->filename);
|
||||
return @\imagecreatefromgif($this->filename);
|
||||
case '2': // IMAGETYPE_JPEG
|
||||
return @imagecreatefromjpeg($this->filename);
|
||||
return @\imagecreatefromjpeg($this->filename);
|
||||
case '3': // IMAGETYPE_PNG
|
||||
return @imagecreatefrompng($this->filename);
|
||||
return @\imagecreatefrompng($this->filename);
|
||||
default:
|
||||
$image_type_code = image_type_to_mime_type($type);
|
||||
throw new \ErrorException("Unknown image type: {$image_type_code} ({$type}): {$this->filename}");
|
||||
throw new GenericException("Unknown image type: {$image_type_code} ({$type}): {$this->filename}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,13 +263,13 @@ class Palette
|
||||
* @return boolean True or false, with exceptions
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function checkDestination()
|
||||
private function checkDestination(): bool
|
||||
{
|
||||
$destination_dir = dirname($this->destination);
|
||||
|
||||
// Test if we have destination directory
|
||||
if (!@mkdir($destination_dir, 0755) && !is_dir($destination_dir)) {
|
||||
throw new \ErrorException("Couldn't create missing destination dir: {$destination_dir}");
|
||||
throw new GenericException("Couldn't create missing destination dir: {$destination_dir}");
|
||||
}
|
||||
|
||||
// Test if we can write to it
|
||||
@@ -279,7 +279,7 @@ class Palette
|
||||
chmod($destination_dir, 0755);
|
||||
|
||||
if (!is_writable($destination_dir)) {
|
||||
throw new \ErrorException("Destination directory not writable: {$destination_dir}");
|
||||
throw new GenericException("Destination directory not writable: {$destination_dir}");
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -6,22 +6,22 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PaletteTest extends TestCase
|
||||
{
|
||||
public function testClassIsFoundAndHasDefaultAttributes()
|
||||
public function testClassIsFoundAndHasDefaultAttributes(): void
|
||||
{
|
||||
$palette = new \ivuorinen\Palette\Palette();
|
||||
$palette = new \ivuorinen\Palette\Palette('');
|
||||
$this->assertInstanceOf('ivuorinen\Palette\Palette', $palette);
|
||||
|
||||
$this->assertIsInt($palette->precision);
|
||||
$this->assertIsInt($palette->returnColors);
|
||||
$this->assertIsArray($palette->colorsArray);
|
||||
$this->assertNull($palette->filename);
|
||||
$this->assertIsString($palette->filename);
|
||||
$this->assertIsString($palette->destination);
|
||||
}
|
||||
|
||||
public function testKnownImagesWithOneColor()
|
||||
public function testKnownImagesWithOneColor(): void
|
||||
{
|
||||
$location = __DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR;
|
||||
$images = [ 'black.png' => '000000', 'red.png' => 'CC3333' ];
|
||||
$images = ['black.png' => '000000', 'red.png' => 'CC3333'];
|
||||
|
||||
foreach ($images as $imageFile => $hex) {
|
||||
$image = $location . $imageFile;
|
||||
@@ -34,10 +34,10 @@ class PaletteTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testKnownImagesWithManyColors()
|
||||
public function testKnownImagesWithManyColors(): void
|
||||
{
|
||||
$location = __DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR;
|
||||
$images = [ 'example.gif', 'example.jpg', 'example.png' ];
|
||||
$images = ['example.gif', 'example.jpg', 'example.png'];
|
||||
|
||||
foreach ($images as $imageFile) {
|
||||
$image = $location . $imageFile;
|
||||
@@ -49,7 +49,7 @@ class PaletteTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testFailureNoImage()
|
||||
public function testFailureNoImage(): void
|
||||
{
|
||||
$palette = new \ivuorinen\Palette\Palette('');
|
||||
$this->expectException(\ErrorException::class);
|
||||
@@ -57,11 +57,11 @@ class PaletteTest extends TestCase
|
||||
$palette->getPalette();
|
||||
}
|
||||
|
||||
public function testFailureNotAnImage()
|
||||
public function testFailureNotAnImage(): void
|
||||
{
|
||||
$palette = new \ivuorinen\Palette\Palette();
|
||||
$palette->filename = 'NOT_HERE';
|
||||
$this->expectException(\ErrorException::class);
|
||||
|
||||
$palette = new \ivuorinen\Palette\Palette('NOT_HERE');
|
||||
$this->expectExceptionMessage('Image ' . $palette->filename . ' is not readable');
|
||||
|
||||
$palette->getPalette();
|
||||
|
||||
Reference in New Issue
Block a user