Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 426 · После первых изображений Gmagick, объектов, массивов и циклов

Gmagick: последовательности, ракурсы и навигация

Собрать ракурсы, выбрать обложку, пройти список в обе стороны и удалить ненужное изображение.

Собрать три ракурса и вставить недостающий средний

Продолжаем первые изображения и чтение файлов, байтов и потоков. Это расширение Gmagick для GraphicsMagick, не Imagick. Проверенная среда: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Для понимания переменных, объектов и циклов доступны переменные, объекты и циклы.

Карточке товара нужны несколько ракурсов в одном файле. Начнём с трёх собственных рисунков: коробка находится слева, посередине и справа. Отдельные PNG позволяют спокойно рассмотреть каждый кадр, а GIF показывает переходы. Изображение в переменной и список изображений — разные уровни работы.

Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Вставленный средний ракурс
Вставленный средний ракурс — 320 × 200 px.
Последний ракурс: коробка справа
Последний ракурс: коробка справа — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(1);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__ . '/middle.png', false);

new создаёт объект; конструктор читает готовый собственный views.gif, включённый в ZIP. Стрелка -> вызывает метод объекта. Индекс1 — второй кадр, потому что отсчёт начинается с0. false во втором аргументе writeImage сохраняет только активный кадр. Полная программа ниже строит новый GIF: сначала читает два PPM одним потоком, затем addImage вставляет средний ракурс после индекса0, у которого уже есть следующий элемент. True в writeImage записывает весь список.

Собрать три ракурса и вставить недостающий среднийСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/view0.ppm').file_get_contents(__DIR__.'/view2.ppm'));
$sequence->setImageIndex(0);
$same=$sequence->addImage(new Gmagick(__DIR__.'/view1.ppm'))===$sequence;
$count=$sequence->getNumberImages();
foreach([0,1,2] as $i){
    $sequence->setImageIndex($i);$sequence->setImageDelay(30);
    $sequence->setImageIterations(0);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);
    $sequence->setImageDepth(8);$sequence->setImageFormat('PNG');
    $sequence->writeImage(__DIR__.'/gm100-view'.$i.'.png',false);
}
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-views.gif',true);

gmReport('gm100-seq-assemble',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.addimage", "gmagick.getnumberimages", "gmagick.setimagedelay", "gmagick.setimageiterations", "gmagick.setimagedispose", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.nextimage"],["gm100-view0.png", "gm100-view1.png", "gm100-view2.png"],
    ['three_frames'=>$count===3,'chain_return'=>$same,'size_gm100_view0'=>gmInspect('gm100-view0.png')['width']===320&&gmInspect('gm100-view0.png')['height']===200,'size_gm100_view1'=>gmInspect('gm100-view1.png')['width']===320&&gmInspect('gm100-view1.png')['height']===200,'size_gm100_view2'=>gmInspect('gm100-view2.png')['width']===320&&gmInspect('gm100-view2.png')['height']===200],
    $errors ?? [], ['count'=>$count,'insertion'=>'after index0 with an existing successor']);
echo "gm100-view0.png 320x200 gm100-view1.png 320x200 gm100-view2.png 320x200\n";
Ожидаемый результат
gm100-view0.png 320x200 gm100-view1.png 320x200 gm100-view2.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-view0.png 320x200 gm100-view1.png 320x200 gm100-view2.png 320x200

GIF — контейнер с последовательностью, а расширение .gif само по себе не заставляет записать все кадры. Если после преобразования получилась одна неподвижная картинка, сначала проверьте наличие нескольких кадров в объекте и all_frames=true в записи. Ошибку сборки списка нельзя исправить одним переименованием PNG в GIF.

Исходники собственные: шесть PPM320×200 содержат три положения коробки на ровном и градиентном фоне. views.ppm и ramp-views.ppm — три PPM подряд в одном многокадровом потоке. Готовый views.gif получен этой же библиотекой из этих кадров и включён для коротких фрагментов. gmRestore восстанавливает точные байты из .b64; никаких сетевых загрузок нет. file_get_contents читает байты файла, точка соединяет строки, readImageBlob декодирует один получившийся поток. Сами изучаемые операции не спрятаны в помощнике.

