PHP предоставляет два основных расширения для работы с изображениями: GD (встроенная библиотека, есть практически всегда) и Imagick (обёртка над ImageMagick -- мощным инструментом для обработки изображений). GD хорошо подходит для базовых операций, Imagick -- для сложных преобразований и профессиональной работы.
GD Library
Проверка и конфигурация
<?php
declare(strict_types=1);
// Check if GD is available
var_dump(extension_loaded('gd')); // true
// Get detailed GD info
$gdInfo = gd_info();
echo $gdInfo['GD Version']; // "bundled (2.1.0 compatible)"
echo $gdInfo['FreeType Support']; // true (for TTF fonts)
echo $gdInfo['JPEG Support']; // true
echo $gdInfo['PNG Support']; // true
echo $gdInfo['GIF Read Support']; // true
echo $gdInfo['GIF Create Support']; // true
echo $gdInfo['WebP Support']; // true
echo $gdInfo['AVIF Support']; // true (PHP 8.1+)
// Supported image types as bitmask
$types = imagetypes();
var_dump($types & IMG_PNG); // non-zero if PNG supported
var_dump($types & IMG_JPEG); // non-zero if JPEG supported
var_dump($types & IMG_WEBP); // non-zero if WebP supported
var_dump($types & IMG_AVIF); // non-zero if AVIF supported (PHP 8.1+)
Создание изображений
<?php
declare(strict_types=1);
// imagecreatetruecolor() — creates a true-color image (24-bit)
// Always use this over imagecreate()
$width = 800;
$height = 600;
$image = imagecreatetruecolor($width, $height);
// imagecreate() — palette-based image (limited to 256 colors, legacy)
// $image = imagecreate(800, 600); // avoid unless needed
// Enable alpha blending and save alpha channel
imagealphablending($image, true);
imagesavealpha($image, true);
// Allocate colors: imagecolorallocate(image, red, green, blue)
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
$red = imagecolorallocate($image, 255, 0, 0);
$green = imagecolorallocate($image, 0, 128, 0);
$blue = imagecolorallocate($image, 0, 0, 255);
// Colors with alpha (0 = opaque, 127 = fully transparent)
$semiTransparentRed = imagecolorallocatealpha($image, 255, 0, 0, 64);
$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
// Fill entire image with white
imagefill($image, 0, 0, $white);
// Output and cleanup
imagepng($image, '/tmp/blank.png');
imagedestroy($image);
Загрузка существующих изображений
<?php
declare(strict_types=1);
// Load from file based on format
$jpeg = imagecreatefromjpeg('/path/to/photo.jpg');
$png = imagecreatefrompng('/path/to/image.png');
$gif = imagecreatefromgif('/path/to/animation.gif');
$webp = imagecreatefromwebp('/path/to/image.webp');
// $avif = imagecreatefromavif('/path/to/image.avif'); // PHP 8.1+
// Load from string (e.g., from database or HTTP response)
$data = file_get_contents('/path/to/photo.jpg');
$image = imagecreatefromstring($data);
// Auto-detect format and load
function loadImage(string $path): \GdImage
{
$info = getimagesize($path);
if ($info === false) {
throw new \RuntimeException('Cannot read image: ' . $path);
}
return match ($info[2]) {
IMAGETYPE_JPEG => imagecreatefromjpeg($path),
IMAGETYPE_PNG => imagecreatefrompng($path),
IMAGETYPE_GIF => imagecreatefromgif($path),
IMAGETYPE_WEBP => imagecreatefromwebp($path),
IMAGETYPE_BMP => imagecreatefrombmp($path),
default => throw new \RuntimeException(
'Unsupported image type: ' . image_type_to_mime_type($info[2])
),
};
}
// Get image dimensions
$image = loadImage('/path/to/photo.jpg');
echo imagesx($image); // width in pixels
echo imagesy($image); // height in pixels
Рисование примитивов
<?php
declare(strict_types=1);
$image = imagecreatetruecolor(400, 400);
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
$red = imagecolorallocate($image, 220, 50, 50);
$blue = imagecolorallocate($image, 50, 50, 220);
$green = imagecolorallocate($image, 50, 180, 50);
imagefill($image, 0, 0, $white);
// imageline(image, x1, y1, x2, y2, color)
imageline($image, 0, 0, 399, 399, $black); // diagonal line
// imagerectangle(image, x1, y1, x2, y2, color) — outline
imagerectangle($image, 50, 50, 150, 150, $red);
// imagefilledrectangle — filled rectangle
imagefilledrectangle($image, 200, 50, 350, 150, $blue);
// imageellipse(image, cx, cy, width, height, color) — outline
imageellipse($image, 200, 300, 200, 100, $green);
// imagefilledellipse — filled ellipse
imagefilledellipse($image, 200, 300, 180, 80, $green);
// imagearc(image, cx, cy, width, height, start_deg, end_deg, color)
imagearc($image, 100, 300, 100, 100, 0, 270, $red); // 3/4 circle
// imagefilledarc — filled arc (for pie charts)
imagefilledarc(
$image, 100, 300, 100, 100, 0, 90,
$blue, IMG_ARC_PIE
);
// imagepolygon — polygon outline
$points = [100, 10, 50, 80, 150, 80]; // triangle (x1,y1, x2,y2, x3,y3)
imagepolygon($image, $points, 3, $black);
// imagesetthickness — line thickness
imagesetthickness($image, 3);
imageline($image, 0, 200, 399, 200, $red); // thick line
// imagesetpixel — individual pixel
imagesetpixel($image, 200, 200, $red);
imagepng($image, '/tmp/shapes.png');
imagedestroy($image);
Текст на изображениях
<?php
declare(strict_types=1);
$image = imagecreatetruecolor(600, 200);
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
imagefill($image, 0, 0, $white);
// imagestring() — built-in bitmap fonts (1-5)
imagestring($image, 5, 10, 10, 'Built-in font (size 5)', $black);
imagestring($image, 3, 10, 30, 'Built-in font (size 3)', $black);
imagestring($image, 1, 10, 50, 'Built-in font (size 1)', $black);
// imagestringup() — vertical text (bottom to top)
imagestringup($image, 5, 550, 190, 'Vertical', $black);
// imagettftext() — TrueType fonts (much better quality)
// imagettftext(image, size, angle, x, y, color, font_file, text)
$fontPath = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf';
if (file_exists($fontPath)) {
imagettftext($image, 20, 0, 10, 100, $black, $fontPath, 'Hello, World!');
imagettftext($image, 14, 0, 10, 130, $black, $fontPath, 'TrueType шрифты');
imagettftext($image, 24, 15, 10, 180, $black, $fontPath, 'Rotated text');
// imagettfbbox() — get bounding box for text (before drawing)
$bbox = imagettfbbox(20, 0, $fontPath, 'Hello, World!');
// Returns array of 8 values: x/y for 4 corners
// [0] lower-left X, [1] lower-left Y
// [2] lower-right X, [3] lower-right Y
// [4] upper-right X, [5] upper-right Y
// [6] upper-left X, [7] upper-left Y
$textWidth = abs($bbox[2] - $bbox[0]);
$textHeight = abs($bbox[7] - $bbox[1]);
// Center text horizontally
$x = (imagesx($image) - $textWidth) / 2;
imagettftext($image, 20, 0, (int) $x, 170, $black, $fontPath, 'Centered!');
}
imagepng($image, '/tmp/text.png');
imagedestroy($image);
Ресайз и кроп
<?php
declare(strict_types=1);
// Load original image
$source = imagecreatefromjpeg('/path/to/photo.jpg');
$srcW = imagesx($source);
$srcH = imagesy($source);
// --- Method 1: imagescale() (simplest, PHP 5.5+) ---
// Scale to width 400, preserve aspect ratio
$resized = imagescale($source, 400); // height auto-calculated
// or with specific height:
$resized = imagescale($source, 400, 300, IMG_BILINEAR_FIXED);
// --- Method 2: imagecopyresampled() (best quality) ---
$newW = 400;
$newH = 300;
$destination = imagecreatetruecolor($newW, $newH);
imagecopyresampled(
$destination, // destination image
$source, // source image
0, 0, // destination x, y
0, 0, // source x, y
$newW, $newH, // destination width, height
$srcW, $srcH // source width, height
);
// --- Method 3: imagecopyresized() (faster, lower quality) ---
// Same signature as imagecopyresampled, but uses nearest-neighbor
// Use only when speed is critical and quality doesn't matter
// --- Crop ---
// imagecrop(image, ['x' => ..., 'y' => ..., 'width' => ..., 'height' => ...])
$cropped = imagecrop($source, [
'x' => 100,
'y' => 50,
'width' => 300,
'height' => 300,
]);
// imagecropauto() — auto-crop based on content
$autoCropped = imagecropauto($source, IMG_CROP_DEFAULT);
// Modes: IMG_CROP_DEFAULT, IMG_CROP_TRANSPARENT, IMG_CROP_BLACK, IMG_CROP_WHITE
// --- Practical: resize maintaining aspect ratio ---
function resizeImage(
\GdImage $source,
int $maxWidth,
int $maxHeight,
): \GdImage {
$srcW = imagesx($source);
$srcH = imagesy($source);
$ratio = min($maxWidth / $srcW, $maxHeight / $srcH);
// Don't upscale
if ($ratio >= 1.0) {
$newW = $srcW;
$newH = $srcH;
} else {
$newW = (int) round($srcW * $ratio);
$newH = (int) round($srcH * $ratio);
}
$dest = imagecreatetruecolor($newW, $newH);
// Preserve transparency for PNG
imagealphablending($dest, false);
imagesavealpha($dest, true);
$transparent = imagecolorallocatealpha($dest, 0, 0, 0, 127);
imagefilledrectangle($dest, 0, 0, $newW - 1, $newH - 1, $transparent);
imagecopyresampled($dest, $source, 0, 0, 0, 0, $newW, $newH, $srcW, $srcH);
return $dest;
}
Rotate и Flip
<?php
declare(strict_types=1);
$image = imagecreatefromjpeg('/path/to/photo.jpg');
// imagerotate(image, angle, bg_color)
// Angle in degrees, counter-clockwise
$rotated90 = imagerotate($image, 90, 0); // 90° counter-clockwise
$rotated180 = imagerotate($image, 180, 0);
$rotated270 = imagerotate($image, 270, 0); // or -90°
// Arbitrary rotation (non-90 degree) fills corners with bg_color
$bg = imagecolorallocate($image, 255, 255, 255);
$rotated45 = imagerotate($image, 45, $bg);
// imageflip() — mirror image
imageflip($image, IMG_FLIP_HORIZONTAL); // mirror left-right
imageflip($image, IMG_FLIP_VERTICAL); // mirror top-bottom
imageflip($image, IMG_FLIP_BOTH); // mirror both (= 180° rotation)
Фильтры
<?php
declare(strict_types=1);
$image = imagecreatefromjpeg('/path/to/photo.jpg');
// imagefilter(image, filter_type, ...args)
// Grayscale — remove all color
imagefilter($image, IMG_FILTER_GRAYSCALE);
// Brightness — adjust brightness (-255 to 255)
imagefilter($image, IMG_FILTER_BRIGHTNESS, 50); // brighter
imagefilter($image, IMG_FILTER_BRIGHTNESS, -50); // darker
// Contrast — adjust contrast (-100 to 100)
imagefilter($image, IMG_FILTER_CONTRAST, -20); // more contrast
imagefilter($image, IMG_FILTER_CONTRAST, 20); // less contrast
// Colorize — add color tint (r, g, b, alpha)
imagefilter($image, IMG_FILTER_COLORIZE, 100, 0, 0, 0); // red tint
imagefilter($image, IMG_FILTER_COLORIZE, 0, 0, 100, 0); // blue tint
// Edge detection
imagefilter($image, IMG_FILTER_EDGEDETECT);
// Emboss
imagefilter($image, IMG_FILTER_EMBOSS);
// Gaussian blur (apply multiple times for stronger blur)
for ($i = 0; $i < 5; $i++) {
imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);
}
// Selective blur — preserves edges better
imagefilter($image, IMG_FILTER_SELECTIVE_BLUR);
// Sharpen — opposite of blur
imagefilter($image, IMG_FILTER_SHARPEN);
// Negate — invert colors
imagefilter($image, IMG_FILTER_NEGATE);
// Pixelate — block_size, advanced_pixelation
imagefilter($image, IMG_FILTER_PIXELATE, 10, true);
// Smooth — smoothing level (-8 to 8)
imagefilter($image, IMG_FILTER_SMOOTH, 5);
imagejpeg($image, '/tmp/filtered.jpg', 90);
imagedestroy($image);
Вывод и сохранение
<?php
declare(strict_types=1);
$image = imagecreatetruecolor(100, 100);
$red = imagecolorallocate($image, 255, 0, 0);
imagefill($image, 0, 0, $red);
// Save to file
imagejpeg($image, '/tmp/output.jpg', 90); // quality 0-100 (default 75)
imagepng($image, '/tmp/output.png', 6); // compression 0-9 (default 6)
imagegif($image, '/tmp/output.gif');
imagewebp($image, '/tmp/output.webp', 85); // quality 0-100
// imageavif($image, '/tmp/output.avif', 50, 4); // quality, speed (PHP 8.1+)
// Output directly to browser (null as filename)
header('Content-Type: image/png');
imagepng($image); // outputs to stdout
// Capture to string using output buffering
ob_start();
imagepng($image);
$pngData = ob_get_clean();
// Base64 encode for embedding in HTML
$base64 = base64_encode($pngData);
echo '<img src="data:image/png;base64,' . $base64 . '">';
imagedestroy($image);
EXIF и ориентация
<?php
declare(strict_types=1);
// exif_read_data() — read EXIF metadata from JPEG/TIFF
$exif = exif_read_data('/path/to/photo.jpg', 'ANY_TAG', true);
if ($exif !== false) {
echo $exif['FILE']['FileName'] ?? 'unknown';
echo $exif['COMPUTED']['Width'] ?? 'unknown';
echo $exif['COMPUTED']['Height'] ?? 'unknown';
echo $exif['IFD0']['Make'] ?? 'unknown'; // Camera manufacturer
echo $exif['IFD0']['Model'] ?? 'unknown'; // Camera model
echo $exif['EXIF']['DateTimeOriginal'] ?? 'unknown';
echo $exif['EXIF']['ExposureTime'] ?? 'unknown';
echo $exif['EXIF']['FNumber'] ?? 'unknown';
echo $exif['EXIF']['ISOSpeedRatings'] ?? 'unknown';
}
// Fix orientation (phones rotate via EXIF, not actual pixels)
function fixOrientation(string $path): \GdImage
{
$image = imagecreatefromjpeg($path);
$exif = @exif_read_data($path);
if ($exif === false || !isset($exif['Orientation'])) {
return $image;
}
return match ((int) $exif['Orientation']) {
2 => tap($image, fn($img) => imageflip($img, IMG_FLIP_HORIZONTAL)),
3 => imagerotate($image, 180, 0),
4 => tap($image, fn($img) => imageflip($img, IMG_FLIP_VERTICAL)),
5 => tap(imagerotate($image, 270, 0), fn($img) => imageflip($img, IMG_FLIP_HORIZONTAL)),
6 => imagerotate($image, 270, 0),
7 => tap(imagerotate($image, 90, 0), fn($img) => imageflip($img, IMG_FLIP_HORIZONTAL)),
8 => imagerotate($image, 90, 0),
default => $image,
};
}
// Helper to apply operation and return same image
function tap(\GdImage $image, callable $fn): \GdImage
{
$fn($image);
return $image;
}
Imagick (ImageMagick)
Imagick -- PHP-обёртка над ImageMagick. Поддерживает 200+ форматов изображений, предоставляет более мощные инструменты обработки и лучшее качество ресайза.
Базовые операции
<?php
declare(strict_types=1);
// Check availability
var_dump(extension_loaded('imagick')); // true
// Install: apt install php-imagick / pecl install imagick
// Create from file
$imagick = new \Imagick('/path/to/photo.jpg');
// Create from blob (string data)
$data = file_get_contents('/path/to/photo.jpg');
$imagick = new \Imagick();
$imagick->readImageBlob($data);
// Create blank image
$imagick = new \Imagick();
$imagick->newImage(800, 600, new \ImagickPixel('white'));
$imagick->setImageFormat('png');
// Get dimensions
echo $imagick->getImageWidth(); // 800
echo $imagick->getImageHeight(); // 600
// Get format
echo $imagick->getImageFormat(); // "JPEG", "PNG", etc.
// Get file size
echo $imagick->getImageLength(); // bytes
Ресайз и кроп
<?php
declare(strict_types=1);
$imagick = new \Imagick('/path/to/photo.jpg');
// resizeImage(width, height, filter, blur)
// Best quality filters: FILTER_LANCZOS, FILTER_MITCHELL
$imagick->resizeImage(400, 300, \Imagick::FILTER_LANCZOS, 1);
// thumbnailImage(width, height, bestfit, fill)
// Simpler API — bestfit=true preserves aspect ratio
$imagick->thumbnailImage(200, 200, true);
// scaleImage(width, height)
// Proportional if one dimension is 0
$imagick->scaleImage(400, 0); // scale to width 400, auto height
// cropImage(width, height, x, y)
$imagick->cropImage(300, 300, 50, 50);
// cropThumbnailImage(width, height) — crop to exact size
// Resizes and center-crops to fill exact dimensions
$imagick->cropThumbnailImage(200, 200);
Преобразования
<?php
declare(strict_types=1);
$imagick = new \Imagick('/path/to/photo.jpg');
// Rotate
$imagick->rotateImage(new \ImagickPixel('white'), 90);
// Flip / Flop
$imagick->flipImage(); // vertical mirror
$imagick->flopImage(); // horizontal mirror
// Blur
$imagick->blurImage(5, 3); // radius, sigma
$imagick->gaussianBlurImage(5, 3); // Gaussian blur
// Sharpen
$imagick->sharpenImage(2, 1); // radius, sigma
$imagick->unsharpMaskImage(2, 1, 1, 0.05); // radius, sigma, amount, threshold
// Auto-orient (fix EXIF rotation)
$imagick->autoOrient(); // PHP 8.3+ or imagick 3.4.4+
// Compression and quality
$imagick->setImageFormat('jpeg');
$imagick->setImageCompressionQuality(85); // 0-100
// Strip metadata (EXIF, profiles) — reduces file size
$imagick->stripImage();
// Output
$imagick->writeImage('/tmp/output.jpg');
echo $imagick->getImageBlob(); // get as string
Compositing (водяные знаки)
<?php
declare(strict_types=1);
// Add watermark to image
$photo = new \Imagick('/path/to/photo.jpg');
$watermark = new \Imagick('/path/to/watermark.png');
// Resize watermark to 20% of photo width
$wmWidth = (int) ($photo->getImageWidth() * 0.2);
$watermark->scaleImage($wmWidth, 0); // proportional
// Set opacity
$watermark->evaluateImage(\Imagick::EVALUATE_MULTIPLY, 0.3, \Imagick::CHANNEL_ALPHA);
// Position: bottom-right with 10px margin
$x = $photo->getImageWidth() - $watermark->getImageWidth() - 10;
$y = $photo->getImageHeight() - $watermark->getImageHeight() - 10;
// Composite
$photo->compositeImage(
$watermark,
\Imagick::COMPOSITE_OVER,
$x,
$y
);
$photo->writeImage('/tmp/watermarked.jpg');
$photo->destroy();
$watermark->destroy();
Сравнение GD и Imagick
| Характеристика | GD | Imagick |
|---|---|---|
| Установка | Обычно встроена | Нужна отдельная установка |
| Форматы | ~10 (JPEG, PNG, GIF, WebP, AVIF) | 200+ |
| Качество ресайза | Хорошее | Отличное (Lanczos) |
| Текст | Базовый (TTF) | Продвинутый |
| Память | Меньше | Больше |
| Скорость | Быстрее для простых задач | Быстрее для сложных |
| API | Процедурный | ООП |
| Слои/композиция | Ограничено | Полноценная |
Практические примеры
Генератор thumbnail
<?php
declare(strict_types=1);
final class ThumbnailGenerator
{
public function __construct(
private readonly int $maxWidth = 300,
private readonly int $maxHeight = 300,
private readonly int $quality = 85,
) {}
public function generate(string $sourcePath, string $outputPath): void
{
$info = getimagesize($sourcePath);
if ($info === false) {
throw new \RuntimeException('Invalid image: ' . $sourcePath);
}
[$srcW, $srcH] = $info;
$source = $this->loadImage($sourcePath, $info[2]);
// Calculate new dimensions
$ratio = min(
$this->maxWidth / $srcW,
$this->maxHeight / $srcH,
1.0 // never upscale
);
$newW = (int) round($srcW * $ratio);
$newH = (int) round($srcH * $ratio);
// Create thumbnail
$thumb = imagecreatetruecolor($newW, $newH);
// Preserve transparency
imagealphablending($thumb, false);
imagesavealpha($thumb, true);
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newW, $newH, $srcW, $srcH);
// Save based on extension
$extension = strtolower(pathinfo($outputPath, PATHINFO_EXTENSION));
match ($extension) {
'jpg', 'jpeg' => imagejpeg($thumb, $outputPath, $this->quality),
'png' => imagepng($thumb, $outputPath, 6),
'webp' => imagewebp($thumb, $outputPath, $this->quality),
'gif' => imagegif($thumb, $outputPath),
default => throw new \RuntimeException('Unsupported format: ' . $extension),
};
imagedestroy($source);
imagedestroy($thumb);
}
private function loadImage(string $path, int $type): \GdImage
{
return match ($type) {
IMAGETYPE_JPEG => imagecreatefromjpeg($path),
IMAGETYPE_PNG => imagecreatefrompng($path),
IMAGETYPE_GIF => imagecreatefromgif($path),
IMAGETYPE_WEBP => imagecreatefromwebp($path),
default => throw new \RuntimeException('Unsupported type'),
};
}
}
$generator = new ThumbnailGenerator(maxWidth: 200, maxHeight: 200);
$generator->generate('/uploads/photo.jpg', '/uploads/thumbs/photo_200.jpg');
Водяной знак (GD)
<?php
declare(strict_types=1);
function addTextWatermark(
string $imagePath,
string $outputPath,
string $text = 'SAMPLE',
string $fontPath = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
): void {
$image = imagecreatefromjpeg($imagePath);
$w = imagesx($image);
$h = imagesy($image);
// Semi-transparent white text
$color = imagecolorallocatealpha($image, 255, 255, 255, 80);
// Calculate font size relative to image
$fontSize = (int) ($w * 0.05);
// Get text bounding box
$bbox = imagettfbbox($fontSize, -30, $fontPath, $text);
$textW = abs($bbox[2] - $bbox[0]);
$textH = abs($bbox[7] - $bbox[1]);
// Center text
$x = (int) (($w - $textW) / 2);
$y = (int) (($h + $textH) / 2);
imagettftext($image, $fontSize, -30, $x, $y, $color, $fontPath, $text);
imagejpeg($image, $outputPath, 90);
imagedestroy($image);
}
addTextWatermark('/uploads/photo.jpg', '/uploads/watermarked.jpg');
Простой CAPTCHA генератор
<?php
declare(strict_types=1);
function generateCaptcha(
int $length = 5,
string $fontPath = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
): array {
// Generate random code
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[random_int(0, strlen($chars) - 1)];
}
// Create image
$width = 180;
$height = 60;
$image = imagecreatetruecolor($width, $height);
// Random background
$bg = imagecolorallocate(
$image,
random_int(220, 255),
random_int(220, 255),
random_int(220, 255)
);
imagefill($image, 0, 0, $bg);
// Add noise lines
for ($i = 0; $i < 6; $i++) {
$lineColor = imagecolorallocate(
$image,
random_int(150, 200),
random_int(150, 200),
random_int(150, 200)
);
imageline(
$image,
random_int(0, $width),
random_int(0, $height),
random_int(0, $width),
random_int(0, $height),
$lineColor
);
}
// Draw each character with random position/angle/color
for ($i = 0; $i < $length; $i++) {
$color = imagecolorallocate(
$image,
random_int(0, 100),
random_int(0, 100),
random_int(0, 100)
);
$angle = random_int(-20, 20);
$x = 15 + $i * 30 + random_int(-5, 5);
$y = 40 + random_int(-5, 5);
imagettftext($image, 22, $angle, $x, $y, $color, $fontPath, $code[$i]);
}
// Add noise dots
for ($i = 0; $i < 100; $i++) {
$dotColor = imagecolorallocate(
$image,
random_int(100, 200),
random_int(100, 200),
random_int(100, 200)
);
imagesetpixel(
$image,
random_int(0, $width - 1),
random_int(0, $height - 1),
$dotColor
);
}
// Return code and image data
ob_start();
imagepng($image);
$imageData = ob_get_clean();
imagedestroy($image);
return [
'code' => $code,
'image' => 'data:image/png;base64,' . base64_encode($imageData),
];
}
$captcha = generateCaptcha();
// Store $captcha['code'] in session, show $captcha['image'] to user
Извлечение информации об изображении
<?php
declare(strict_types=1);
function getImageInfo(string $path): array
{
if (!file_exists($path)) {
throw new \RuntimeException('File not found: ' . $path);
}
$info = getimagesize($path);
if ($info === false) {
throw new \RuntimeException('Not a valid image: ' . $path);
}
$result = [
'width' => $info[0],
'height' => $info[1],
'mime' => $info['mime'],
'type' => image_type_to_extension($info[2], false),
'bits' => $info['bits'] ?? null,
'channels' => $info['channels'] ?? null,
'file_size' => filesize($path),
'megapixels' => round(($info[0] * $info[1]) / 1_000_000, 2),
'aspect_ratio' => round($info[0] / $info[1], 4),
];
// EXIF data for JPEG
if ($info[2] === IMAGETYPE_JPEG && function_exists('exif_read_data')) {
$exif = @exif_read_data($path, 'EXIF', true);
if ($exif !== false) {
$result['exif'] = [
'camera' => trim(($exif['IFD0']['Make'] ?? '') . ' ' . ($exif['IFD0']['Model'] ?? '')),
'date' => $exif['EXIF']['DateTimeOriginal'] ?? null,
'iso' => $exif['EXIF']['ISOSpeedRatings'] ?? null,
'exposure' => $exif['EXIF']['ExposureTime'] ?? null,
'aperture' => $exif['EXIF']['FNumber'] ?? null,
];
}
}
return $result;
}
$info = getImageInfo('/path/to/photo.jpg');
// ['width' => 4032, 'height' => 3024, 'mime' => 'image/jpeg', ...]