Add captcha image generation functionality.

This commit is contained in:
Leonid Nikitin 2023-06-28 17:29:56 +06:00
parent 81635b4efa
commit a35b8db281
Signed by: kor-elf
GPG Key ID: 7DE8F80C5CEC2C0D
26 changed files with 1211 additions and 5 deletions

View 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;
}
}

View 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;
}
}

View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace App\Captcha\Enums;
Enum SymbolType {
case String;
case ImagePath;
}

View 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
View 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;
}
}

View 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
View 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;
}
}

View 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]);
}
}

View 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));
}
}

View File

@ -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\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;

View 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()
];
}
}

View File

@ -1,17 +1,40 @@
<?php <?php declare(strict_types=1);
namespace App\Providers; 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; use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider final class AppServiceProvider extends ServiceProvider
{ {
/** /**
* Register any application services. * Register any application services.
*/ */
public function register(): void 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)
);
});
} }
/** /**

View 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;
}
}

View 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
View 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
);
}
}

View File

@ -13,3 +13,5 @@ use Illuminate\Support\Facades\Route;
| be assigned to the "api" middleware group. Make something great! | be assigned to the "api" middleware group. Make something great!
| |
*/ */
Route::get('/captcha', [\App\Http\Controllers\Api\V1\CaptchaController::class, 'getCaptcha']);