Граница этой сборки: addImage на пустом назначении и на обычной последней позиции возвращал self, но не добавлял кадр. Последовательные readImage увеличивали счётчик, но затем запись списка теряла его начало. Поэтому основной вход читается одним потоком. Для addImage показана настоящая вставка после элемента, у которого есть следующий. Дополнительно исследованный переход nextImage за конец позволяет дописывать к списку с несколькими кадрами, но не нужен начинающему маршруту. После вставки указатель здесь оказался на индексе2, поэтому дальнейшая позиция всегда выбирается явно. Источник addImage в этой библиотеке клонируется целым списком, даже если его указатель стоит посередине; в примерах источник содержит ровно одно изображение.

Перед writeImage(path,true) полная программа назначает GIF каждому кадру. Установка формата только текущему изображению не подготовила весь смешанный список в этой сборке. Малые фрагменты с готовым views.gif уже читают GIF-кадры; фрагменты с PPM явно выполняют цикл форматов. Не делайте вывод об успехе по одному возвращённому объекту — проверяйте сохранённый результат.

Выбрать обложку и понять ссылку current

Для каталога нужна отдельная обложка из второго ракурса. Выберем индекс1 и сохраним только его. Затем увидим важную особенность current: новая переменная указывает на тот же объект, поэтому изменение через неё действует на текущий кадр общего списка.

Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Выбрать обложку и понять ссылку current — анимированный GIF: 300ms, 450ms, 300ms; dispose 1,1,1; поле loop=0
Выбрать обложку и понять ссылку current — анимированный GIF: 300ms, 450ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Статическая обложка из кадра с индексом1
Статическая обложка из кадра с индексом1 — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(1);
$active = $sequence->current();
$index = $active->getImageIndex();
$active->setImageFormat('PNG');
$active->writeImage(__DIR__ . '/cover.png', false);

Готовый views.gif входит в каждый ZIP; короткий фрагмент не требует запуска другого примера. Полная программа дополнительно строит собственный список из PPM. $active — имя той же ссылки на объект; current() не извлекает отдельный файл. getImageIndex возвращает число, а запись false оставляет в PNG выбранное изображение.

Выбрать обложку и понять ссылку currentСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->setImageIndex(1);$active=$sequence->current();
$same=$active===$sequence;$index=$active->getImageIndex();
$active->setImageDelay(45);$delay=$sequence->getImageDelay();
$active->setImageDepth(8);
$active->setImageFormat('PNG');
$active->writeImage(__DIR__.'/gm100-cover.png',false);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-cover-selection.gif',true);

gmReport('gm100-seq-cover',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.current", "gmagick.getimageindex", "gmagick.getimagedelay", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-cover.png"],
    ['same_reference'=>$same,'selected_middle'=>$index===1,'alias_changes_current'=>$delay===45,'size_gm100_cover'=>gmInspect('gm100-cover.png')['width']===320&&gmInspect('gm100-cover.png')['height']===200],
    $errors ?? [], ['index'=>$index,'current_is_self'=>$same,'delay_via_alias'=>$delay]);
echo "gm100-cover.png 320x200\n";
Ожидаемый результат
gm100-cover.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-cover.png 320x200

Если нужна независимая обработка, продумайте отдельную копию и её указатель. Не храните результат current() в массиве в ожидании набора независимых кадров: ссылки будут относиться к одному перемещаемому объекту. Для фиксированных результатов здесь используются записанные PNG.

Экспортировать галерею последовательным обходом

Из трёх ракурсов подготовим три самостоятельные картинки для галереи. Указатель работает как закладка в списке: сначала обрабатываем текущую страницу, затем спрашиваем, существует ли следующая, и только после этого переходим.

Прямой обход: индекс0
Прямой обход: индекс0 — 320 × 200 px.
Прямой обход: индекс1
Прямой обход: индекс1 — 320 × 200 px.
Прямой обход: индекс2
Прямой обход: индекс2 — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(0);
do {
    echo $sequence->getImageIndex(), "\n";
    if (!$sequence->hasNextImage()) break;
} while ($sequence->nextImage());

do выполняет тело хотя бы один раз, поэтому первый кадр не пропускается. hasNextImage только проверяет соседний элемент. ! означает «не», break заканчивает цикл, nextImage действительно двигает указатель и сообщает успех. Полная программа вместо одного echo пишет каждый активный ракурс в отдельный PNG.

Экспортировать галерею последовательным обходомСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$indices=[];
do{
    $i=$sequence->getImageIndex();$indices[]=$i;
    $sequence->setImageDepth(8);$sequence->setImageFormat('PNG');
    $sequence->writeImage(__DIR__.'/gm100-forward'.$i.'.png',false);
    if(!$sequence->hasNextImage())break;
}while($sequence->nextImage());
$atEnd=!$sequence->hasNextImage();

