diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..190d701 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 227c2f4..34d9d0b 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -1,39 +1,39 @@ name: PHP Composer on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] + push: + branches: ["master"] + pull_request: + branches: ["master"] permissions: - contents: read + contents: read jobs: - build: + build: + runs-on: ubuntu-latest - runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 - 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 + - name: Validate composer.json and composer.lock + run: composer validate --strict - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v4 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- - - name: Install dependencies - run: composer install --prefer-dist --no-progress + - 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 + - name: Run test suite + run: composer run-script test diff --git a/.travis.yml b/.travis.yml index 8232dc4..62ed1ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,22 @@ language: php php: - - 5.6 - - 7.0 - - 7.1 - - nightly + - 8.3 + - nightly matrix: - allow_failures: - - php: 5.6 - - php: nightly - fast_finish: true + allow_failures: + - php: nightly + fast_finish: true cache: - directories: - - $HOME/.composer/cache/files + directories: + - $HOME/.composer/cache/files before_script: - - phpenv global "$TRAVIS_PHP_VERSION" - - composer config extra.platform.php $TRAVIS_PHP_VERSION - - composer require php-coveralls/php-coveralls + - phpenv global "$TRAVIS_PHP_VERSION" + - composer config extra.platform.php $TRAVIS_PHP_VERSION + - composer require php-coveralls/php-coveralls install: - - flags="--ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress" - - composer install $flags + - flags="--ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress" + - composer install $flags script: - - ./vendor/bin/phpunit -c phpunit.xml --coverage-clover build/logs/clover.xml + - ./vendor/bin/phpunit -c phpunit.xml --coverage-clover build/logs/clover.xml after_script: - - php vendor/bin/coveralls -v + - php vendor/bin/coveralls -v diff --git a/composer.json b/composer.json index ed41865..4b865b9 100644 --- a/composer.json +++ b/composer.json @@ -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" } } diff --git a/phpunit.xml b/phpunit.xml index 8d0cb78..0961104 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,13 @@ - - - - ./src/ - - + ./tests + + + ./src/ + + diff --git a/src/Exceptions/GenericException.php b/src/Exceptions/GenericException.php new file mode 100644 index 0000000..4d64800 --- /dev/null +++ b/src/Exceptions/GenericException.php @@ -0,0 +1,14 @@ +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; diff --git a/tests/PaletteTest.php b/tests/PaletteTest.php index 833ff51..c3eef82 100644 --- a/tests/PaletteTest.php +++ b/tests/PaletteTest.php @@ -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();