diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..0f88135 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: PHP Tests + +on: [push, pull_request] + +jobs: + + build: + strategy: + matrix: + operating-system: [ubuntu-22.04] + php-versions: ['7.3', '7.4', '8.0', '8.1'] + fail-fast: false + runs-on: ${{ matrix.operating-system }} + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Run test suite + run: composer run-script test diff --git a/.gitignore b/.gitignore index b12c2aa..affaa2d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ vendor composer.lock /phpunit.xml /.php_cs.cache +.idea +.phpunit.result.cache diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3fcdfbf..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: php - -php: - - 7.1 - - 7.2 - - 7.3 - - 7.4 - - 8.0 - -env: - global: - - PATH="$HOME/.composer/vendor/bin:$PATH" - -matrix: - fast_finish: true - -sudo: false - -cache: - directories: - - $HOME/.composer/cache/files - -before_script: - - composer selfupdate - - composer global require phpunit/phpunit --no-update - - composer global update --prefer-dist --no-interaction - - composer update --prefer-dist --no-interaction $COMPOSER_FLAGS - -script: make test diff --git a/Adapter/GD.php b/Adapter/GD.php index a0c0532..b5172da 100644 --- a/Adapter/GD.php +++ b/Adapter/GD.php @@ -4,6 +4,7 @@ use Gregwar\Image\Image; use Gregwar\Image\ImageColor; +use Gregwar\Image\Utils\FileUtils; class GD extends Common { @@ -644,7 +645,7 @@ public function saveJpeg($file, $quality) */ protected function openJpeg($file) { - if (file_exists($file) && filesize($file)) { + if (FileUtils::safeExists($file) && filesize($file)) { $this->resource = @imagecreatefromjpeg($file); } else { $this->resource = false; @@ -660,7 +661,7 @@ protected function openJpeg($file) */ protected function openGif($file) { - if (file_exists($file) && filesize($file)) { + if (FileUtils::safeExists($file) && filesize($file)) { $this->resource = @imagecreatefromgif($file); } else { $this->resource = false; @@ -676,7 +677,7 @@ protected function openGif($file) */ protected function openPng($file) { - if (file_exists($file) && filesize($file)) { + if (FileUtils::safeExists($file) && filesize($file)) { $this->resource = @imagecreatefrompng($file); } else { $this->resource = false; @@ -692,7 +693,7 @@ protected function openPng($file) */ protected function openWebp($file) { - if (file_exists($file) && filesize($file)) { + if (FileUtils::safeExists($file) && filesize($file)) { $this->resource = @imagecreatefromwebp($file); } else { $this->resource = false; diff --git a/README.md b/README.md index 7d2e477..385b726 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gregwar's Image class -[![Build status](https://travis-ci.org/Gregwar/Image.svg?branch=master)](https://travis-ci.org/Gregwar/Image) +[![Build status](https://github.com/Gregwar/Image/actions/workflows/test.yml/badge.svg)](https://github.com/Gregwar/Image/actions/workflows/test.yml) [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YUXRLWHQSWS6L) The `Gregwar\Image` class purpose is to provide a simple object-oriented images handling and caching API. @@ -211,8 +211,8 @@ You can also create your own image on-the-fly using drawing functions: ## Using fallback image -If the image file doesn't exists, you can configurate a fallback image that will be used -by the class (note that this require the cache directory to be available). +If the image file doesn't exist, you can configure a fallback image that will be used +by the class (note that this requires the cache directory to be available). A default "error" image which is used is in `images/error.jpg`, you can change it with: diff --git a/Source/File.php b/Source/File.php index 1ad263d..97c994b 100644 --- a/Source/File.php +++ b/Source/File.php @@ -3,6 +3,7 @@ namespace Gregwar\Image\Source; use Gregwar\Image\Image; +use Gregwar\Image\Utils\FileUtils; /** * Open an image from a file. @@ -47,7 +48,7 @@ public function correct() */ public function guessType() { - if (function_exists('exif_imagetype')) { + if (function_exists('exif_imagetype') && FileUtils::safeExists($this->file)) { $type = @exif_imagetype($this->file); if (false !== $type) { diff --git a/Utils/FileUtils.php b/Utils/FileUtils.php new file mode 100644 index 0000000..75c7582 --- /dev/null +++ b/Utils/FileUtils.php @@ -0,0 +1,22 @@ + diff --git a/tests/ImageTests.php b/tests/ImageTests.php index 2c0c5fc..0111394 100755 --- a/tests/ImageTests.php +++ b/tests/ImageTests.php @@ -13,18 +13,18 @@ class ImageTests extends \PHPUnit\Framework\TestCase /** * Testing the basic width & height. */ - public function testBasics() + public function testBasics(): void { $image = $this->open('monalisa.jpg'); - $this->assertSame($image->width(), 771); - $this->assertSame($image->height(), 961); + self::assertSame($image->width(), 771); + self::assertSame($image->height(), 961); } /** * Testing the resize. */ - public function testResize() + public function testResize(): void { $image = $this->open('monalisa.jpg'); @@ -34,17 +34,17 @@ public function testResize() ->save($out) ; - $this->assertTrue(file_exists($out)); + self::assertFileExists($out); $i = imagecreatefromjpeg($out); - $this->assertSame(300, imagesx($i)); - $this->assertSame(200, imagesy($i)); + self::assertSame(300, imagesx($i)); + self::assertSame(200, imagesy($i)); } /** * Testing the resize %. */ - public function testResizePercent() + public function testResizePercent(): void { $image = $this->open('monalisa.jpg'); @@ -54,17 +54,17 @@ public function testResizePercent() ->save($out) ; - $this->assertTrue(file_exists($out)); + self::assertFileExists($out); $i = imagecreatefromjpeg($out); - $this->assertSame(386, imagesx($i)); - $this->assertSame(481, imagesy($i)); + self::assertSame(386, imagesx($i)); + self::assertSame(481, imagesy($i)); } /** * Testing to create an image, jpeg, gif and png. */ - public function testCreateImage() + public function testCreateImage(): void { $black = $this->output('black.jpg'); @@ -73,12 +73,12 @@ public function testCreateImage() ->save($black, 100); $i = imagecreatefromjpeg($black); - $this->assertTrue(file_exists($black)); - $this->assertSame(150, imagesx($i)); - $this->assertSame(200, imagesy($i)); + self::assertFileExists($black); + self::assertSame(150, imagesx($i)); + self::assertSame(200, imagesy($i)); $j = imagecolorat($i, 40, 40); - $this->assertSame(0, $j); + self::assertSame(0, $j); $black = $this->output('black.png'); Image::create(150, 200) @@ -86,9 +86,9 @@ public function testCreateImage() ->save($black, 'png'); $i = imagecreatefrompng($black); - $this->assertTrue(file_exists($black)); - $this->assertSame(150, imagesx($i)); - $this->assertSame(200, imagesy($i)); + self::assertFileExists($black); + self::assertSame(150, imagesx($i)); + self::assertSame(200, imagesy($i)); $black = $this->output('black.gif'); Image::create(150, 200) @@ -96,131 +96,131 @@ public function testCreateImage() ->save($black, 'gif'); $i = imagecreatefromgif($black); - $this->assertTrue(file_exists($black)); - $this->assertSame(150, imagesx($i)); - $this->assertSame(200, imagesy($i)); + self::assertFileExists($black); + self::assertSame(150, imagesx($i)); + self::assertSame(200, imagesy($i)); } /** * Testing type guess. */ - public function testGuess() + public function testGuess(): void { $image = $this->open('monalisa.jpg'); - $this->assertSame('jpeg', $image->guessType()); + self::assertSame('jpeg', $image->guessType()); $image = $this->open('monalisa.png'); - $this->assertSame('png', $image->guessType()); + self::assertSame('png', $image->guessType()); $image = $this->open('monalisa.gif'); - $this->assertSame('gif', $image->guessType()); + self::assertSame('gif', $image->guessType()); } - public function testDefaultCacheSystem() + public function testDefaultCacheSystem(): void { $image = $this->open('monalisa.jpg'); - $this->assertInstanceOf('Gregwar\Cache\Cache', $image->getCacheSystem()); + self::assertInstanceOf('Gregwar\Cache\Cache', $image->getCacheSystem()); } - public function testCustomCacheSystem() + public function testCustomCacheSystem(): void { $image = $this->open('monalisa.jpg'); $cache = new Cache(); $image->setCacheSystem($cache); - $this->assertEquals($image->getCacheSystem(), $cache); + self::assertInstanceOf(Gregwar\Cache\CacheInterface::class, $image->getCacheSystem()); } /** * Testing that caching an image without operations will result * in the original image when force cache is disabled. */ - public function testNoCache() + public function testNoCache(): void { $monalisa = __DIR__.'/files/monalisa.jpg'; $image = $this->open('monalisa.jpg')->setForceCache(false); - $this->assertSame($monalisa, $image->guess()); + self::assertSame($monalisa, $image->guess()); $image = $this->open('monalisa.jpg'); - $this->assertNotSame($monalisa, $image->guess()); + self::assertNotSame($monalisa, $image->guess()); $image = $this->open('monalisa.jpg')->setForceCache(true); - $this->assertNotSame($monalisa, $image->guess()); + self::assertNotSame($monalisa, $image->guess()); } - public function testActualCache() + public function testActualCache(): void { $output = $this->open('monalisa.jpg') ->setCacheDir('/magic/path/to/cache/') ->resize(100, 50)->negate() ->guess(); - $this->assertStringContainsString('/magic/path/to/cache', $output); + self::assertStringContainsString('/magic/path/to/cache', $output); $file = str_replace('/magic/path/to', __DIR__.'/output/', $output); - $this->assertTrue(file_exists($file)); + self::assertFileExists($file); } - public function testCacheData() + public function testCacheData(): void { $output = $this->open('monalisa.jpg') ->resize(300, 200) ->cacheData(); $jpg = imagecreatefromstring($output); - $this->assertSame(300, imagesx($jpg)); - $this->assertSame(200, imagesy($jpg)); + self::assertSame(300, imagesx($jpg)); + self::assertSame(200, imagesy($jpg)); } /** * Testing using cache. */ - public function testCache() + public function testCache(): void { $output = $this->open('monalisa.jpg') ->resize(100, 50)->negate() ->guess(); - $this->assertTrue(file_exists($output)); + self::assertFileExists($output); $i = imagecreatefromjpeg($output); - $this->assertSame(100, imagesx($i)); - $this->assertSame(50, imagesy($i)); + self::assertSame(100, imagesx($i)); + self::assertSame(50, imagesy($i)); $output2 = $this->open('monalisa.jpg') ->resize(100, 50)->negate() ->guess(); - $this->assertSame($output, $output2); + self::assertSame($output, $output2); $output3 = $this->open('monalisa.jpg') ->resize(100, 50)->negate() ->png(); - $this->assertTrue(file_exists($output)); + self::assertFileExists($output); $i = imagecreatefrompng($output3); - $this->assertSame(100, imagesx($i)); - $this->assertSame(50, imagesy($i)); + self::assertSame(100, imagesx($i)); + self::assertSame(50, imagesy($i)); $output4 = $this->open('monalisa.jpg') ->resize(100, 50)->negate() ->gif(); - $this->assertTrue(file_exists($output)); + self::assertFileExists($output); $i = imagecreatefromgif($output4); - $this->assertSame(100, imagesx($i)); - $this->assertSame(50, imagesy($i)); + self::assertSame(100, imagesx($i)); + self::assertSame(50, imagesy($i)); } /** * Testing Gaussian blur filter. */ - public function testGaussianBlur() + public function testGaussianBlur(): void { $image = $this->open('monalisa.jpg') ->gaussianBlur(); $secondImage = $this->open('monalisa.jpg') ->gaussianBlur(5); - $this->assertTrue(file_exists($image)); - $this->assertTrue(file_exists($secondImage)); + self::assertFileExists($image); + self::assertFileExists($secondImage); } /** * Testing creating image from data. */ - public function testData() + public function testData(): void { $data = file_get_contents(__DIR__.'/files/monalisa.jpg'); @@ -228,16 +228,16 @@ public function testData() $image = Image::fromData($data); $image->save($output); - $this->assertTrue(file_exists($output)); + self::assertFileExists($output); $i = imagecreatefromjpeg($output); - $this->assertSame(771, imagesx($i)); - $this->assertSame(961, imagesy($i)); + self::assertSame(771, imagesx($i)); + self::assertSame(961, imagesy($i)); } /** * Opening an image. */ - protected function open($file) + protected function open(string $file): Image { $image = Image::open(__DIR__.'/files/'.$file); $image->setCacheDir(__DIR__.'/output/cache/'); @@ -249,33 +249,33 @@ protected function open($file) /** * Testing transparent image. */ - public function testTransparent() + public function testTransparent(): void { $gif = $this->output('transparent.gif'); $i = Image::create(200, 100) ->fill('transparent') ->save($gif, 'gif'); - $this->assertTrue(file_exists($gif)); + self::assertFileExists($gif); $img = imagecreatefromgif($gif); - $this->assertSame(200, imagesx($img)); - $this->assertSame(100, imagesy($img)); + self::assertSame(200, imagesx($img)); + self::assertSame(100, imagesy($img)); $index = imagecolorat($img, 2, 2); $color = imagecolorsforindex($img, $index); - $this->assertSame(127, $color['alpha']); + self::assertSame(127, $color['alpha']); } - public function testNonExistingFile() + public function testNonExistingFile(): void { $jpg = $this->output('a.jpg'); $img = $this->open('non_existing_file.jpg') ->negate(); $error = $img->save($jpg); - $this->assertTrue(file_exists($error)); + self::assertFileExists($error); } - public function testNonExistingFileNoFallback() + public function testNonExistingFileNoFallback(): void { $this->expectException(\UnexpectedValueException::class); @@ -287,20 +287,20 @@ public function testNonExistingFileNoFallback() /** * Test that image::save returns the file name. */ - public function testSaveReturn() + public function testSaveReturn(): void { $red = $this->output('red.jpg'); $result = Image::create(10, 10) ->fill('red') ->save($red); - $this->assertSame($red, $result); + self::assertSame($red, $result); } /** * Testing merge. */ - public function testMerge() + public function testMerge(): void { $out = $this->output('merge.jpg'); Image::create(100, 100) @@ -311,7 +311,7 @@ public function testMerge() ->save($out); // Merge file exists - $this->assertTrue(file_exists($out)); + self::assertFileExists($out); // Test that the upper left zone contain a black pixel, and the lower // down contains a red one @@ -323,23 +323,23 @@ public function testMerge() /** * Test that dependencies are well generated. */ - public function testDependencies() + public function testDependencies(): void { - $this->assertSame(array(), Image::create(100, 100) + self::assertSame(array(), Image::create(100, 100) ->getDependencies()); - $this->assertSame(array(), Image::create(100, 100) + self::assertSame(array(), Image::create(100, 100) ->merge(Image::create(100, 50)) ->getDependencies()); - $this->assertSame(array('toto.jpg'), Image::open('toto.jpg') + self::assertSame(array('toto.jpg'), Image::open('toto.jpg') ->merge(Image::create(100, 50)) ->getDependencies()); - $this->assertSame(array('toto.jpg', 'titi.jpg'), Image::open('toto.jpg') + self::assertSame(array('toto.jpg', 'titi.jpg'), Image::open('toto.jpg') ->merge(Image::open('titi.jpg')) ->getDependencies()); - $this->assertSame(array('toto.jpg', 'titi.jpg', 'tata.jpg'), Image::open('toto.jpg') + self::assertSame(array('toto.jpg', 'titi.jpg', 'tata.jpg'), Image::open('toto.jpg') ->merge(Image::open('titi.jpg') ->merge(Image::open('tata.jpg'))) ->getDependencies()); @@ -348,21 +348,21 @@ public function testDependencies() /** * Test that pretty name (for referencing) is well respected. */ - public function testPrettyName() + public function testPrettyName(): void { $output = $this->open('monalisa.jpg') ->resize(100, 50)->negate() ->setPrettyName('davinci', false) ->guess(); - $this->assertStringContainsString('davinci', $output); + self::assertStringContainsString('davinci', $output); $output2 = $this->open('monalisa.jpg') ->resize(100, 55)->negate() ->setPrettyName('davinci', false) ->guess(); - $this->assertSame($output, $output2); + self::assertSame($output, $output2); $prefix1 = $this->open('monalisa.jpg') ->resize(100, 50)->negate() @@ -373,9 +373,9 @@ public function testPrettyName() ->setPrettyName('davinci') ->guess(); - $this->assertStringContainsString('davinci', $prefix1); - $this->assertStringContainsString('davinci', $prefix2); - $this->assertNotSame($prefix1, $prefix2); + self::assertStringContainsString('davinci', $prefix1); + self::assertStringContainsString('davinci', $prefix2); + self::assertNotSame($prefix1, $prefix2); $transliterator = '\Behat\Transliterator\Transliterator'; @@ -386,7 +386,7 @@ public function testPrettyName() ->setPrettyName($nonLatinName1, false) ->guess(); - $this->assertContains( + self::assertContains( $transliterator::urlize($transliterator::transliterate($nonLatinName1)), $nonLatinOutput1 ); @@ -397,7 +397,7 @@ public function testPrettyName() ->setPrettyName($nonLatinName2) ->guess(); - $this->assertContains( + self::assertContains( $transliterator::urlize($transliterator::transliterate($nonLatinName2)), $nonLatinOutput2 ); @@ -407,25 +407,25 @@ public function testPrettyName() /** * Testing inlining. */ - public function testInline() + public function testInline(): void { $output = $this->open('monalisa.jpg') ->resize(20, 20) ->inline(); - $this->assertSame(0, strpos($output, 'data:image/jpeg;base64,')); + self::assertSame(0, strpos($output, 'data:image/jpeg;base64,')); $data = base64_decode(substr($output, 23)); $image = imagecreatefromstring($data); - $this->assertSame(20, imagesx($image)); - $this->assertSame(20, imagesy($image)); + self::assertSame(20, imagesx($image)); + self::assertSame(20, imagesy($image)); } /** * Testing that width() can be called after cache */ - public function testWidthPostCache() + public function testWidthPostCache(): void { $this->open('monalisa.jpg') ->resize(100, 50)->negate() @@ -436,27 +436,27 @@ public function testWidthPostCache() $png = $dummy2->png(); $i = imagecreatefrompng($png); - $this->assertEquals(imagesx($i), $dummy2->width()); + self::assertEquals(imagesx($i), $dummy2->width()); } /** - * Asaserting that two colors are equals + * Asserting that two colors are equals * (JPG compression is not preserving colors for instance, so we * need a non-strict way to compare it). */ - protected function assertColorEquals($c1, $c2, $delta = 8) + protected function assertColorEquals($c1, $c2, $delta = 8): void { $c1 = ImageColor::parse($c1); $c2 = ImageColor::parse($c2); list($r1, $g1, $b1) = $this->toRGB($c1); list($r2, $g2, $b2) = $this->toRGB($c2); - $this->assertLessThan($delta, abs($r1 - $r2)); - $this->assertLessThan($delta, abs($g1 - $g2)); - $this->assertLessThan($delta, abs($b1 - $b2)); + self::assertLessThan($delta, abs($r1 - $r2)); + self::assertLessThan($delta, abs($g1 - $g2)); + self::assertLessThan($delta, abs($b1 - $b2)); } - protected function toRGB($color) + protected function toRGB($color): array { $b = $color & 0xff; $g = ($color >> 8) & 0xff; @@ -464,11 +464,11 @@ protected function toRGB($color) return array($r, $g, $b); } - + /** - * Outputing an image to a file. + * Outputting an image to a file. */ - protected function output($file) + protected function output(string $file): string { return __DIR__.'/output/'.$file; } @@ -480,7 +480,11 @@ public function setUp(): void { $dir = $this->output(''); `rm -rf $dir`; - mkdir($dir); - mkdir($this->output('cache')); + if( !mkdir($dir) && !is_dir($dir) ){ + throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir)); + } + if( !mkdir($concurrentDirectory = $this->output('cache')) && !is_dir($concurrentDirectory) ){ + throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); + } } }