gmReport('gm100-seq-forward',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getimageindex", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.hasnextimage"],["gm100-forward0.png", "gm100-forward1.png", "gm100-forward2.png"],
    ['all_once'=>$indices===[0,1,2],'end_reached'=>$atEnd,'size_gm100_forward0'=>gmInspect('gm100-forward0.png')['width']===320&&gmInspect('gm100-forward0.png')['height']===200,'size_gm100_forward1'=>gmInspect('gm100-forward1.png')['width']===320&&gmInspect('gm100-forward1.png')['height']===200,'size_gm100_forward2'=>gmInspect('gm100-forward2.png')['width']===320&&gmInspect('gm100-forward2.png')['height']===200],
    $errors ?? [], ['visited'=>$indices]);
echo "gm100-forward0.png 320x200 gm100-forward1.png 320x200 gm100-forward2.png 320x200\n";
Ожидаемый результат
gm100-forward0.png 320x200 gm100-forward1.png 320x200 gm100-forward2.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-forward0.png 320x200 gm100-forward1.png 320x200 gm100-forward2.png 320x200

Нельзя писать while(nextImage()) после выбора индекса0 и ожидать обработку первого кадра внутри цикла: переход произойдёт раньше тела. Для пустого списка требуется отдельная проверка; здесь список сначала создаётся из известных файлов.

Просмотреть список назад и собрать обратную анимацию

Посетитель листает ракурсы назад. Сохраним обратный порядок как отдельную анимацию: справа → центр → слева. Исходный список остаётся удобным источником; новый список содержит кадры в порядке добавления.

Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Просмотреть список назад и собрать обратную анимацию — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Просмотреть список назад и собрать обратную анимацию — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Первым при обратном обходе сохранён индекс2
Первым при обратном обходе сохранён индекс2 — 320 × 200 px.
Следующим сохранён индекс1
Следующим сохранён индекс1 — 320 × 200 px.
Последним сохранён индекс0
Последним сохранён индекс0 — 320 × 200 px.
Обратная анимация начинается с правого положения
Обратная анимация начинается с правого положения — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(2);
do {
    echo $sequence->getImageIndex(), "\n";
    if (!$sequence->hasPreviousImage()) break;
} while ($sequence->previousImage());

Индекс2 — третий элемент списка из трёх. hasPreviousImage не двигает указатель; previousImage переводит его назад. В полном примере каждый посещённый ракурс сохраняется в PNG. Затем отдельный обратный список строится из крайних кадров с вставкой среднего через addImage. Обратный просмотр сам по себе не меняет порядок элементов исходного списка.

Просмотреть список назад и собрать обратную анимациюСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->setImageIndex(2);$indices=[];
do{
    $i=$sequence->getImageIndex();$indices[]=$i;
    $sequence->setImageDepth(8);$sequence->setImageFormat('PNG');
    $sequence->writeImage(__DIR__.'/gm100-backward'.$i.'.png',false);
    if(!$sequence->hasPreviousImage())break;
}while($sequence->previousImage());
$reverse=new Gmagick();$reverse->readImageBlob(file_get_contents(__DIR__.'/view2.ppm').file_get_contents(__DIR__.'/view0.ppm'));
$reverse->setImageIndex(0);$reverse->addImage(new Gmagick(__DIR__.'/view1.ppm'));
foreach([0,1,2] as $i){$reverse->setImageIndex($i);$reverse->setImageDelay(30);$reverse->setImageDispose(Gmagick::DISPOSE_NONE);$reverse->setImageIterations(0);}
$reverse->setImageIndex(0);
$reverse->setImageDepth(8);
$reverse->setImageFormat('PNG');
$reverse->writeImage(__DIR__.'/gm100-reverse-first.png',false);
$reverse->setImageIndex(0);
do{$reverse->setImageFormat('GIF');}while($reverse->nextImage());
$reverse->setImageIndex(0);$reverse->writeImage(__DIR__.'/gm100-reverse.gif',true);

gmReport('gm100-seq-backward',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getimageindex", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.haspreviousimage", "gmagick.previousimage", "gmagick.addimage"],["gm100-backward2.png", "gm100-backward1.png", "gm100-backward0.png", "gm100-reverse-first.png"],
    ['reverse_order'=>$indices===[2,1,0],'three_reverse_frames'=>$reverse->getNumberImages()===3,'at_start'=>!$sequence->hasPreviousImage(),'size_gm100_backward2'=>gmInspect('gm100-backward2.png')['width']===320&&gmInspect('gm100-backward2.png')['height']===200,'size_gm100_backward1'=>gmInspect('gm100-backward1.png')['width']===320&&gmInspect('gm100-backward1.png')['height']===200,'size_gm100_backward0'=>gmInspect('gm100-backward0.png')['width']===320&&gmInspect('gm100-backward0.png')['height']===200,'size_gm100_reverse_first'=>gmInspect('gm100-reverse-first.png')['width']===320&&gmInspect('gm100-reverse-first.png')['height']===200],
    $errors ?? [], ['visited_backwards'=>$indices]);
