Skip to content

Instantly share code, notes, and snippets.

@HDDen
Last active April 16, 2024 10:28
Show Gist options
  • Save HDDen/2e23003b13496bb7ac8eb76a1dc17d2a to your computer and use it in GitHub Desktop.
Save HDDen/2e23003b13496bb7ac8eb76a1dc17d2a to your computer and use it in GitHub Desktop.
Kama Thumbnails wrapper
<?php
// https://gist.github.com/HDDen/2e23003b13496bb7ac8eb76a1dc17d2a
// v 1.0.10
if (!defined('ABSPATH')){
die();
}
/*
$team_img = get_kama_img(20, array(
'width' => 720,
'height' => 200,
'2x' => true,
'crop' => true,
'html' => false,
'lazy' => true,
'q' => false, // 87
'full_img_in_src' => true,
'native_srcset' => true,
'sizes' => [
['width' => 1190],
['width' => 768],
['width' => 550, 'height' => 192],
['width' => 400, 'height' => 192, 'crop' => true],
],
'rise_small' => true,
'attr' => [
'class' => 'wow',
'sizes' => '(max-width: 544px) 100vw, (max-width: 1259px) 922px, 544px', // переопределить sizes в атрибуте - можно нагенерить правильных, а рулить отображением самостоятельно
],
'relative_domain' => false,
'skip_cache' => false,
'flush_cache' => false,
'skip_meta' => false,
'pre' => '', // html перед <img
'after' => '', // подставляем после <img ...>
'imagick_readImageBlob_cache' => false, // кэширование Imagick. Активно при модификации файла Make_Thumb_Creators.php
)
);
*/
/**
* Обёртка над Kama Thumbnails, возвращает атрибуты, в т.ч. метаданные
* ['width' => '', 'height' => '', '2x' => true, 'crop' => true, 'html' => false, 'lazy' => true, 'q' => false, 'full_img_in_src' => true, 'native_srcset' => true, 'sizes' => [], 'rise_small' => true, 'attr' => [], 'relative_domain' => true ]
*
* 'sizes' => [
* [
* 'width' => 0,
* 'height' => 0,
* 'crop' => true
* ],
* ]
*
* Просто получить url оригинала:
* wp_get_attachment_url($id);
* wp_get_attachment_image_srcset( $attachment_id, 'full');
* wp_get_attachment_image_sizes( $attachment_id, 'full');
* get_post_thumbnail_id($post_id); - чтобы получить id прикрепленной
*
* @param integer|string $src
* @param array $args
*/
use \Kama_Thumbnail\Options as Kama_options;
// $GLOBALS['kama_imagickBlobCache_active'] = false; // глобальная опция на отключение кэша readImageBlob к модифицированному Kama Thumbnail
if (!defined('HDDEN_NOCACHE_KAMA')) define('HDDEN_NOCACHE_KAMA', false); // overall cache control
if (!function_exists('get_kama_img')){
function get_kama_img($src, $args = array()){
// проверка на атрибуты
if ( (!isset($args['width']) && !isset($args['height'])) || !$args || !$src ){
return false;
}
// возвращать будем массив
$result = array(
'alt' => '',
'title' => '',
'url' => $src,
'srcset' => $src,
'width' => '', //(isset($args['width']) && $args['width'] ? $args['width'] : ''), нужно проверять живьём из-за обрезки и/или увеличения
'height' => '', //(isset($args['height']) && $args['height'] ? $args['height'] : ''),
'sizes' => '100vw', // умолчание
);
// достанем метаданные
// определить, прилетел id или путь
$src_id = false; // нужен для нахождения мета и нативного srcset
$src_str = '';
if ( (int) $src === 0 ) {
// прилетел путь
$image_src_root_pos = stripos($src, '/wp-content/');
$image_src = get_site_url() . substr($src, $image_src_root_pos);
unset($image_src_root_pos);
// пытаемся в получение id
$src_id = attachment_url_to_postid($image_src);
$image_data = wp_get_attachment_metadata($src_id);
// незачем сначала находить id, а потом из id в каме искать снова путь
$src_str = $image_src;
} else {
// прилетел integer
$src = (int) $src;
$image_data = wp_get_attachment_metadata($src);
if (!$image_data){
return false; // уходим, т.к. не получим даже url
}
$src_id = $src;
$image_src = '/wp-content/uploads/'.$image_data['file'];
// Проверка на корректность вычисленного $image_src
if (!file_exists(ABSPATH.ltrim($image_src, '/'))){
$image_src = parse_url(wp_get_attachment_url($src))['path'];
}
// в каму передаём путь
$src_str = $image_src;
}
// пополним результирующий массив
$result['source'] = $image_src; // оригинал
// смешиваем рабочие атрибуты
$args = array_replace([
'2x' => true,
'crop' => true,
'html' => false,
'lazy' => true,
'full_img_in_src' => true,
'native_srcset' => true,
'rise_small' => true,
'attr' => false,
'relative_domain' => true,
'skip_cache' => false,
'flush_cache' => false,
'sizes' => false,
'skip_meta' => false,
'pre' => '',
'after' => '',
'imagick_readImageBlob_cache' => false,
], $args);
// Можно проверить кэш
$cache_key = '';
if (!$args['skip_cache'] && !(defined('HDDEN_NOCACHE_KAMA') && HDDEN_NOCACHE_KAMA)){
$cache_key = 'hdden_kama_'.$src.'_'.md5(serialize($args));
$cached = get_transient($cache_key);
if ($cached){
// валидация кэша
if (hdden_getkama_validateCache($cached)){
// возврат нельзя, нужно продолжить обработку
// return $cached;
} else {
// удалим из кэша
delete_transient($cache_key);
unset($cached);
}
}
}
// удаление кэша
if ($args['flush_cache']){
if ($args['skip_cache']){
$cache_key = 'hdden_kama_'.$src.'_'.md5(serialize($args));
}
delete_transient($cache_key);
$cached = false;
}
// кэширование
if ($args['2x'] || $args['sizes']){
$args['imagick_readImageBlob_cache'] = true;
}
if (!isset($cached) || !$cached){
// мета
if ($src_id && !$args['skip_meta']){
$image_alt = get_post_meta($src_id, '_wp_attachment_image_alt', TRUE);
$image_title = get_the_title($src_id);
$result['alt'] = $image_alt;
$result['title'] = $image_title;
}
// обрезка
if (function_exists('kama_thumb_src')){
// атрибуты для kama
$kama_attrs = array();
if (isset($args['width'])) $kama_attrs['width'] = $args['width'];
if (isset($args['height'])) $kama_attrs['height'] = $args['height'];
if (isset($args['q']) && ($args['q'] !== false)) $kama_attrs['q'] = $args['q'];
if (isset($args['imagick_readImageBlob_cache']) && ($args['imagick_readImageBlob_cache'] !== false)) $kama_attrs['imagick_readImageBlob_cache'] = $args['imagick_readImageBlob_cache'];
$kama_attrs['crop'] = $args['crop'];
$kama_attrs['rise_small'] = $args['rise_small'];
// Запрос к kama
$processed_imgSrc = kama_thumb_src($kama_attrs, $src_str);
// можем тут же получить недостающую ширину/высоту
$kama_last_instance = kama_thumb();
// если получили инстанс и есть свойства
if ($kama_last_instance){
if (isset($kama_last_instance->width)){
$result['width'] = $kama_last_instance->width;
}
if (isset($kama_last_instance->height)){
$result['height'] = $kama_last_instance->height;
}
}
// если всё-таки не проставлена высота/ширина
if (!$result['width'] || !$result['height']){
// получаем по старинке
$img_abspath = ABSPATH.wp_parse_url($image_src, PHP_URL_PATH);
$img_data = getimagesize($img_abspath);
// фиксируем
$result['width'] = $img_data[0];
$result['height'] = $img_data[1];
}
// загружаем ссыль на картинку
$result['url'] = $processed_imgSrc;
$result['srcset'] = $processed_imgSrc;
if (function_exists('hdden_rellink') && $args['relative_domain']){
$result['url'] = hdden_rellink($result['url']);
$result['srcset'] = hdden_rellink($result['srcset']);
}
// 2x-версия (для ретины)
if ($args['2x']){
// если генерим родными средствами
if (isset($args['native_srcset']) && ($args['native_srcset'] !== false)){
// пока пропускаем
} else {
// модифицируем исходный массив
if (isset($kama_attrs['width']) && $kama_attrs['width']){
$kama_attrs['width'] = intval($kama_attrs['width'])*2;
}
if (isset($kama_attrs['height']) && $kama_attrs['height']){
$kama_attrs['height'] = intval($kama_attrs['height'])*2;
}
// генерируем 2x-копию
$result['2x'] = kama_thumb_src($kama_attrs, $src_str);
if (function_exists('hdden_rellink') && $args['relative_domain']){
$result['url'] = hdden_rellink($result['url']);
$result['srcset'] = hdden_rellink($result['srcset']);
}
}
}
} else {
$result['url'] = $image_src;
$result['srcset'] = $image_src;
if (function_exists('hdden_rellink') && $args['relative_domain']){
$result['url'] = hdden_rellink($result['url']);
$result['srcset'] = hdden_rellink($result['srcset']);
}
// установка ширины/высоты
$img_abspath = ABSPATH.wp_parse_url($image_src, PHP_URL_PATH);
$img_data = getimagesize($img_abspath);
// фиксируем
$result['width'] = $img_data[0];
$result['height'] = $img_data[1];
}
// добавляем srcset: родными способами либо kama
if ($args['2x'] && function_exists('kama_thumb_src')){
// поддержка кастомного sizes здесь
if ($args['sizes']){
// получим srcset и sizes
$temporary_srcset = get_kama_customSrcset($src_str, $args['sizes'], ($args['crop'] ? false : true), $result['width'], $result['height'], $quality = ( (isset($args['q']) && $args['q']) ? $args['q'] : null), ($args['crop'] ?: true));
if (isset($temporary_srcset['srcset']) && $temporary_srcset['srcset']){
$result['srcset'] = $temporary_srcset['srcset'];
$result['sizes'] = $temporary_srcset['sizes'];
}
unset($temporary_srcset);
} else {
// кастомного нет, создаём srcset либо из зарегистрированных, либо просто 2x-копию
if (isset($args['native_srcset']) && ($args['native_srcset'] !== false)){
// нативный способ: получаем srcset и sizes
// есть два варианта: если известны высота/ширина, и если нет.
// в первом мы явно пробрасываем, во втором - получаем от оригинала (как фоллбэк).
// но может стоит работать через каму для второго случая?
// если нам известны ширина и высота
if (
(isset($result['width']) && $result['width'])
&& (isset($result['height']) && $result['height'])
){
// Почти всегда у нас не совпадёт ширина+высота с имеющимися сгенерированными sizes.
// В таком случае нам всегда будет прилетать неоптимальная картинка.
// Решение - кастомный sizes и srcset
// Нам нужно создать размеры картинок, основываясь на имеющихся responsive-версиях для этой картинки.
// Стандартный wp_get_attachment_image_srcset() и wp_get_attachment_image_sizes() здесь не подойдут,
// т.к. первая вернёт картинки другого размера (например, по высоте, т.к. у нас кастомный размер),
// а вторая из-за этого же вернёт sizes с размером 288px для картинки, т.к. не сможет вычислить подходящий максимальный размер.
// Получим все созданные для этой картинки
$actual_sizes = wp_get_attachment_metadata($src_id);
if (isset($actual_sizes['sizes']) && $actual_sizes['sizes']){
// получим srcset и sizes
$temporary_srcset = get_kama_customSrcset($src_str, $actual_sizes['sizes'], true, $result['width'], $result['height'], $quality = ( (isset($args['q']) && $args['q']) ? $args['q'] : false), $args['crop']);
if (isset($temporary_srcset['srcset']) && $temporary_srcset['srcset']){
$result['srcset'] = $temporary_srcset['srcset'];
$result['sizes'] = $temporary_srcset['sizes'];
}
unset($temporary_srcset);
}
} else {
// получим атрибуты от оригинала
$result["srcset"] = hdden_rellink(wp_get_attachment_image_srcset( $src_id, 'full'));
$result["sizes"] = wp_get_attachment_image_sizes( $src_id, 'full');
}
// заполним массив - просто пробросим самую большую, т.к. у нас несколько изображений
$result['2x'] = $result['source'];
} else {
// добавим srcset через kama: изначально нужный размер + 2x-копией
// проверка, реально ли сгенерировалась 2x версия
if ($result['2x'] !== $result['url']){
$result['srcset'] .= ', '.$result['2x'].' 2x';
}
}
}
}
// Сохраним в кэш
if (!$args['skip_cache'] && !$args['flush_cache'] && !(defined('HDDEN_NOCACHE_KAMA') && HDDEN_NOCACHE_KAMA)){
set_transient($cache_key, $result, 60*60*48);
}
} else {
$result = $cached;
}
// если запрошен html, построим его
if ($args['html']){
// проверяем, чтобы в source не лежал id
if ($args['full_img_in_src'] && ( strval((int) $result["source"]) != $result["source"])){
$result['url'] = $result["source"]; // доп операция по ходу
$args['attr']['src'] = $html_img_src = $result["source"];
} else {
$args['attr']['src'] = $html_img_src = $result["url"];
}
$args['attr']['srcset'] = $html_img_srcset = $result["srcset"];
if (!isset($args['attr']['sizes']) || !($args['attr']['sizes'])){
$args['attr']['sizes'] = $result['sizes'];
}
$args['attr']['width'] = $html_img_width = $result["width"];
$args['attr']['height'] = $html_img_height = $result["height"];
if (!isset($args['attr']['alt']) || !$args['attr']['alt']){
$args['attr']['alt'] = $html_img_alt = $result["alt"];
}
if (!isset($args['attr']['title']) || !$args['attr']['title']){
$args['attr']['title'] = $html_img_title = $result["title"];
}
$html_img_lazy = ($args['lazy'] ? 'lazy' : false);
if ($html_img_lazy){
$args['attr']['loading'] = $html_img_lazy;
}
if (!isset($args['attr']['decoding'])){
$args['attr']['decoding'] = 'async';
}
$html_img_attrs = $args['attr'];
$html_img_attrs_string = '';
foreach ($html_img_attrs as $html_attr => $html_attr_val){
if ($html_img_attrs_string){
$html_img_attrs_string .= ' ';
}
$html_img_attrs_string .= $html_attr.'="'.$html_attr_val.'"';
}
$result = '<img '.$html_img_attrs_string.' >';
// pre- and -after
if (@$args['pre']){
$result = $args['pre'] . $result;
}
if (@$args['after']){
$result = $result . $args['after'];
}
//$result = '<img src="'.$html_img_src.'" srcset="'.$html_img_srcset.'" width="'.$html_img_width.'" height="'.$html_img_height.'" alt="'.$html_img_alt.'" title="'.$html_img_title.'" '.( $args['lazy'] ? 'loading="lazy" ' : '').'decoding="async" />';
}
return $result;
}
}
/**
* Массовый процессинг
*
* [
* '--fbg-500' => ['src' => $bg, 'args' => ['width' => 500, 'height' => 536, 'crop' => true, '2x' => false]],
* '--fbg-500-2x' => ['src' => $bg, 'args' => ['width' => (500 * 2), 'height' => (536 * 2), 'crop' => true, '2x' => false]],
* '--fbg-800' => ['src' => $bg, 'args' => ['width' => 800, 'height' => 857, 'crop' => true, '2x' => false]],
* ]
*
* @param array $images
* @return void
*/
if (!function_exists('get_kama_multiple')){
function get_kama_multiple($images = array()){
if ($images){
$result = [];
foreach ($images as $key => $data) {
if (isset($data['src']) && isset($data['args'])){
if (!isset($data['args']['imagick_readImageBlob_cache'])){
$data['args']['imagick_readImageBlob_cache'] = true;
}
$result[$key] = get_kama_img($data['src'], $data['args']);
}
}
return $result;
} else {
return false;
}
}
}
/**
* Создаёт srcset и соответствующий sizes для переданной картинки и размеров
*/
if (!function_exists('get_kama_customSrcset')){
function get_kama_customSrcset($src, $sizes, $save_proportions = true, $target_width, $target_height, $quality = false, $crop = true){
$result = array(
'sizes' => '100vw',
'srcset' => '',
);
if ($sizes){
$temporary_srcset = '';
$temporary_sizes = '';
$temporary_sizes_arr = array();
// проверим, ассоциативный или нет
if (isset($sizes[0])){
$temporary_sizes_arr = &$sizes;
} else {
// ассоциативный, создаём адаптированный
foreach ($sizes as $key => $value) {
$tmp_params_arr = [];
if (!empty($value['width'])){
$tmp_params_arr['width'] = $value['width'];
}
if (!empty($value['height'])){
$tmp_params_arr['height'] = $value['height'];
}
if (!empty($value['crop'])){
$tmp_params_arr['crop'] = $value['crop'];
}
if ($tmp_params_arr){
$temporary_sizes_arr[] = $tmp_params_arr;
}
unset($tmp_params_arr);
}
}
// работаем
foreach ($temporary_sizes_arr as $size_data) {
// получаем ширину, вычисляем разницу, скейлим высоту
if (empty($size_data['width']) && empty($size_data['height'])){
continue;
}
// узнаём модификатор для пропроций
if (isset($size_data['width']) && $size_data['width']){
$temporary_modifier = $size_data['width'] / $target_width;
} elseif (isset($size_data['height']) && $size_data['height']){
$temporary_modifier = $size_data['height'] / $target_height;
} else {
continue;
}
// если работаем с сохранением пропорций, а не обрезкой
if ($save_proportions && (empty($size_data['crop']) || $size_data['crop'] === false)){
$temporary_width = (int) ($target_width * $temporary_modifier);
$temporary_height = (int) ($target_height * $temporary_modifier);
$temporary_width__w = $temporary_width;
// коррекция: проверяем лимиты
if (!empty($size_data['height']) && $temporary_height > $size_data['height']){
$temporary_width__w = $temporary_width;
$temporary_width = round($size_data['height'] / $temporary_height * $temporary_width);
$temporary_height = $size_data['height'];
}
// if ($temporary_width > $size_data['width']){
// $temporary_width = $size_data['width'];
// }
} else {
$temporary_width = (isset($size_data['width']) && $size_data['width']) ? $size_data['width'] : ( (int) ($target_width * $temporary_modifier) );
$temporary_height = (isset($size_data['height']) && $size_data['height']) ? $size_data['height'] : ( (int) ($target_height * $temporary_modifier) );
$temporary_width__w = $temporary_width;
}
// создаём обрезанную миниатюру
// $temporary_arr = get_kama_img($src, ['width' => $temporary_width, 'height' => $temporary_height, '2x' => false, 'native_srcset' => false, 'crop' => true, 'q' => $quality, 'rise_small' => false, 'skip_meta' => true,]);
// $temporary_url = $temporary_arr['url'];
// вычислим crop
// обратимся напрямую
$temporary_args = [
'width' => $temporary_width,
'height' => $temporary_height,
// 'crop' => ($size_data['crop'] ?: true),
'crop' => ($crop ?: true),
'rise_small' => false,
'skip_meta' => true,
'imagick_readImageBlob_cache' => true,
];
if ($quality !== false){
$temporary_args['q'] = $quality;
}
$temporary_url = kama_thumb_src($temporary_args, $src);
unset($temporary_args);
if ($temporary_url){
// версии изображения
// если указана конкретная ширина - ее и передадим, иначе (если передана только высота) реальную
$temporary_srcset_2 = $temporary_url . ' ' . (!empty($size_data['width']) ? $temporary_width__w : $target_width) .'w'; // $temporary_width instead of target
// проверка на дубль
if (mb_strpos($temporary_srcset, $temporary_srcset_2) === false){
if ($temporary_srcset){
$temporary_srcset .= ', ';
}
$temporary_srcset .= $temporary_srcset_2;
}
}
}
// если нагенерировали копий
if ($temporary_srcset){
// на разрешениях до своего размера она занимает 100%, а на разрешениях выше - свой размер
$temporary_sizes = '(max-width: '.$target_width.'px) 100vw, '.$target_width.'px';
}
// проверяем, что можем заменить
if ($temporary_srcset && $temporary_sizes){
$result['srcset'] = $temporary_srcset;
$result['sizes'] = $temporary_sizes;
// добавляем картинку своего размера
// снова проверка на дубль
//$temporary_srcset_2_arr = get_kama_img($src, ['width' => $target_width, 'height' => $target_height, '2x' => false, 'native_srcset' => false, 'crop' => true, 'q' => false, 'rise_small' => false, 'skip_meta' => true,]);
//$temporary_srcset_2 = $temporary_srcset_2_arr['url'] . ' ' . $target_width . 'w';
// обратимся напрямую
$temporary_args = [
'width' => $target_width,
'height' => $target_height,
'crop' => $crop,
'rise_small' => false,
'skip_meta' => true,
'imagick_readImageBlob_cache' => true,
];
if ($quality !== false){
$temporary_args['q'] = $quality;
}
$temporary_url = kama_thumb_src($temporary_args, $src);
unset($temporary_args);
if (mb_strpos($result['srcset'], $temporary_url) === false){
if ($result['srcset']){
$result['srcset'] .= ', ';
}
$result['srcset'] .= $temporary_url . ' ' . $target_width . 'w';
}
}
}
return $result;
}
}
/**
* Убирает домен из строки, делает url относительными
*/
if (!function_exists('hdden_rellink')){
function hdden_rellink($url){
$result = $url;
$parsed = parse_url($url);
$to_remove = '';
if (isset($parsed['scheme'])) $to_remove .= $parsed['scheme'] . '://';
if (isset($parsed['host'])) $to_remove .= $parsed['host'];
if (isset($parsed['port'])) $to_remove .= ':' . $parsed['host'];
$result = str_replace($to_remove, '', $result);
$result = '/' . ltrim($result, '/');
return $result;
}
}
if (!function_exists('hdden_getkama_validateCache')){
function hdden_getkama_validateCache($arr){
$result = true;
if (!is_array($arr)){
$result = false;
return $result;
}
if (!isset($arr['url']) || !$arr['url']){
$result = false;
return $result;
}
$cache_url = '';
// нужно получить актуальный url к кэшу
if (class_exists('\Kama_Thumbnail\Options')){
$options = new Kama_options();
$opts = $options->get_options_raw();
if (isset($opts['cache_dir_url']) && $opts['cache_dir_url']){
$cache_url = parse_url($opts['cache_dir_url'], PHP_URL_PATH);
}
}
if (!$cache_url){
$cache_url = '/wp-content/cache/thumb/';
}
foreach ($arr as $key => $param){
// ищем упоминание файла
if (mb_strpos($param, $cache_url) !== false){
// разделим на файлы
$files_arr = explode(' ', $param);
foreach ($files_arr as $maybefile){
if (strpos($maybefile, $cache_url) !== false){
$file_abspath = rtrim(ABSPATH, '/\\') . hdden_rellink($maybefile);
if (!file_exists($file_abspath)){
$result = false;
return $result;
}
}
}
}
}
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment