Version 0.7.0 #1
							
								
								
									
										144
									
								
								app/Captcha/Config/ImageBody.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								app/Captcha/Config/ImageBody.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Config;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Exceptions\CaptchaException;
 | 
			
		||||
use Illuminate\Support\Arr;
 | 
			
		||||
 | 
			
		||||
final readonly class ImageBody
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private array $backgrounds,
 | 
			
		||||
        private array $fonts,
 | 
			
		||||
        private array $fontColors,
 | 
			
		||||
        private int $width = 300,
 | 
			
		||||
        private int $height = 240,
 | 
			
		||||
        private int $angle = 20,
 | 
			
		||||
        private array | int $fontSize = [20, 50],
 | 
			
		||||
        private int $numberLines = 3,
 | 
			
		||||
        private array $lineColors = []
 | 
			
		||||
    ) {
 | 
			
		||||
        if ($this->width <= 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $width settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->height <= 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $height settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($this->backgrounds) < 1) {
 | 
			
		||||
            throw new CaptchaException('Invalid $backgrounds parameter.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($this->fonts) < 1) {
 | 
			
		||||
            throw new CaptchaException('Invalid $fonts parameter.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($this->fontColors) < 1) {
 | 
			
		||||
            throw new CaptchaException('Invalid $fontColors parameter.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->angle < 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $angle settings.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!is_integer($this->fontSize) && !is_array($this->fontSize)) {
 | 
			
		||||
            throw new CaptchaException('The $fontSize parameter is not an array or integer.');
 | 
			
		||||
        } elseif (is_array($this->fontSize)) {
 | 
			
		||||
            if (count($this->fontSize) > 2) {
 | 
			
		||||
                throw new CaptchaException('The array of $fontSize parameters contains more than 2 keys.');
 | 
			
		||||
            }
 | 
			
		||||
            if ($this->fontSize[0] > $this->fontSize[1]) {
 | 
			
		||||
                throw new CaptchaException('The number of $fontSize[1] is less than $fontSize[0].');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getBackgrounds(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backgrounds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomBackground(): string
 | 
			
		||||
    {
 | 
			
		||||
        return Arr::random($this->getBackgrounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getFonts(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->fonts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomFont(): string
 | 
			
		||||
    {
 | 
			
		||||
        return Arr::random($this->getFonts());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getFontColors(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->fontColors;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomFontColor(): string
 | 
			
		||||
    {
 | 
			
		||||
        return Arr::random($this->getFontColors());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getWidth(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->width;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getHeight(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->height;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getAngle(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->angle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomAngle(): int
 | 
			
		||||
    {
 | 
			
		||||
        return mt_rand($this->getAngle() * -1, $this->getAngle());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getNumberLines(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->numberLines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fontSize(): int
 | 
			
		||||
    {
 | 
			
		||||
        $fontSize = $this->fontSize;
 | 
			
		||||
        if (is_integer($fontSize)) {
 | 
			
		||||
            return $fontSize;
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($fontSize[1])) {
 | 
			
		||||
            return $fontSize[0];
 | 
			
		||||
        }
 | 
			
		||||
        return random_int($fontSize[0], $fontSize[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getLineColors(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->lineColors;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										147
									
								
								app/Captcha/Config/ImageHead.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								app/Captcha/Config/ImageHead.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Config;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Exceptions\CaptchaException;
 | 
			
		||||
use Illuminate\Support\Arr;
 | 
			
		||||
 | 
			
		||||
final readonly class ImageHead
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private array $backgrounds,
 | 
			
		||||
        private array $fonts,
 | 
			
		||||
        private array $fontColors,
 | 
			
		||||
        private int $width = 150,
 | 
			
		||||
        private int $height = 40,
 | 
			
		||||
        private int $textPaddingTop = 5,
 | 
			
		||||
        private int $textPaddingLeft = 10,
 | 
			
		||||
        private int $angle = 20,
 | 
			
		||||
        private int $numberLines = 3,
 | 
			
		||||
        private array $lineColors = []
 | 
			
		||||
    ) {
 | 
			
		||||
        if ($this->width <= 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $width settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->height <= 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $height settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->textPaddingTop < 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $textPaddingTop settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->textPaddingLeft < 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $textPaddingLeft settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($this->backgrounds) < 1) {
 | 
			
		||||
            throw new CaptchaException('Invalid $backgrounds parameter.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($this->fonts) < 1) {
 | 
			
		||||
            throw new CaptchaException('Invalid $fonts parameter.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($this->fontColors) < 1) {
 | 
			
		||||
            throw new CaptchaException('Invalid $fontColors parameter.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->angle < 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $angle settings.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->numberLines < 0) {
 | 
			
		||||
            throw new CaptchaException('Incorrect $numberLines settings.');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getBackgrounds(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backgrounds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomBackground(): string
 | 
			
		||||
    {
 | 
			
		||||
        return Arr::random($this->getBackgrounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getFonts(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->fonts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomFont(): string
 | 
			
		||||
    {
 | 
			
		||||
        return Arr::random($this->getFonts());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getFontColors(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->fontColors;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomFontColor(): string
 | 
			
		||||
    {
 | 
			
		||||
        return Arr::random($this->getFontColors());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getWidth(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->width;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getHeight(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->height;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getTextPaddingTop(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->textPaddingTop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getTextPaddingLeft(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->textPaddingLeft;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getAngle(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->angle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function randomAngle(): int
 | 
			
		||||
    {
 | 
			
		||||
        return mt_rand($this->getAngle() * -1, $this->getAngle());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getNumberLines(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->numberLines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getLineColors(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->lineColors;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								app/Captcha/Contracts/ImageBody.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/Captcha/Contracts/ImageBody.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Contracts;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Config\ImageBody as ImageBodyConfig;
 | 
			
		||||
use App\Captcha\Dto\ImageBody as ImageBodyDto;
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
 | 
			
		||||
interface ImageBody
 | 
			
		||||
{
 | 
			
		||||
    public function processing(Symbols $symbols, ImageBodyConfig $config): ImageBodyDto;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								app/Captcha/Contracts/ImageHead.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/Captcha/Contracts/ImageHead.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Contracts;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Dto\ImageHead as ImageHeadDto;
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
use App\Captcha\Config\ImageHead as ImageHeadConfig;
 | 
			
		||||
 | 
			
		||||
interface ImageHead
 | 
			
		||||
{
 | 
			
		||||
    public function processing(Symbols $symbols, ImageHeadConfig $config): ImageHeadDto;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								app/Captcha/Contracts/Type.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/Captcha/Contracts/Type.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Contracts;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
 | 
			
		||||
interface Type
 | 
			
		||||
{
 | 
			
		||||
    public function getSymbols(): Symbols;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								app/Captcha/Dto/Coordinators.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/Captcha/Dto/Coordinators.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
final readonly class Coordinators
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private int $x1,
 | 
			
		||||
        private int $y1,
 | 
			
		||||
        private int $x2,
 | 
			
		||||
        private int $y2,
 | 
			
		||||
        private int $x3,
 | 
			
		||||
        private int $y3,
 | 
			
		||||
        private int $x4,
 | 
			
		||||
        private int $y4
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * lower left x-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getX1(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->x1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * lower left y-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getY1(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->y1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * lower right x-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getX2(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->x2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * lower right y-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getY2(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->y2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * upper right x-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getX3(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->x3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * upper right y-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getY3(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->y3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * upper left x-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getX4(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->x4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * upper left y-coordinate
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getY4(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->y4;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								app/Captcha/Dto/Image.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/Captcha/Dto/Image.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
final readonly class Image
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private string $imageBase64,
 | 
			
		||||
        private int $width,
 | 
			
		||||
        private int $height
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getImageBase64(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->imageBase64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getWidth(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->width;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getHeight(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->height;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								app/Captcha/Dto/ImageBody.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/Captcha/Dto/ImageBody.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
final readonly class ImageBody
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private Image $image,
 | 
			
		||||
        private array $coordinators
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Image
 | 
			
		||||
     */
 | 
			
		||||
    public function getImage(): Image
 | 
			
		||||
    {
 | 
			
		||||
        return $this->image;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Coordinators
 | 
			
		||||
     */
 | 
			
		||||
    public function getCoordinators(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->coordinators;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								app/Captcha/Dto/ImageHead.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/Captcha/Dto/ImageHead.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
final readonly class ImageHead
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private Image $image
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Image
 | 
			
		||||
     */
 | 
			
		||||
    public function getImage(): Image
 | 
			
		||||
    {
 | 
			
		||||
        return $this->image;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								app/Captcha/Dto/Sector.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/Captcha/Dto/Sector.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
final readonly class Sector
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private int $x,
 | 
			
		||||
        private int $y,
 | 
			
		||||
        private int $width,
 | 
			
		||||
        private int $height,
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getX(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->x;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getY(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->y;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getWidth(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->width;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getHeight(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->height;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								app/Captcha/Dto/Sectors.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Captcha/Dto/Sectors.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
final class Sectors
 | 
			
		||||
{
 | 
			
		||||
    private array $points = [];
 | 
			
		||||
 | 
			
		||||
    public function add(int|float $x, int|float $y, int|float $width, int|float $height): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->points[] = new Sector((int) $x, (int) $y, (int) $width, (int) $height);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function random(): Sector
 | 
			
		||||
    {
 | 
			
		||||
        $key = array_rand($this->points);
 | 
			
		||||
        $sector = $this->points[$key];
 | 
			
		||||
        unset($this->points[$key]);
 | 
			
		||||
 | 
			
		||||
        return $sector;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								app/Captcha/Dto/Symbols.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/Captcha/Dto/Symbols.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Dto;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Enums\SymbolType;
 | 
			
		||||
 | 
			
		||||
final readonly class Symbols
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private array $success,
 | 
			
		||||
        private array $fakes,
 | 
			
		||||
        private SymbolType $type
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getSuccess(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getFakes(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->fakes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return SymbolType
 | 
			
		||||
     */
 | 
			
		||||
    public function getType(): SymbolType
 | 
			
		||||
    {
 | 
			
		||||
        return $this->type;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								app/Captcha/Enums/SymbolType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/Captcha/Enums/SymbolType.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Enums;
 | 
			
		||||
 | 
			
		||||
Enum SymbolType {
 | 
			
		||||
    case String;
 | 
			
		||||
    case ImagePath;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								app/Captcha/Exceptions/CaptchaException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/Captcha/Exceptions/CaptchaException.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Exceptions;
 | 
			
		||||
 | 
			
		||||
final class CaptchaException extends \Exception
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								app/Captcha/Images/Body.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								app/Captcha/Images/Body.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Images;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Config\ImageBody as ImageBodyConfig;
 | 
			
		||||
use App\Captcha\Contracts\ImageLines;
 | 
			
		||||
use App\Captcha\Dto\Coordinators;
 | 
			
		||||
use App\Captcha\Dto\Image as ImageDto;
 | 
			
		||||
use App\Captcha\Dto\ImageBody as ImageBodyDto;
 | 
			
		||||
use App\Captcha\Dto\Sector;
 | 
			
		||||
use App\Captcha\Dto\Sectors;
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
use App\Captcha\Contracts\ImageBody;
 | 
			
		||||
use App\Captcha\Enums\SymbolType;
 | 
			
		||||
use App\Captcha\Contracts\ImageManager;
 | 
			
		||||
 | 
			
		||||
final class Body implements ImageBody
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly ImageManager $imageManager,
 | 
			
		||||
        private readonly ImageLines $imageLines
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    public function processing(Symbols $symbols, ImageBodyConfig $config): ImageBodyDto
 | 
			
		||||
    {
 | 
			
		||||
        $image = $this->imageManager->createImage($config->getWidth(), $config->getHeight());
 | 
			
		||||
        $image->insertBackground($config->randomBackground());
 | 
			
		||||
 | 
			
		||||
        $imageData = match ($symbols->getType()) {
 | 
			
		||||
            SymbolType::String => $this->processingString($image, $symbols, $config),
 | 
			
		||||
        };
 | 
			
		||||
        $image = $imageData['image'];
 | 
			
		||||
 | 
			
		||||
        if ($config->getNumberLines() > 0) {
 | 
			
		||||
            $this->imageLines->processing(image: $image, colors: $config->getLineColors(), lines: $config->getNumberLines());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $image = new ImageDto(
 | 
			
		||||
            imageBase64: $image->encode(),
 | 
			
		||||
            width: $image->getWidth(),
 | 
			
		||||
            height: $image->getHeight()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return new ImageBodyDto(
 | 
			
		||||
            image: $image,
 | 
			
		||||
            coordinators: $imageData['coordinators']
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function processingString(Image $image, Symbols $symbols, ImageBodyConfig $config): array
 | 
			
		||||
    {
 | 
			
		||||
        $sectors = $this->calculateSectors($symbols, $image->getWidth(), $image->getHeight());
 | 
			
		||||
        $coordinators = [];
 | 
			
		||||
        foreach ($symbols->getSuccess() as $number => $symbol) {
 | 
			
		||||
            $coordinators[] = $this->processingStringSymbol($image, $symbol, $config, $sectors->random());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($symbols->getFakes())) {
 | 
			
		||||
            foreach ($symbols->getFakes() as $number => $symbol) {
 | 
			
		||||
                $this->processingStringSymbol($image, $symbol, $config, $sectors->random());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'image' => $image,
 | 
			
		||||
            'coordinators' => $coordinators
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function processingStringSymbol(Image &$image, string|int $symbol, ImageBodyConfig $config, Sector $sector): Coordinators
 | 
			
		||||
    {
 | 
			
		||||
        $fontSize = $config->fontSize();
 | 
			
		||||
        if ($fontSize > $sector->getHeight()) {
 | 
			
		||||
            $fontSize = $sector->getHeight();
 | 
			
		||||
        }
 | 
			
		||||
        $fontPathFile = $config->randomFont();
 | 
			
		||||
        $fontColor = $config->randomFontColor();
 | 
			
		||||
        $angle = $config->randomAngle();
 | 
			
		||||
        $marginLeft = mt_rand($sector->getX(), $sector->getX() + $sector->getWidth() - $fontSize);
 | 
			
		||||
        $marginTop = mt_rand($sector->getY(), $sector->getY() + $sector->getHeight() - $fontSize);
 | 
			
		||||
 | 
			
		||||
        return $image->addText(
 | 
			
		||||
            text: $symbol,
 | 
			
		||||
            x: $marginLeft,
 | 
			
		||||
            y: $marginTop,
 | 
			
		||||
            size: $fontSize,
 | 
			
		||||
            angle: $angle,
 | 
			
		||||
            hexColor: $fontColor,
 | 
			
		||||
            fontName: $fontPathFile
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function calculateSectors(Symbols $symbols, int $width, int $height): Sectors
 | 
			
		||||
    {
 | 
			
		||||
        $points = count($symbols->getSuccess()) + count($symbols->getFakes());
 | 
			
		||||
        $sumFloors = 3;
 | 
			
		||||
        $sumRooms = ceil($points / $sumFloors);
 | 
			
		||||
 | 
			
		||||
        $heightFloor = floor($height / $sumFloors);
 | 
			
		||||
        $widthFloor  = floor($width / $sumRooms);
 | 
			
		||||
 | 
			
		||||
        $sectors = new Sectors();
 | 
			
		||||
        for ($floor = 0; $floor < $sumFloors; $floor++) {
 | 
			
		||||
            $y = $heightFloor * $floor;
 | 
			
		||||
            for ($room = 0; $room < $sumRooms; $room++) {
 | 
			
		||||
                $sectors->add(
 | 
			
		||||
                    x: ($widthFloor * $room),
 | 
			
		||||
                    y: $y,
 | 
			
		||||
                    width:  $widthFloor,
 | 
			
		||||
                    height: $heightFloor
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $sectors;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								app/Captcha/Images/Head.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								app/Captcha/Images/Head.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Images;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Config\ImageHead as ImageHeadConfig;
 | 
			
		||||
use App\Captcha\Contracts\ImageLines;
 | 
			
		||||
use App\Captcha\Dto\Image as ImageDto;
 | 
			
		||||
use App\Captcha\Dto\ImageHead as ImageHeadDto;
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
use App\Captcha\Contracts\ImageHead;
 | 
			
		||||
use \App\Captcha\Contracts\Image as ImageContract;
 | 
			
		||||
use App\Captcha\Enums\SymbolType;
 | 
			
		||||
use App\Captcha\Contracts\ImageManager;
 | 
			
		||||
 | 
			
		||||
final class Head implements ImageHead
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly ImageManager $imageManager,
 | 
			
		||||
        private readonly ImageLines $imageLines
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    public function processing(Symbols $symbols, ImageHeadConfig $config): ImageHeadDto
 | 
			
		||||
    {
 | 
			
		||||
        $image = $this->imageManager->createImage($config->getWidth(), $config->getHeight());
 | 
			
		||||
        $image->insertBackground($config->randomBackground());
 | 
			
		||||
 | 
			
		||||
        $image = match ($symbols->getType()) {
 | 
			
		||||
            SymbolType::String => $this->processingString($image, $symbols, $config),
 | 
			
		||||
        };
 | 
			
		||||
        if ($config->getNumberLines() > 0) {
 | 
			
		||||
            $this->imageLines->processing(image: $image, colors: $config->getLineColors(), lines: $config->getNumberLines());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $image = new ImageDto(
 | 
			
		||||
            imageBase64: $image->encode(),
 | 
			
		||||
            width: $image->getWidth(),
 | 
			
		||||
            height: $image->getHeight()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return new ImageHeadDto(image: $image);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function processingString(ImageContract $image, Symbols $symbols, ImageHeadConfig $config): Image
 | 
			
		||||
    {
 | 
			
		||||
        $textLeftPadding = $config->getTextPaddingLeft();
 | 
			
		||||
        $textTopPadding  = $config->getTextPaddingTop();
 | 
			
		||||
        $countSymbols    = count($symbols->getSuccess());
 | 
			
		||||
        $widthPrefix     = $image->getWidth() - $textLeftPadding;
 | 
			
		||||
        $imageHeight     = $image->getHeight() - $textTopPadding;
 | 
			
		||||
 | 
			
		||||
        foreach ($symbols->getSuccess() as $number => $symbol) {
 | 
			
		||||
            $marginLeft = $textLeftPadding + $number * $widthPrefix / $countSymbols;
 | 
			
		||||
 | 
			
		||||
            $image->addText(
 | 
			
		||||
                text:     $symbol,
 | 
			
		||||
                x:        intval($marginLeft),
 | 
			
		||||
                y:        $textTopPadding,
 | 
			
		||||
                size:     $this->randomFontSize($imageHeight),
 | 
			
		||||
                angle:    $config->randomAngle(),
 | 
			
		||||
                hexColor: $config->randomFontColor(),
 | 
			
		||||
                fontName: $config->randomFont()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $image;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function randomFontSize(int $imageHeight): int
 | 
			
		||||
    {
 | 
			
		||||
        return mt_rand($imageHeight - 10, $imageHeight);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								app/Captcha/Type.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/Captcha/Type.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Contracts\Type as TypeContract;
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
 | 
			
		||||
abstract class Type implements TypeContract
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly array $config
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    abstract public function getSymbols(): Symbols;
 | 
			
		||||
 | 
			
		||||
    final public function getConfig(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->config;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function getConfigValue(string | int $name, mixed $default = null): mixed
 | 
			
		||||
    {
 | 
			
		||||
        $config = $this->getConfig();
 | 
			
		||||
        return $config[$name] ?? $default;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								app/Captcha/Types/StringType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								app/Captcha/Types/StringType.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Captcha\Types;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Dto\Symbols;
 | 
			
		||||
use App\Captcha\Enums\SymbolType;
 | 
			
		||||
use App\Captcha\Exceptions\CaptchaException;
 | 
			
		||||
use App\Captcha\Type;
 | 
			
		||||
use Illuminate\Support\Arr;
 | 
			
		||||
 | 
			
		||||
final class StringType extends Type
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function getSymbols(): Symbols
 | 
			
		||||
    {
 | 
			
		||||
        $lengthSymbols = $this->lengthSymbols();
 | 
			
		||||
        $lengthFakeSymbols = $this->lengthFakeSymbols();
 | 
			
		||||
 | 
			
		||||
        $success = $this->randomSymbols($lengthSymbols);
 | 
			
		||||
        $fakes = [];
 | 
			
		||||
        if (!empty($lengthFakeSymbols)) {
 | 
			
		||||
            $fakes = $this->randomSymbols($lengthFakeSymbols, $fakes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new Symbols(
 | 
			
		||||
            success: $success,
 | 
			
		||||
            fakes: $fakes,
 | 
			
		||||
            type: SymbolType::String
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param int $min
 | 
			
		||||
     * @param int $max
 | 
			
		||||
     * @param array $except
 | 
			
		||||
     * @return array
 | 
			
		||||
     * @throws CaptchaException
 | 
			
		||||
     */
 | 
			
		||||
    private function randomSymbols(int $lenght, array $except = []): array
 | 
			
		||||
    {
 | 
			
		||||
        $symbols = $this->symbols();
 | 
			
		||||
        if (!empty($except)) {
 | 
			
		||||
            $symbols = array_diff($symbols, $except);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (count($symbols) < $lenght) {
 | 
			
		||||
            throw new CaptchaException('The number of characters is less than $lenght.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Arr::random($symbols, $lenght);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     * @throws CaptchaException
 | 
			
		||||
     */
 | 
			
		||||
    private function symbols(): array
 | 
			
		||||
    {
 | 
			
		||||
        $symbols = $this->getConfigValue('symbols');
 | 
			
		||||
        if (!is_array($symbols)) {
 | 
			
		||||
            throw new CaptchaException('The symbols parameter is not an array.');
 | 
			
		||||
        }
 | 
			
		||||
        return $symbols;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function lengthSymbols(): int
 | 
			
		||||
    {
 | 
			
		||||
        $length = $this->getConfigValue('length_symbols');
 | 
			
		||||
        if (is_integer($length)) {
 | 
			
		||||
            return $length;
 | 
			
		||||
        }
 | 
			
		||||
        if (!is_array($length)) {
 | 
			
		||||
            throw new CaptchaException('The length_symbols parameter is not an array or integer.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($length) > 2) {
 | 
			
		||||
            throw new CaptchaException('The array of length_symbols parameters contains more than 2 keys.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($length[0] > $length[1]) {
 | 
			
		||||
            throw new CaptchaException('The number of length_symbols[1] is less than length_symbols[0].');
 | 
			
		||||
        }
 | 
			
		||||
        return random_int($length[0], $length[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function lengthFakeSymbols(): int
 | 
			
		||||
    {
 | 
			
		||||
        $length = $this->getConfigValue('length_fake_symbols');
 | 
			
		||||
        if (is_integer($length)) {
 | 
			
		||||
            return $length;
 | 
			
		||||
        }
 | 
			
		||||
        if (is_null($length) || $length === false) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (!is_array($length)) {
 | 
			
		||||
            throw new CaptchaException('The length_fake_symbols parameter is not an array or integer or null or false.');
 | 
			
		||||
        }
 | 
			
		||||
        if (count($length) > 2) {
 | 
			
		||||
            throw new CaptchaException('The array of length_fake_symbols parameters contains more than 2 keys.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($length[0] > $length[1]) {
 | 
			
		||||
            throw new CaptchaException('The number of length_fake_symbols[1] is less than length_fake_symbols[0].');
 | 
			
		||||
        }
 | 
			
		||||
        return random_int($length[0], $length[1]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								app/Http/Controllers/Api/V1/CaptchaController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Http/Controllers/Api/V1/CaptchaController.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers\Api\V1;
 | 
			
		||||
 | 
			
		||||
use App\Http\Resources\Api\V1\Captcha;
 | 
			
		||||
use App\Services\Api\V1\CaptchaService;
 | 
			
		||||
use Illuminate\Http\JsonResponse;
 | 
			
		||||
 | 
			
		||||
final class CaptchaController extends Controller
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly CaptchaService $captchaService
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    public function getCaptcha(): JsonResponse
 | 
			
		||||
    {
 | 
			
		||||
        $result = $this->captchaService->generate();
 | 
			
		||||
        if (!$result->isSuccess()) {
 | 
			
		||||
            return response()->json($result->getData())->setStatusCode($result->getCode());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response()->json(new Captcha($result));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<?php
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers;
 | 
			
		||||
namespace App\Http\Controllers\Api\V1;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
 | 
			
		||||
use Illuminate\Foundation\Validation\ValidatesRequests;
 | 
			
		||||
							
								
								
									
										31
									
								
								app/Http/Resources/Api/V1/Captcha.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								app/Http/Resources/Api/V1/Captcha.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Resources\Api\V1;
 | 
			
		||||
 | 
			
		||||
use App\ServiceResults\Api\V1\CaptchaService\Captcha as CaptchaResult;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use Illuminate\Http\Resources\Json\JsonResource;
 | 
			
		||||
 | 
			
		||||
final class Captcha extends JsonResource
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var CaptchaResult
 | 
			
		||||
     */
 | 
			
		||||
    public $resource;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Transform the resource into an array.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  Request $request
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function toArray(Request $request): array
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'image_base64'      => $this->resource->getImageBase64(),
 | 
			
		||||
            'image_text_base64' => $this->resource->getImageTextBase64(),
 | 
			
		||||
            'captcha_key'       => $this->resource->getKey()
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +1,40 @@
 | 
			
		||||
<?php
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Providers;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Contracts\ImageBody;
 | 
			
		||||
use App\Captcha\Contracts\ImageHead;
 | 
			
		||||
use App\Captcha\Contracts\ImageLines;
 | 
			
		||||
use App\Captcha\Contracts\ImageManager as ImageManagerContract;
 | 
			
		||||
use App\Captcha\Images\Body;
 | 
			
		||||
use App\Captcha\Images\Head;
 | 
			
		||||
use App\Captcha\Images\ImageManager;
 | 
			
		||||
use App\Captcha\Images\Lines;
 | 
			
		||||
use App\Services\Api\V1\CaptchaService;
 | 
			
		||||
use Illuminate\Contracts\Foundation\Application;
 | 
			
		||||
use Illuminate\Support\ServiceProvider;
 | 
			
		||||
 | 
			
		||||
class AppServiceProvider extends ServiceProvider
 | 
			
		||||
final class AppServiceProvider extends ServiceProvider
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Register any application services.
 | 
			
		||||
     */
 | 
			
		||||
    public function register(): void
 | 
			
		||||
    {
 | 
			
		||||
        //
 | 
			
		||||
        $this->app->bind(ImageManagerContract::class, function () {
 | 
			
		||||
            return new ImageManager(imageClassName: config('captcha.imageClass'));
 | 
			
		||||
        });
 | 
			
		||||
        $this->app->bind(ImageHead::class, Head::class);
 | 
			
		||||
        $this->app->bind(ImageBody::class, Body::class);
 | 
			
		||||
        $this->app->bind(ImageLines::class, Lines::class);
 | 
			
		||||
 | 
			
		||||
        $this->app->bind(CaptchaService::class, function (Application $app) {
 | 
			
		||||
            return new CaptchaService(
 | 
			
		||||
                config: config('captcha', []),
 | 
			
		||||
                imageHead: $app->make(ImageHead::class),
 | 
			
		||||
                imageBody: $app->make(ImageBody::class)
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								app/ServiceResults/Api/V1/CaptchaService/Captcha.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/ServiceResults/Api/V1/CaptchaService/Captcha.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\ServiceResults\Api\V1\CaptchaService;
 | 
			
		||||
 | 
			
		||||
use App\ServiceResults\ServiceResult;
 | 
			
		||||
 | 
			
		||||
final class Captcha extends ServiceResult
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly string $imageBase64,
 | 
			
		||||
        private readonly string $imageTextBase64,
 | 
			
		||||
        private readonly string $key
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getImageBase64(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->imageBase64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getImageTextBase64(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->imageTextBase64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getKey(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->key;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										97
									
								
								app/Services/Api/V1/CaptchaService.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								app/Services/Api/V1/CaptchaService.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
<?php declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Services\Api\V1;
 | 
			
		||||
 | 
			
		||||
use App\Captcha\Contracts\ImageBody;
 | 
			
		||||
use App\Captcha\Contracts\ImageHead;
 | 
			
		||||
use App\Captcha\Contracts\Type;
 | 
			
		||||
use App\ServiceResults\Api\V1\CaptchaService\Captcha;
 | 
			
		||||
use App\ServiceResults\ServiceResultError;
 | 
			
		||||
use App\Services\Service;
 | 
			
		||||
use Illuminate\Support\Arr;
 | 
			
		||||
use App\Captcha\Config\ImageHead as ImageHeadConfig;
 | 
			
		||||
use App\Captcha\Config\ImageBody as ImageBodyConfig;
 | 
			
		||||
 | 
			
		||||
final class CaptchaService extends Service
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly array $config,
 | 
			
		||||
        private readonly ImageHead $imageHead,
 | 
			
		||||
        private readonly ImageBody $imageBody
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    public function generate(): ServiceResultError | Captcha
 | 
			
		||||
    {
 | 
			
		||||
        $types = $this->config['types'] ?? [];
 | 
			
		||||
        if (empty($types)) {
 | 
			
		||||
            $error = __('No captcha type settings!');
 | 
			
		||||
            report($error);
 | 
			
		||||
            return $this->errService($error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $type = Arr::random($types);
 | 
			
		||||
 | 
			
		||||
            /** @var Type $captcha */
 | 
			
		||||
            $typeCaptcha = new $type['class'](
 | 
			
		||||
                $type['params'] ?? []
 | 
			
		||||
            );
 | 
			
		||||
            $symbols = $typeCaptcha->getSymbols();
 | 
			
		||||
 | 
			
		||||
            $imageHeadConfig = $this->makeImageHeadConfig($this->config['image_head'] ?? []);
 | 
			
		||||
            $imageHead = $this->imageHead->processing($symbols, $imageHeadConfig);
 | 
			
		||||
            unset($imageHeadConfig);
 | 
			
		||||
 | 
			
		||||
            $imageBodyConfig = $this->makeImageBodyConfig($this->config['image_body'] ?? []);
 | 
			
		||||
            $imageBody = $this->imageBody->processing($symbols, $imageBodyConfig);
 | 
			
		||||
            unset($imageBodyConfig);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            dd($imageHead, $imageBody);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            report($e);
 | 
			
		||||
            return $this->errService('Captcha service error!');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return new Captcha(
 | 
			
		||||
            imageBase64: $imageHead->getImage()->getImageBase64(),
 | 
			
		||||
            imageTextBase64: $imageBody->getImage()->getImageBase64(),
 | 
			
		||||
            key: 'dddd'
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function makeImageHeadConfig(array $params): ImageHeadConfig
 | 
			
		||||
    {
 | 
			
		||||
        return new ImageHeadConfig(
 | 
			
		||||
            backgrounds: $params['backgrounds'] ?? [],
 | 
			
		||||
            fonts: $params['fonts'] ?? [],
 | 
			
		||||
            fontColors: $params['font_colors'] ?? [],
 | 
			
		||||
            width: $params['width'] ?? 150,
 | 
			
		||||
            height: $params['height'] ?? 40,
 | 
			
		||||
            textPaddingTop: $params['text_padding_top'] ?? 5,
 | 
			
		||||
            textPaddingLeft: $params['text_padding_left'] ?? 10,
 | 
			
		||||
            angle: $params['angle'] ?? 20,
 | 
			
		||||
            numberLines: $params['number_lines'] ?? 3,
 | 
			
		||||
            lineColors: $params['line_colors'] ?? []
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function makeImageBodyConfig(array $params): ImageBodyConfig
 | 
			
		||||
    {
 | 
			
		||||
        return new ImageBodyConfig(
 | 
			
		||||
            backgrounds: $params['backgrounds'] ?? [],
 | 
			
		||||
            fonts: $params['fonts'] ?? [],
 | 
			
		||||
            fontColors: $params['font_colors'] ?? [],
 | 
			
		||||
            width: $params['width'] ?? 300,
 | 
			
		||||
            height: $params['height'] ?? 240,
 | 
			
		||||
            angle: $params['angle'] ?? 20,
 | 
			
		||||
            fontSize: $params['font_size'] ?? [20, 50],
 | 
			
		||||
            numberLines: $params['number_lines'] ?? 3,
 | 
			
		||||
            lineColors: $params['line_colors'] ?? []
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								app/Services/Service.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								app/Services/Service.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Services;
 | 
			
		||||
 | 
			
		||||
use App\ServiceResults\ServiceResultArray;
 | 
			
		||||
use App\ServiceResults\ServiceResultError;
 | 
			
		||||
 | 
			
		||||
abstract class Service
 | 
			
		||||
{
 | 
			
		||||
    final protected function errValidate(string $message, array $errors = []): ServiceResultError
 | 
			
		||||
    {
 | 
			
		||||
        return $this->error(422, $message, $errors);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function errFobidden(string $message): ServiceResultError
 | 
			
		||||
    {
 | 
			
		||||
        return $this->error(403, $message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function errNotFound(string $message): ServiceResultError
 | 
			
		||||
    {
 | 
			
		||||
        return $this->error(404, $message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function errService(string $message): ServiceResultError
 | 
			
		||||
    {
 | 
			
		||||
        return $this->error(500, $message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function notAcceptable(string $message): ServiceResultError
 | 
			
		||||
    {
 | 
			
		||||
        return $this->error(406, $message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function ok(string $message = 'OK'): ServiceResultArray
 | 
			
		||||
    {
 | 
			
		||||
        return $this->result(['message' => $message]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function result(array $data = []): ServiceResultArray
 | 
			
		||||
    {
 | 
			
		||||
        return new ServiceResultArray(data: $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final protected function error(int $code, string $message, array $errors = []): ServiceResultError
 | 
			
		||||
    {
 | 
			
		||||
        return new ServiceResultError(
 | 
			
		||||
            message: $message,
 | 
			
		||||
            errors: $errors,
 | 
			
		||||
            code: $code
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,3 +13,5 @@ use Illuminate\Support\Facades\Route;
 | 
			
		||||
| be assigned to the "api" middleware group. Make something great!
 | 
			
		||||
|
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
Route::get('/captcha', [\App\Http\Controllers\Api\V1\CaptchaController::class, 'getCaptcha']);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user