echo "gm100-backward2.png 320x200 gm100-backward1.png 320x200 gm100-backward0.png 320x200 gm100-reverse-first.png 320x200\n";
Ожидаемый результат
gm100-backward2.png 320x200 gm100-backward1.png 320x200 gm100-backward0.png 320x200 gm100-reverse-first.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-backward2.png 320x200 gm100-backward1.png 320x200 gm100-backward0.png 320x200 gm100-reverse-first.png 320x200

Начальный индекс должен соответствовать размеру списка: число2 здесь относится именно к трём нашим кадрам. В прикладной функции размер узнают заранее или обходят список до конца. Не используйте номер scene в качестве индекса последнего элемента.

Удалить лишний средний ракурс

Средний ракурс оказался лишним. Удалим активный элемент, получим переход сразу от левого к правому и сравним анимации до и после. Это удаление кадра из памяти; три исходных PPM на диске сохраняются.

Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Удалить лишний средний ракурс — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Удалить лишний средний ракурс — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Удалить лишний средний ракурс — анимированный GIF: 300ms, 300ms; dispose 1,1; поле loop=0
Удалить лишний средний ракурс — анимированный GIF: 300ms, 300ms; dispose 1,1; поле loop=0 — 320 × 200 px.
Исключённый средний кадр сохранён отдельно
Исключённый средний кадр сохранён отдельно — 320 × 200 px.
Начальный кадр списка после удаления
Начальный кадр списка после удаления — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(1);
$sequence->removeImage();
$sequence->setImageIndex(0);
$sequence->setImageFormat('GIF');
$sequence->writeImage(__DIR__ . '/two-views.gif', true);

Сначала setImageIndex выбирает удаляемый элемент. removeImage не принимает номер: он работает с активным изображением. После удаления явно возвращаемся к индексу0. У нового списка уже два элемента, поэтому старый индекс2 больше не обозначает третий ракурс.

Удалить лишний средний ракурсСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-before-removal.gif',true);
$sequence->setImageIndex(1);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-removed-frame.png',false);
$same=$sequence->removeImage()===$sequence;$count=$sequence->getNumberImages();
$sequence->setImageIndex(0);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-remove-first.png',false);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-two-views.gif',true);

gmReport('gm100-seq-remove',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.setimagedepth", "gmagick.removeimage", "gmagick.getnumberimages"],["gm100-removed-frame.png", "gm100-remove-first.png"],
    ['two_remaining'=>$count===2,'same_object'=>$same,'size_gm100_removed_frame'=>gmInspect('gm100-removed-frame.png')['width']===320&&gmInspect('gm100-removed-frame.png')['height']===200,'size_gm100_remove_first'=>gmInspect('gm100-remove-first.png')['width']===320&&gmInspect('gm100-remove-first.png')['height']===200],
    $errors ?? [], ['remaining'=>$count]);
echo "gm100-removed-frame.png 320x200 gm100-remove-first.png 320x200\n";
Ожидаемый результат
gm100-removed-frame.png 320x200 gm100-remove-first.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-removed-frame.png 320x200 gm100-remove-first.png 320x200

Перед удалением удобно сохранить самостоятельный PNG исключаемого кадра для проверки выбора. Не удаляйте элементы прямым циклом по возрастающим старым индексам без учёта сдвига: после каждой операции следующий элемент может получить уже обработанный номер.

Подготовить ракурсы без начальной заставки

Сервису нужна функция подготовки двух оставшихся ракурсов после удаления начальной заставки. Функция проверяет вход, удаляет выбранный элемент и экспортирует результат. Видимые PNG оставшихся кадров помогают обнаружить неверный индекс лучше одного сообщения «готово».

Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Собрать три ракурса и вставить недостающий средний — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Подготовить ракурсы без начальной заставки — анимированный GIF: 300ms, 300ms; dispose 1,1; поле loop=0
Подготовить ракурсы без начальной заставки — анимированный GIF: 300ms, 300ms; dispose 1,1; поле loop=0 — 320 × 200 px.
Первым стал прежний средний кадр
Первым стал прежний средний кадр — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(0);
$sequence->removeImage();
$sequence->setImageIndex(1);
$hasPrevious = $sequence->hasPreviousImage();
$sequence->previousImage();
$index = $sequence->current()->getImageIndex();

