├── .github └── workflows │ ├── packagist-deploy.yml │ └── tests.yml ├── .gitignore ├── Dockerfile ├── README.md ├── assets └── fonts │ └── Roboto-Regular.ttf ├── composer.json ├── docker-compose.yml ├── phpunit.xml ├── src ├── Exceptions │ └── NoImageDriverException.php ├── Extensions │ └── ImagesExtension.php ├── FakerImagesServiceProvider.php └── Providers │ └── ImageManagerProvider.php └── tests └── Unit ├── Extensions └── ImagesExtensionTest.php ├── Providers └── ImageManagerProviderTest.php └── TestCase.php /.github/workflows/packagist-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Packagist Deploy 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: mnavarrocarter/packagist-update@v1.0.0 18 | with: 19 | username: "GautierDele" 20 | api_token: ${{ secrets.PACKAGIST_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | php-version: [ '8.3', '8.4' ] 18 | image-extensions: 19 | - 'none,mbstring,dom,tokenizer,xml,xmlwriter,ctype' 20 | - 'none,mbstring,dom,tokenizer,xml,xmlwriter,ctype,gd' 21 | - 'none,mbstring,dom,tokenizer,xml,xmlwriter,ctype,imagick' 22 | 23 | name: Tests on PHP ${{ matrix.php-version }} with extensions ${{ matrix.image-extensions}} 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | 28 | - name: Setup PHP 29 | uses: shivammathur/setup-php@v2 30 | with: 31 | php-version: ${{ matrix.php-version }} 32 | extensions: ${{ matrix.image-extensions }} 33 | coverage: none 34 | 35 | - name: Validate composer.json and composer.lock 36 | run: composer validate 37 | 38 | - name: Cache Composer packages 39 | id: composer-cache 40 | uses: actions/cache@v4 41 | with: 42 | path: vendor 43 | key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} 44 | restore-keys: | 45 | ${{ runner.os }}-php-${{ matrix.php-version }}- 46 | 47 | - name: Install dependencies 48 | if: steps.composer-cache.outputs.cache-hit != 'true' 49 | run: composer i --prefer-dist --no-progress 50 | 51 | - name: Run test suite 52 | run: vendor/bin/phpunit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | .phpunit* 4 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:cli 2 | 3 | RUN apt update 4 | RUN apt install unzip curl libfreetype6-dev libjpeg62-turbo-dev libpng-dev -y 5 | 6 | # Uncomment the following lines to activate the GD extension to try local images generation 7 | #RUN docker-php-ext-configure gd --with-freetype --with-jpeg 8 | #RUN docker-php-ext-install gd 9 | 10 | RUN curl -sS https://getcomposer.org/installer -o /usr/local/composer-setup.php 11 | 12 | RUN php /usr/local/composer-setup.php --install-dir=/usr/local/bin --filename=composer 13 | 14 | RUN rm /usr/local/composer-setup.php 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Faker PHP - Images extension 2 | 3 | This repository is an extension of the core [Faker package](https://github.com/xefi/faker-php). 4 | Please refer to the [main package](https://github.com/xefi/faker-php) or the [documentation](https://faker-php.xefi.com/) to understand it well. -------------------------------------------------------------------------------- /assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xefi/faker-php-images/1c78c8097f2d6ba52fc1c4cb8b4bbcf30c84b213/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xefi/faker-php-images", 3 | "type": "library", 4 | "description": "Faker extension to generate images", 5 | "keywords": [ 6 | "faker", 7 | "file", 8 | "image" 9 | ], 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Gautier Deleglise" 14 | }, 15 | { 16 | "name": "Martin Soenen" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.3", 21 | "intervention/image": "^3.9", 22 | "psr/container": "^2.0", 23 | "xefi/faker-php": "^1" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "^11" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Xefi\\Faker\\Images\\": "src/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Xefi\\Faker\\Images\\Tests\\": "tests/" 36 | } 37 | }, 38 | "config": { 39 | "sort-packages": true 40 | }, 41 | "extra": { 42 | "faker": { 43 | "providers": [ 44 | "Xefi\\Faker\\Images\\FakerImagesServiceProvider" 45 | ] 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | php: 3 | build: . 4 | image: php:latest 5 | volumes: 6 | - ./:/var/www/html 7 | working_dir: /var/www/html 8 | tty: true -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | tests/Unit 13 | 14 | 15 | 16 | 17 | src 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Exceptions/NoImageDriverException.php: -------------------------------------------------------------------------------- 1 | imageManagerProvider = new ImageManagerProvider(); 16 | 17 | parent::__construct($randomizer); 18 | } 19 | 20 | /** 21 | * Calculate the best text size from width and height 22 | * 23 | * @param int $width 24 | * @param int $height 25 | * 26 | * @return int 27 | */ 28 | private function calculateTextSize(int $width, int $height): int 29 | { 30 | return round( 31 | min($width / 8, $height / 1.5) 32 | ); 33 | } 34 | 35 | public function image(int $width = 300, int $height = 200, string $backgroundColor = '#cccccc', string $textColor = '#333333', ?string $text = null): Image 36 | { 37 | $text ??= $width.' x '.$height; 38 | 39 | $imageManager = $this->imageManagerProvider->getImageManager(); 40 | 41 | $image = $imageManager->create($width,$height); 42 | $image->fill($backgroundColor); 43 | 44 | $image->text($text, round($width / 2), round($height / 2), function($font) use ($textColor, $width, $height) { 45 | $font->file(__DIR__.'/../../assets/fonts/Roboto-Regular.ttf'); 46 | $font->size($this->calculateTextSize($width, $height)); 47 | $font->color($textColor); 48 | $font->align('center'); 49 | $font->valign('middle'); 50 | }); 51 | 52 | return $image; 53 | } 54 | 55 | public function imageUrl(int $width = 300, int $height = 200): string 56 | { 57 | return 'https://placehold.co/'.$width.'x'.$height; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/FakerImagesServiceProvider.php: -------------------------------------------------------------------------------- 1 | extensions([ 12 | ImagesExtension::class 13 | ]); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/Providers/ImageManagerProvider.php: -------------------------------------------------------------------------------- 1 | imageManager === null) { 17 | $this->imageManager = new ImageManager(self::selectDriver()); 18 | } 19 | 20 | return $this->imageManager; 21 | } 22 | 23 | /** 24 | * Ensure a driver is available, and select the right driver for Imagick 25 | * 26 | * @return GdDriver|ImagickDriver 27 | */ 28 | static private function selectDriver(): GdDriver|ImagickDriver 29 | { 30 | if (extension_loaded('gd')) { 31 | return new GdDriver(); 32 | } 33 | 34 | if (extension_loaded('imagick')) { 35 | return new ImagickDriver(); 36 | } 37 | 38 | throw new NoImageDriverException('Please activate GD or Imagick in your PHP extensions.'); 39 | } 40 | } -------------------------------------------------------------------------------- /tests/Unit/Extensions/ImagesExtensionTest.php: -------------------------------------------------------------------------------- 1 | needImageDriver(); 15 | 16 | $faker = new Container(false); 17 | 18 | $image = $faker->unique()->image(); 19 | 20 | $this->assertInstanceOf(Image::class, $image); 21 | $this->assertEquals(300, $image->width()); 22 | $this->assertEquals(200, $image->height()); 23 | } 24 | 25 | public function testImageWithCustomParameters(): void 26 | { 27 | $this->needImageDriver(); 28 | 29 | $faker = new Container(false); 30 | $randomizer = new Randomizer(); 31 | 32 | for ($i = 0; $i < 10; $i++) { 33 | $width = $randomizer->getInt(1, 2000); 34 | $height = $randomizer->getInt(1, 2000); 35 | 36 | $image = $faker->unique()->image($width, $height); 37 | 38 | $this->assertInstanceOf(Image::class, $image); 39 | $this->assertEquals($width, $image->width()); 40 | $this->assertEquals($height, $image->height()); 41 | } 42 | } 43 | 44 | public function testImageUrl(): void 45 | { 46 | $faker = new Container(false); 47 | 48 | $url = $faker->unique()->imageUrl(); 49 | 50 | $this->assertEquals('https://placehold.co/300x200', $url); 51 | } 52 | 53 | public function testImageUrlWithCustomValues(): void 54 | { 55 | $faker = new Container(false); 56 | $randomizer = new Randomizer(); 57 | 58 | for ($i = 0; $i < 100; $i++) { 59 | $width = $randomizer->getInt(1, 2000); 60 | $height = $randomizer->getInt(1, 2000); 61 | 62 | $url = $faker->unique()->imageUrl($width, $height); 63 | 64 | $this->assertEquals('https://placehold.co/' . $width . 'x' . $height, $url); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/Unit/Providers/ImageManagerProviderTest.php: -------------------------------------------------------------------------------- 1 | getMethod($methodName); 22 | } 23 | 24 | public function testSelectDriverWithoutExtension(): void 25 | { 26 | $this->needNoImageDriver(); 27 | 28 | $this->expectException(NoImageDriverException::class); 29 | 30 | $method = self::getMethod('selectDriver'); 31 | $method->invokeArgs(new ImageManagerProvider(), []); 32 | } 33 | 34 | #[RequiresPhpExtension('gd')] 35 | public function testSelectDriverWithGd(): void 36 | { 37 | $method = self::getMethod('selectDriver'); 38 | $driver = $method->invokeArgs(new ImageManagerProvider(), []); 39 | 40 | $this->assertInstanceOf(GdDriver::class, $driver); 41 | } 42 | 43 | #[RequiresPhpExtension('imagick')] 44 | public function testSelectDriverWithImagick(): void 45 | { 46 | $method = self::getMethod('selectDriver'); 47 | $driver = $method->invokeArgs(new ImageManagerProvider(), []); 48 | 49 | $this->assertInstanceOf(ImagickDriver::class, $driver); 50 | } 51 | 52 | public function testGetImageManagerWithoutDriver() { 53 | $this->needNoImageDriver(); 54 | 55 | $this->expectException(NoImageDriverException::class); 56 | 57 | $provider = new ImageManagerProvider(); 58 | $provider->getImageManager(); 59 | } 60 | 61 | public function testGetImageManager() { 62 | $this->needImageDriver(); 63 | 64 | $provider = new ImageManagerProvider(); 65 | $manager = $provider->getImageManager(); 66 | 67 | $this->assertInstanceOf(ImageManager::class, $manager); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/Unit/TestCase.php: -------------------------------------------------------------------------------- 1 | boot(); 15 | } 16 | 17 | protected function needImageDriver(): void 18 | { 19 | if (!extension_loaded('gd') && !extension_loaded('imagick')) { 20 | $this->markTestSkipped('GD or Imagick extension is required.'); 21 | } 22 | } 23 | 24 | protected function needNoImageDriver(): void 25 | { 26 | if (extension_loaded('gd') || extension_loaded('imagick')) { 27 | $this->markTestSkipped('This test is only without GD and Imagick extensions.'); 28 | } 29 | } 30 | } --------------------------------------------------------------------------------