Files
web-site/Internal/package-captcha/Render.php
T
2026-07-01 16:44:17 +03:00

617 lines
13 KiB
PHP

<?php namespace ZN\Captcha;
/**
* ZN PHP Web Framework
*
* "Simplicity is the ultimate sophistication." ~ Da Vinci
*
* @package ZN
* @license MIT [http://opensource.org/licenses/MIT]
* @author Ozan UYKUN [ozan@znframework.com]
*/
use ZN\Base;
use ZN\Image;
use ZN\Config;
use ZN\Singleton;
use ZN\Filesystem;
use ZN\Request\URL;
use ZN\DataTypes\Arrays;
use ZN\Cryptography\Encode;
class Render implements RenderInterface
{
/**
* Keeps settings.
*
* @var array
*/
protected $sets = [];
/**
* Get path.
*
* @var string
*/
protected $path = FILES_DIR ?? 'captcha/';
/**
* Keeps session class
*
* @var object
*/
protected $session;
/**
* Keeps session key
*
* @var string
*/
protected $key;
/**
* Keeps config variables
*/
protected $sizeWidthC,
$sizeHeightC;
/**
* Magic constructor
*
* @param void
*
* @return void
*/
public function __construct()
{
$this->session = Singleton::class('ZN\Storage\Session');
$this->key = md5('SystemCaptchaCodeData');
}
/**
* The image specifies the path to save.
*
* @param string $path
*
* @return Captcha
*/
public function path(string $path) : Render
{
$this->path = Base::suffix($path);
return $this;
}
/**
* Adjust the size of the captcha.
*
* @param int $width
* @param int $height
*
* @return Captcha
*/
public function size(int $width, int $height) : Render
{
$this->sets['size']['width'] = $width;
$this->sets['size']['height'] = $height;
return $this;
}
/**
* Sets the character type.
*
* @param string $type = 'alnum' - options[alnum|numeric|string|alpha|special|all]
*
* @return Captcha
*/
public function type(string $type = 'alnum') : Render
{
$this->sets['text']['type'] = $type;
return $this;
}
/**
* Sets the character width.
*
* @param int $param
*
* @return Captcha
*/
public function length(int $param) : Render
{
$this->sets['text']['length'] = $param;
return $this;
}
/**
* Sets the character angle.
*
* @param int $param
*
* @return Captcha
*/
public function angle(Float $param) : Render
{
$this->sets['text']['angle'] = $param;
return $this;
}
/**
* Add ttf fonts.
*
* @param array $fonts
*
* @return Captcha
*/
public function ttf(array $fonts) : Render
{
$this->sets['text']['ttf'] = $fonts;
return $this;
}
/**
* Sets the border color.
*
* @param string $color = NULL
*
* @return Captcha
*/
public function borderColor(?string $color = NULL) : Render
{
$this->sets['border']['status'] = true;
if( ! empty($color) )
{
$this->sets['border']['color'] = $this->convertColor($color);
}
return $this;
}
/**
* Sets the background color.
*
* @param string $color = NULL
*
* @return Captcha
*/
public function bgColor(string $color) : Render
{
$this->sets['background']['color'] = $this->convertColor($color);
return $this;
}
/**
* Add background pictures.
*
* @param array $image
*
* @return Captcha
*/
public function bgImage(array $image) : Render
{
$this->sets['background']['image'] = $image;
return $this;
}
/**
* Sets the text size.
*
* @param int $size
*
* @return Captcha
*/
public function textSize(int $size) : Render
{
$this->sets['text']['size'] = $size;
return $this;
}
/**
* Sets the text coordiante.
*
* @param int $x
* @param int $y
*
* @return Captcha
*/
public function textCoordinate(int $x = 0, int $y = 0) : Render
{
$this->sets['text']['x'] = $x;
$this->sets['text']['y'] = $y;
return $this;
}
/**
* Sets the text color.
*
* @param string $color
*
* @return Captcha
*/
public function textColor(string $color) : Render
{
$this->sets['text']['color'] = $this->convertColor($color);
return $this;
}
/**
* Sets the grid color.
*
* @param string $color
*
* @return Captcha
*/
public function gridColor(?string $color = NULL) : Render
{
$this->sets['grid']['status'] = true;
if( ! empty($color) )
{
$this->sets['grid']['color'] = $this->convertColor($color);
}
return $this;
}
/**
* Sets the grid space.
*
* @param int $x = 0
* @param int $y = 0
*
* @return Captcha
*/
public function gridSpace(int $x = 0, int $y = 0) : Render
{
if( ! empty($x) )
{
$this->sets['grid']['spaceX'] = $x;
}
if( ! empty($y) )
{
$this->sets['grid']['spaceY'] = $y;
}
return $this;
}
/**
* Completes the captcha creation process.
*
* @param bool $img = false
*
* @return string
*/
public function create(bool $img = false) : string
{
# Create Session
$this->session();
if( ! $this->getCode() )
{
return ''; // @codeCoverageIgnore
}
# Clean Captcha Images
$this->clean();
# Create Directory
$this->directory();
# Create Color
$this->color($file);
# Background Image
$this->background($file);
# Text
$this->text($file);
# Grid
$this->grid($file);
# Border
$this->border($file);
# Output
return $this->output($img, $file);
}
/**
* Returns the current captcha code.
*
* @param void
*
* @return string
*/
public function getCode() : string
{
return $this->session->select($this->key);
}
/**
* Protected Color
*/
protected function color(& $file = NULL)
{
$sizeset = $this->sets['size'];
$this->sizeWidthC = $sizeset['width'] ?? 100;
$this->sizeHeightC = $sizeset['height'] ?? 30;
$file = imagecreatetruecolor($this->sizeWidthC, $this->sizeHeightC);
}
/**
* Protected Create Directory
*/
protected function directory()
{
if( ! is_dir($this->path) )
{
mkdir($this->path); // @codeCoverageIgnore
}
}
/**
* Protected Session Insert
*/
protected function session()
{
$this->sets = array_merge
(
Config::default('ZN\Captcha\CaptchaDefaultConfiguration')
::get('ViewObjects', 'captcha'),
$this->sets
);
$this->session->insert($this->key, Encode\RandomPassword::create($this->sets['text']['length'] ?? 6, $this->sets['text']['type'] ?? 'alnum'));
}
/**
* Protected Output
*/
protected function output($img, & $file)
{
$filePath = $this->path . $this->name();
imagepng($file, $filePath);
$baseUrl = URL::base($filePath);
if( $img === true )
{
$captcha = '<img src="'.$baseUrl.'">';
}
else
{
$captcha = $baseUrl;
}
imagedestroy($file);
$this->sets = [];
return $captcha;
}
/**
* Protected Border
*/
protected function border(& $file)
{
$borderset = $this->sets['border'];
$borderStatusC = $borderset['status'] ?? true;
if( $borderStatusC === true )
{
$bordercolorC = $borderset['color'] ?? '200|200|200';
$borderColor = imagecolorallocate($file, ...explode('|', $bordercolorC));
# Top
imageline($file, 0, 0, $this->sizeWidthC, 0, $borderColor);
# Right
imageline($file, $this->sizeWidthC - 1, 0, $this->sizeWidthC - 1, $this->sizeHeightC, $borderColor);
# Bottom
imageline($file, 0, $this->sizeHeightC - 1, $this->sizeWidthC, $this->sizeHeightC - 1, $borderColor);
# Left
imageline($file, 0, 0, 0, $this->sizeHeightC - 1, $borderColor);
}
}
/**
* Protected Grid
*/
protected function grid(& $file)
{
$gridset = $this->sets['grid'];
$gridStatusC = $gridset['status'] ?? false;
if( $gridStatusC === true )
{
$gridSpaceXC = $gridset['spaceX'] ?? 12;
$gridSpaceYC = $gridset['spaceY'] ?? 4;
$gridColorC = $gridset['color'] ?? '240|240|240';
$gridIntervalX = $this->sizeWidthC / $gridSpaceXC;
if( ! isset($gridSpaceYC))
{
$gridIntervalY = (($this->sizeHeightC / $gridSpaceXC) * $gridIntervalX / 2); // @codeCoverageIgnore
}
else
{
$gridIntervalY = $this->sizeHeightC / $gridSpaceYC;
}
$gridColor = imagecolorallocate($file, ...explode('|', $gridColorC));
for( $x = 0 ; $x <= $this->sizeWidthC ; $x += $gridIntervalX )
{
imageline($file, $x, 0, $x, $this->sizeHeightC - 1, $gridColor);
}
for( $y = 0 ; $y <= $this->sizeWidthC; $y += $gridIntervalY )
{
imageline($file, 0, $y, $this->sizeWidthC, $y, $gridColor);
}
}
}
/**
* Protected Text
*/
protected function text(& $file)
{
$textset = $this->sets['text'];
$textSizeC = $textset['size'] ?? 10;
$textXC = $textset['x'] ?? 23;
$textYC = $textset['y'] ?? 9;
$textAngleC = $textset['angle'] ?? 0;
$textTTFC = $textset['ttf'] ?? [];
$textColorC = $textset['color'] ?? '0|0|0';
$fontColor = imagecolorallocate($file, ...explode('|', $textColorC));
if( ! empty($textTTFC) )
{
$textTTFC = $textTTFC[rand(0, count($textTTFC) - 1)];
$textTTFC = Base::suffix($textTTFC, '.ttf');
if( is_file($textTTFC) )
{
imagettftext
(
$file,
$textSizeC,
$textAngleC,
$textXC,
$textYC + $textSizeC,
$fontColor,
$textTTFC,
$this->getCode()
);
}
}
else
{
imagestring($file, $textSizeC, $textXC, $textYC, $this->getCode(), $fontColor);
}
}
/**
* Protected Background Image
*/
protected function background(& $file)
{
$bgset = $this->sets['background'];
$backgroundImageC = $bgset['image'] ?? [];
if( ! empty($backgroundImageC) )
{
if( is_array($backgroundImageC) )
{
$backgroundImageC = $backgroundImageC[rand(0, count($backgroundImageC) - 1)];
}
if( is_file($backgroundImageC) )
{
$infoExtension = strtolower(pathinfo($backgroundImageC, PATHINFO_EXTENSION));
switch( $infoExtension )
{
case 'jpeg':
case 'jpg' : $file = imagecreatefromjpeg($backgroundImageC); break;
case 'webp': $file = imagecreatefromwebp($backgroundImageC); break;
case 'png' : $file = imagecreatefrompng($backgroundImageC); break;
case 'gif' : $file = imagecreatefromgif($backgroundImageC); break; // @codeCoverageIgnore
default : $file = imagecreatefromjpeg($backgroundImageC);
}
}
}
else
{
$backgroundColorC = $bgset['color'] ?? '255|255|255';
$color = imagecolorallocate($file, ...explode('|', $backgroundColorC));
imagefill($file, 0, 0, $color);
}
}
/**
* protected clean captcha
*
* @param void
*
* @return void
*/
protected function clean()
{
$captcha = $this->path . $this->name();
$getCaptchaImages = preg_grep('/captcha\-\w+\.png/', Filesystem::getFiles($this->path, 'png'));
foreach( $getCaptchaImages as $image)
{
if( $this->name() !== $image )
{
$captcha = $this->path . $image;
if( is_file($captcha) )
{
unlink($captcha);
}
}
}
}
/**
* protected captcha name
*
* @param void
*
* @return string
*/
protected function name()
{
return 'captcha-' . $this->getCode() . '.png';
}
/**
* protected conver color
*
* @param string $color
*
* @return string
*/
protected function convertColor($color)
{
if( $convert = (Image\Properties::$colors[$color] ?? NULL) )
{
return $convert; // @codeCoverageIgnore
}
return $color;
}
}