После удаления первого кадра остаются индексы0 и1. Чтение предыдущего элемента возвращает указатель на0. В полной функции отдельная проверка допустимого индекса даёт понятную InvalidArgumentException, а сама библиотека сообщает GmagickException при попытке выбрать несуществующий кадр.

Подготовить ракурсы без начальной заставкиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
function dropFrame(Gmagick $sequence,int $index):Gmagick{
    if($index<0||$index>=$sequence->getNumberImages())throw new InvalidArgumentException('Frame index outside list');
    $sequence->setImageIndex($index);$sequence->removeImage();
    $sequence->setImageIndex(0);return $sequence;
}
$sequence=dropFrame($sequence,0);$first=$sequence->current();$same=$first===$sequence;
$canNext=$sequence->hasNextImage();$sequence->nextImage();$canPrevious=$sequence->hasPreviousImage();$sequence->previousImage();$index=$sequence->getImageIndex();
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-after-intro.png',false);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-without-intro.gif',true);
$errors=['range'=>gmTry(fn()=>dropFrame($sequence,9)),'native_index'=>gmTry(fn()=>$sequence->setImageIndex(9))];

gmReport('gm100-seq-function',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getnumberimages", "gmagick.removeimage", "gmagick.current", "gmagick.hasnextimage", "gmagick.haspreviousimage", "gmagick.previousimage", "gmagick.getimageindex", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-after-intro.png"],
    ['two_remaining'=>$sequence->getNumberImages()===2,'navigation'=>$canNext&&$canPrevious&&$index===0,'current_identity'=>$same,'range_rejected'=>$errors['range']['class']==='InvalidArgumentException','native_rejected'=>$errors['native_index']['class']==='GmagickException','size_gm100_after_intro'=>gmInspect('gm100-after-intro.png')['width']===320&&gmInspect('gm100-after-intro.png')['height']===200],
    $errors ?? [], ['remaining'=>2,'returned_index'=>$index]);
echo "gm100-after-intro.png 320x200\n";
Ожидаемый результат
gm100-after-intro.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-after-intro.png 320x200

Приложение должно различать правило своего интерфейса и отказ библиотеки. Текст исключения может меняться между версиями; проверяем класс и наличие сообщения, не выдаём конкретную английскую фразу за постоянный API.

Упражнение: у трёх кадров какие индексы? Ответ:0,1,2. Создаёт ли current независимый кадр? Ответ:нет, он возвращает self. Что произойдёт с прежним третьим кадром после удаления среднего? Ответ:он станет вторым, с индексом1. Дальше настроим время и повторы. Для проверки ошибок полезны исключения и функции.

Практический вопрос и наш ответ

Imagick making animated gifs not animated?: После обработки загруженного GIF автор получает одну неподвижную картинку и не понимает, потерялась ли анимация при resize или при записи. Наш самостоятельный маршрут различает список кадров, текущую позицию и all_frames. Для галереи экспортируются отдельные PNG, для движения явно записывается весь GIF. Это оригинальное объяснение на собственных кадрах Gmagick; код ответа Imagick не переносится как контракт другой библиотеки.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

Gmagick::addimageДобавляет изображения источника к списку назначения

PECL gmagick >= Unknown · gmagick

Gmagick::addimage(Gmagick $source): Gmagick

Добавляет изображения источника к списку назначения. Официальное описание говорит о текущей позиции источника и перемещении итератора в конец. Возвращает объект назначения. Отличайте сам список от выбранного в нём кадра; после операции проверяйте требуемую позицию явно.

Gmagick::addimage(Gmagick $source): Gmagick
АргументТипСмысл
sourceGmagick — обязательныйОбъект Gmagick, из которого добавляются изображения относительно его текущей позиции. Проверяйте фактический состав списка: реализация установленной версии может расходиться с описанием.

Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Собрать три ракурса и вставить недостающий средний; Просмотреть список назад и собрать обратную анимацию.

Gmagick::currentВозвращает self — тот же Gmagick с текущим указателем изображения

PECL gmagick >= Unknown · gmagick

Gmagick::current(): Gmagick

Возвращает self — тот же Gmagick с текущим указателем изображения. Это не выделение одного кадра и не независимая копия. Вызов метода через полученную переменную меняет тот же объект; одиночный PNG записывается writeImage(path,false).

Gmagick::current(): Gmagick

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: self: ссылка на тот же Gmagick, не самостоятельный кадр.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Выбрать обложку и понять ссылку current; Подготовить ракурсы без начальной заставки.

Gmagick::getimageindexВозвращает текущую позицию активного изображения внутри списка

PECL gmagick >= Unknown · gmagick

Gmagick::getimageindex(): int

Возвращает текущую позицию активного изображения внутри списка. У трёх кадров позиции0,1,2. Это положение указателя, а не общее количество изображений и не метка scene.

Gmagick::getimageindex(): int

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Целое число с указанным выше смыслом.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Выбрать обложку и понять ссылку current; Экспортировать галерею последовательным обходом.

Gmagick::hasnextimageПроверяет наличие следующего изображения при движении вперёд, не перемещая указатель

PECL gmagick >= Unknown · gmagick

Gmagick::hasnextimage(): mixed

Проверяет наличие следующего изображения при движении вперёд, не перемещая указатель. Сигнатура PHP.net содержит mixed, но описание обещает true при наличии и false при отсутствии следующего кадра.

Gmagick::hasnextimage(): mixed

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Сигнатура mixed, описание PHP.net обещает true/false по наличию соседнего изображения.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Экспортировать галерею последовательным обходом; Подготовить ракурсы без начальной заставки.

Gmagick::haspreviousimageПроверяет наличие предыдущего изображения при движении назад, не перемещая указатель

PECL gmagick >= Unknown · gmagick

Gmagick::haspreviousimage(): mixed

Проверяет наличие предыдущего изображения при движении назад, не перемещая указатель. Сигнатура PHP.net — mixed; фактический смысл возврата по описанию — булево условие наличия предыдущего кадра.

Gmagick::haspreviousimage(): mixed

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Сигнатура mixed, описание PHP.net обещает true/false по наличию соседнего изображения.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Просмотреть список назад и собрать обратную анимацию; Подготовить ракурсы без начальной заставки.

Gmagick::nextimageСвязывает объект со следующим изображением списка и возвращает true при успешном переходе либо false при неудаче

PECL gmagick >= Unknown · gmagick

Gmagick::nextimage(): bool

Связывает объект со следующим изображением списка и возвращает true при успешном переходе либо false при неудаче. Обычная граница списка — повод закончить обход. В разделе ошибок PHP.net отдельно допускается GmagickException.

Gmagick::nextimage(): bool

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: true при успешном переходе, false при неудаче.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Экспортировать галерею последовательным обходом; Подготовить ракурсы без начальной заставки.

Gmagick::previousimageПереходит к предыдущему изображению

PECL gmagick >= Unknown · gmagick

Gmagick::previousimage(): bool

Переходит к предыдущему изображению. Возвращает true при успехе и false при неудаче; официальный раздел ошибок также допускает GmagickException. Полезен для просмотра ракурсов в обратном порядке без переименования исходных файлов.

Gmagick::previousimage(): bool

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: true при успешном переходе, false при неудаче.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Просмотреть список назад и собрать обратную анимацию; Подготовить ракурсы без начальной заставки.

Gmagick::removeimageУдаляет активное изображение из списка и возвращает изменённый Gmagick

PECL gmagick >= Unknown · gmagick

Gmagick::removeimage(): Gmagick

Удаляет активное изображение из списка и возвращает изменённый Gmagick. После удаления индексы оставшихся кадров могут сдвинуться; перед следующей выборкой назначьте позицию явно. Исходные PPM-файлы эта операция не удаляет.

Gmagick::removeimage(): Gmagick

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Удалить лишний средний ракурс; Подготовить ракурсы без начальной заставки.

Gmagick::setimageindexУстанавливает итератор на указанную позицию списка

PECL gmagick >= Unknown · gmagick

Gmagick::setimageindex(int $index): Gmagick

Устанавливает итератор на указанную позицию списка. Нужен для выбора ракурса, правки задержки отдельного кадра и явного возврата в начало перед экспортом. Несуществующая позиция должна обрабатываться как ошибка.

Gmagick::setimageindex(int $index): Gmagick
АргументТипСмысл
indexint — обязательныйПозиция в списке, начиная с нуля. Это индекс итератора, а не метка scene; в кратком описании параметра PHP.net эти понятия неудачно смешаны.

Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Выбрать обложку и понять ссылку current; Удалить лишний средний ракурс.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.