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

Дерево руководства
Глава 385 · После объектов, файлов и глубины изображения
Imagick: encipherImage, decipherImage и восстановление пикселей после PNG
Разобрать существующую обфускацию: настоящий lossless roundtrip, неправильная фраза, открытые метаданные и потеря глубины без ложных обещаний безопасности.
Увидеть неузнаваемый растр и восстановить его после настоящего чтения файла
В старом ImageMagick-процессе найден PNG, который открывается как изображение, но выглядит шумом. Нужно воспроизвести этот способ преобразования, сохранить результат и понять, почему восстановление иногда перестаёт работать. Начнём с собственного рисунка: сначала исходник, затем реальные cipher-пиксели, после них — восстановление после настоящей записи файла и повторного чтения. Это урок об обслуживании такой обфускации, а не рецепт защищённого хранения.

У результата восстановились все 384 000 компонентов: 400 × 320 пикселей по три RGB-канала. Это точное совпадение измеренных значений, а не только похожий вид картинки. Исходные белая основа и непрозрачный RGB8 подготовлены явно. Заголовок файла и метаданные не скрыты: даже у шумного cipher-файла остаются публичные comment=PUBLIC DEMO ORDER 42 и demo:ticket=PUBLIC-42.
$cipher = clone $source;
$ok = $cipher->encipherImage('public-demo-passphrase-2026-only');
$restored = clone $cipher;
$done = $restored->decipherImage('public-demo-passphrase-2026-only');$source — объект с изображением. clone создаёт отдельную рабочую копию; -> вызывает её метод. В круглых скобках передаётся один аргумент: строка в кавычках. Фраза здесь публичная и в обоих вызовах одинаковая. $ok и $done получают bool true, не новое изображение; пиксели меняются внутри соответствующего объекта. Точка с запятой завершает команду. Переменные, объекты и clone разобраны отдельно. Следующая самостоятельная программа добавляет отсутствующий в этом коротком фрагменте важный шаг: запись и повторное открытие PNG.
Сохранить неузнаваемый растр и восстановить его после настоящего чтения файла
Видны исходник, действительно преобразованные пиксели и точное восстановление после записи cipher-data.png и повторного чтения. Все 384 000 компонентов RGB совпали; открытые comment и demo:ticket в cipher-файле читаются.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=clone $source;
$enc=$cipher->encipherImage($phrase);
icSavePNG($cipher,'cipher-data.png',8);
$loaded=new Imagick(__DIR__.'/cipher-data.png');
$restored=clone $loaded;
$dec=$restored->decipherImage($phrase);
icFirstProof($source,$cipher,$loaded,$restored,$enc,$dec,$before);
echo "image-cipher-first.png\nsource-preview8.png\ncipher-view8.png\nrestored-view8.png\ncipher-data.png\nmetrics.json\nencipher=true decipher=true lossless-save=1 restored-exact=1 public-metadata=1\n";
Видны исходник, действительно преобразованные пиксели и точное восстановление после записи cipher-data.png и повторного чтения. Все 384 000 компонентов RGB совпали; открытые comment и demo:ticket в cipher-файле читаются. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-first.png
source-preview8.png
cipher-view8.png
restored-view8.png
cipher-data.png
metrics.json
encipher=true decipher=true lossless-save=1 restored-exact=1 public-metadata=1
Различить пиксельный поток, формат файла, глубину и открытые метаданные

| Часть процесса | Что сохраняется или меняется |
|---|---|
| $source | Нормализованный непрозрачный RGB8-исходник; оригинал остаётся отдельным объектом. |
| $cipher | Рабочий поток пикселей меняется. Изображение по-прежнему имеет размер, depth, заголовок и читаемые свойства. |
| cipher-data.png | Настоящий lossless-файл, из которого программа заново прочитала тот же cipher-поток. |
| $restored | Копия прочитанного файла. decipherImage изменяет её; затем сравниваются все компоненты с эталоном. |
| PNG8-просмотр | Отдельная иллюстрация. Если исходные cipher-данные 16-битные, такой просмотр теряет необходимые биты и не годится для восстановления. |
Модель напоминает обратимое перемешивание содержимого ячеек внутри узнаваемой упаковки. Это ограниченная аналогия: реальные операции работают с потоком пиксельных байтов и AES-CTR, а не просто переставляют цветные квадраты. Ширина, высота, каналы и глубина определяют представление данных. Один и тот же passphrase в этой реализации не создаёт случайно новый nonce; повтор на том же входе дал тот же cipher. Поэтому шумный вид сам по себе не доказывает стойкость или аутентичность.
Не смешивайте шифрование всего файла с преобразованием его пикселей. Произвольный файловый ciphertext нельзя открыть как PNG только потому, что он раньше был картинкой. И обратное: открытый PNG с шумом ещё не обязан быть исправным cipher-входом. Подписи, метаданные, точная геометрия и наличие обычного PNG-заголовка не проверяют правильность фразы. JPEG-кодирование или уменьшение глубины может испортить нужные значения даже при успешном чтении файла.
При диагностике полезны представление RGB, глубина каналов и потери сжатия. Начальный пример ограничен opaque RGB; результат не переносится автоматически на произвольный RGBA, CMYK, палитру, изменённый размер или другую реализацию AES.
Запустить короткий encipher и отдельно восстановить известный cipher-вход

Для первого короткого примера вход уже готов. icRestore декодирует два точных PNG из base64 и проверяет SHA; icSource открывает собственный RGB8 и добавляет только публичные свойства. Никаких сетевых данных, секретных ключей или чужих фотографий нет. Один изучаемый вызов меняет рабочую копию; затем отдельная запись сохраняет настоящие пиксели, а повторное чтение проверяет их сохранность.
Одним вызовом преобразовать пиксели и сохранить настоящий PNG
encipherImage вернул true и изменил пиксели рабочей копии. В настоящем encipher-data.png они сохранились точно после повторного чтения. Размер холста, формат изображения и открытые метаданные не стали скрытым файловым контейнером.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=clone $source;
$ok=$cipher->encipherImage($phrase);
icSavePNG($cipher,'encipher-data.png',8);
$loaded=new Imagick(__DIR__.'/encipher-data.png');
icEncipherProof($source,$cipher,$loaded,$ok,$before);
echo "image-cipher-encipher.png\nencipher-source8.png\nencipher-data.png\nmetrics.json\nencipher=true pixels-changed=1 saved-cipher-exact=1 source-unchanged=1\n";
encipherImage вернул true и изменил пиксели рабочей копии. В настоящем encipher-data.png они сохранились точно после повторного чтения. Размер холста, формат изображения и открытые метаданные не стали скрытым файловым контейнером. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-encipher.png
encipher-source8.png
encipher-data.png
metrics.json
encipher=true pixels-changed=1 saved-cipher-exact=1 source-unchanged=1
$ok = $restored->decipherImage($phrase);Минимальный decipher не должен незаметно создавать свой вход тем же вызовом encipher. Поэтому второй архив включает подлинный encoded-rgb8.png, полученный из этого рисунка той же версией и публичной фразой при отдельной подготовке. Объект $cipher читается из него; $restored — независимый clone. Метод получает строку и изменяет пиксели копии. После этого сравнение с известным источником доказывает восстановление именно этого рисунка.
Восстановить отдельный заранее подготовленный cipher-вход
Короткий decipher работает с настоящим encoded-rgb8.png, созданным заранее той же версией и публичной фразой. Он не повторяет encipher в этой программе. Рабочая копия восстановилась точно, fixture и эталон сохранены.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=new Imagick(__DIR__.'/encoded-rgb8.png');
$cipherBefore=icHash($cipher);
$restored=clone $cipher;
$ok=$restored->decipherImage($phrase);
icDecipherProof($source,$cipher,$restored,$ok,$before,$cipherBefore);
echo "image-cipher-decipher.png\ndecipher-input8.png\nmetrics.json\ndecipher=true restored-exact=1 cipher-input-unchanged=1 source-unchanged=1\n";
Короткий decipher работает с настоящим encoded-rgb8.png, созданным заранее той же версией и публичной фразой. Он не повторяет encipher в этой программе. Рабочая копия восстановилась точно, fixture и эталон сохранены. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-decipher.png
decipher-input8.png
metrics.json
decipher=true restored-exact=1 cipher-input-unchanged=1 source-unchanged=1
Сравнение RGB-хэша отличается от сравнения файлов по SHA. Новый PNG может иметь другой набор текстовых chunks и время записи, оставаясь теми же пикселями. icHash сравнивает массив RGB через PIXEL_SHORT: каждое значение нормализовано в диапазон 0..65535, даже если входной файл 8-битный. Отдельные file SHA проверяют точность входных fixtures и связывают созданные артефакты; они не подменяют проверку восстановленных пикселей.
Разобрать неправильную фразу, потери формата, глубину и собственную политику входа

При разборе сбоя задайте конкретный вопрос: неправильная фраза, испорченный формат или потерянные биты? Программа ниже сравнивает эти случаи на одном источнике. Она сохраняет настоящий 16-битный cipher в PNG16 и отдельно намеренно портит его, записав как PNG8. Файлы данных не заменяются иллюстрациями. Последняя часть показывает собственную политику входа и реальные ошибки пустых объектов.
Найти причину невосстановления: фраза, JPEG, глубина или неподходящая проверка
Правильный lossless-вход восстановился; неверная фраза, JPEG и потеря глубины дали true без правильного результата. PNG16 сохранил 16-битный cipher и восстановил источник. Собственная политика отклонила пустую/NUL-фразу, а API отдельно продемонстрировал принятие пустой строки. Две ошибки пустого объекта пойманы.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=new Imagick(__DIR__.'/encoded-rgb8.png');$cipherBefore=icHash($cipher);
$good=clone $cipher;$goodReturn=$good->decipherImage($phrase);
$wrong=clone $cipher;$wrongReturn=$wrong->decipherImage('different-public-demo-phrase');
$jpeg=clone $cipher;$jpeg->setImageFormat('jpeg');$jpeg->setImageCompressionQuality(90);
$jpeg->writeImage(__DIR__.'/encoded-lossy.jpg');
$jpeg=new Imagick(__DIR__.'/encoded-lossy.jpg');$jpegReturn=$jpeg->decipherImage($phrase);
$source16=clone $source;$source16->setImageDepth(16);
$cipher16=clone $source16;$enc16=$cipher16->encipherImage($phrase);
icSavePNG($cipher16,'workflow-cipher16.png',16);
icSavePNG($cipher16,'workflow-cipher16-lost8.png',8);
$read16=new Imagick(__DIR__.'/workflow-cipher16.png');$saved16Hash=icHash($read16);
$readLost8=new Imagick(__DIR__.'/workflow-cipher16-lost8.png');
$dec16=$read16->decipherImage($phrase);$decLost8=$readLost8->decipherImage($phrase);
$stripped=clone $cipher;$stripped->stripImage();$strippedMetadata=icMetadata($stripped);
$stripReturn=$stripped->decipherImage($phrase);
$emptyPhrase=clone $source;$emptyEnc=$emptyPhrase->encipherImage('');$emptyHash=icHash($emptyPhrase);
$emptyDec=$emptyPhrase->decipherImage('');
$policyRejected=[];
foreach(['',"demo\0suffix"] as $badPhrase){
try{icPhrase($badPhrase);$policyRejected[]=false;}catch(InvalidArgumentException $error){$policyRejected[]=true;}
}
$acceptedPhrase=icPhrase($phrase);
$again=clone $source;$againReturn=$again->encipherImage($acceptedPhrase);
$errors=[];
foreach(['encipher_empty'=>fn()=>(new Imagick())->encipherImage($phrase),
'decipher_empty'=>fn()=>(new Imagick())->decipherImage($phrase)] as $name=>$call){
try{$call();$errors[$name]=['class'=>'not caught','message'=>''];}
catch(Throwable $error){$errors[$name]=['class'=>get_class($error),'message'=>$error->getMessage()];}
}
icView($good,'workflow-correct8.png');icView($wrong,'workflow-wrong8.png');
icView($jpeg,'workflow-jpeg8.png');icView($readLost8,'workflow-depth-loss8.png');
icView($read16,'workflow-png16-restored8.png');
$rects=icCompare([$good,$wrong,$jpeg,$readLost8],'image-cipher-workflow.png');
$wrongError=icError($source,$wrong);$jpegError=icError($source,$jpeg);$depthError=icError($source,$readLost8);
icDiagnostic('image-cipher-workflow',['executed_apis'=>['encipherImage','decipherImage'],
'correct'=>['return'=>$goodReturn,'restored_exact'=>icHash($good)===$before,'error'=>icError($source,$good)],
'wrong_phrase'=>['return'=>$wrongReturn,'restored_exact'=>icHash($wrong)===$before,'error'=>$wrongError],
'jpeg90'=>['return'=>$jpegReturn,'restored_exact'=>icHash($jpeg)===$before,'error'=>$jpegError],
'depth16'=>['encipher_return'=>$enc16,'cipher_RGB16_sha256'=>icHash($cipher16),'saved_RGB16_sha256'=>$saved16Hash,
'decipher16_return'=>$dec16,'restored16_exact'=>icHash($read16)===$before,'error16'=>icError($source,$read16),
'forced8_return'=>$decLost8,'forced8_restored_exact'=>icHash($readLost8)===$before,'forced8_error'=>$depthError],
'strip'=>['properties_before_decode'=>$strippedMetadata,'return'=>$stripReturn,'restored_exact'=>icHash($stripped)===$before],
'empty_phrase'=>['encipher_return'=>$emptyEnc,'cipher_changed'=>$emptyHash!==$before,'decipher_return'=>$emptyDec,'restored_exact'=>icHash($emptyPhrase)===$before],
'application_policy_rejected'=>$policyRejected,'deterministic_cipher'=>icHash($again)===$cipherBefore,
'errors'=>$errors,'public_metadata'=>icMetadata($cipher),'source_unchanged'=>icHash($source)===$before,'cipher_input_unchanged'=>icHash($cipher)===$cipherBefore,'panel_geometry'=>$rects,
'checks'=>['correct_returns_true_and_restores'=>$goodReturn===true&&icHash($good)===$before,
'wrong_phrase_true_is_not_authentication'=>$wrongReturn===true&&$wrongError['max']>0,
'JPEG_true_does_not_restore'=>$jpegReturn===true&&$jpegError['max']>0,
'PNG16_preserves_cipher_and_restores'=>$enc16===true&&$saved16Hash===icHash($cipher16)&&$dec16===true&&icHash($read16)===$before,
'forced_PNG8_loses_16bit_cipher'=>$decLost8===true&&$depthError['max']>0,
'strip_is_not_remove_all_properties'=>$strippedMetadata['comment']===false&&$strippedMetadata['ticket']==='PUBLIC-42'&&$strippedMetadata['cipher_mode']==='CTR',
'stripped_pixels_still_restore'=>$stripReturn===true&&icHash($stripped)===$before,
'API_accepts_empty_phrase'=>$emptyEnc===true&&$emptyHash!==$before&&$emptyDec===true&&icHash($emptyPhrase)===$before,
'own_policy_rejects_empty_and_NUL'=>$policyRejected===[true,true],
'same_phrase_reuses_cipher'=>$againReturn===true&&icHash($again)===$cipherBefore,
'two_empty_object_errors'=>array_column($errors,'class')===['ImagickException','ImagickException'],
'originals_preserved'=>icHash($source)===$before&&icHash($cipher)===$cipherBefore]]);
echo "image-cipher-workflow.png\nworkflow-correct8.png\nworkflow-wrong8.png\nworkflow-jpeg8.png\nworkflow-depth-loss8.png\nworkflow-png16-restored8.png\nworkflow-cipher16.png\nworkflow-cipher16-lost8.png\nencoded-lossy.jpg\nmetrics.json\ncorrect=exact wrong=true-but-invalid JPEG=true-but-invalid PNG16=exact forced8=invalid policy-rejections=2 errors=2\n";
Правильный lossless-вход восстановился; неверная фраза, JPEG и потеря глубины дали true без правильного результата. PNG16 сохранил 16-битный cipher и восстановил источник. Собственная политика отклонила пустую/NUL-фразу, а API отдельно продемонстрировал принятие пустой строки. Две ошибки пустого объекта пойманы. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-workflow.png
workflow-correct8.png
workflow-wrong8.png
workflow-jpeg8.png
workflow-depth-loss8.png
workflow-png16-restored8.png
workflow-cipher16.png
workflow-cipher16-lost8.png
encoded-lossy.jpg
metrics.json
correct=exact wrong=true-but-invalid JPEG=true-but-invalid PNG16=exact forced8=invalid policy-rejections=2 errors=2
| Проверка | Реальный результат |
|---|---|
| PNG8, правильная фраза | true и точное восстановление; max=0 для всех 384 000 компонентов. |
| Неверная публичная фраза | true, но восстановление неверно; RMSE≈35161.67 в единицах PIXEL_SHORT. |
| Cipher сохранён как JPEG90 | true после decipher, но результат неверен; RMSE≈12256.55. |
| Настоящий 16-битный cipher сохранён как PNG16 | Те же cipher-компоненты после чтения; восстановление точное. |
| Тот же 16-битный cipher принудительно сохранён как PNG8 | true без верного восстановления; RMSE≈35206.00. |
| Пустой Imagick у любого из методов | ImagickException: Can not process empty Imagick object. |
Указанные RMSE измерены по RGB через PIXEL_SHORT непосредственно в объектах. Они не являются процентами качества и не вычисляются из уменьшенных скриншотов. Просмотры PNG8 кодируются отдельно; у 16-битного результата они специально служат только показу. Имена workflow-cipher16.png и workflow-cipher16-lost8.png обозначают разные реальные файлы данных; превращение второго обратно в 16-битный PNG потерянные биты не восстановит.
stripImage здесь удалил comment, но оставил demo:ticket и cipher:* свойства. Поэтому нельзя объявлять вызов универсальным удалением всех пользовательских полей. В свойствах перед decipher по-прежнему виден CTR; восстановление после strip осталось точным. Это не проверка подлинности по метаданным: конкретный Core пересчитывает поток и не сверяет аутентификационный тег. Фраза, повреждённые данные или неправильное представление могут дать true и неправильный рисунок.
Пустая фраза в этом API тоже дала true у обоих методов, изменила cipher-пиксели и позволила восстановление. icPhrase явно запрещает пустую строку и NUL только как нашу политику приложения; такой запрет не приписывается официальной сигнатуре. NUL важен потому, что точный внутренний C-путь использует строку с нулевым завершением. Не превращайте этот частный guard в заявление о безопасности всего механизма. Здесь функция просто возвращает допустимую демонстрационную строку или выбрасывает InvalidArgumentException; функции и исключения объясняются отдельно.
Сохранить полный официальный базис и воспроизвести конкретную версию
Полный официальный базис
Imagick::encipherImage(string $passphrase): boolОфициальная доступность: PECL imagick 2 начиная с 2.3.0 и PECL imagick 3, связанный ImageMagick не ниже 6.3.9. Оба метода действуют в нашей Imagick 3.8.1 с IM7 и не помечены deprecated. Исходные записи целиком сохранены ниже, включая взаимные ссылки; официальных программных примеров и отдельного раздела ошибок на этих двух страницах нет.
encipherImage принимает обязательную строку passphrase и преобразует обычные пиксели в cipher-пиксели текущего изображения. Возвращает true при успехе. Прежнее содержимое нужно восстанавливать paired-методом decipherImage. Результат остаётся изображением с обычным заголовком, геометрией и читаемыми метаданными, а не произвольным зашифрованным бинарным файлом.
Строка PHP — содержимое фразы, а не путь к файлу. Это отличие от CLI-примеров ImageMagick с -encipher passphrase.txt. Передача имени файла PHP-методу не прочитает этот файл как фразу. Сам метод меняет объект; bool не содержит пиксели и не является именем выходного файла. Для сохранения требуется отдельная операция записи, для сохранения исходника — отдельный clone.
В точном wrapper требуется непустое текущее изображение; оба вызова на пустом Imagick выбросили ImagickException с Can not process empty Imagick object. При выключенной compile-time поддержке Cipher Core выдаёт CipherSupportNotEnabled, хотя имена методов могут существовать. При этом пустая строка passphrase в данной реализации допускается и roundtrip выполняется. Наш запрет пустой/NUL-строки — явно дополнительная политика приложения, не объявление такого ограничения в официальном API.
Exact IM7.1.1-46 использует AES-CTR над экспортированными пиксельными байтами; nonce детерминирован, независимой проверки аутентичности нет. Current official guide относит этот механизм только к обычной обфускации. Не используйте его как обещание конфиденциальности документов, защищённое хранилище или проверку целостности. Полнофайловое шифрование с аутентификацией — другая задача, разобранная в соответствующих уроках Sodium и OpenSSL.
Полный официальный XML: описание, доступность, параметр, return и связанные методы
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.encipherimage">
<refnamediv>
<refname>Imagick::encipherImage</refname>
<refpurpose>Enciphers an image</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<modifier>public</modifier> <type>bool</type><methodname>Imagick::encipherImage</methodname>
<methodparam><type>string</type><parameter>passphrase</parameter></methodparam>
</methodsynopsis>
<para>
Converts plain pixels to enciphered pixels. The image is not readable
until it has been deciphered using <function>Imagick::decipherImage</function>
&imagick.method.available.0x639;
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<para>
<variablelist>
<varlistentry>
<term><parameter>passphrase</parameter></term>
<listitem>
<para>
The passphrase
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
&imagick.return.success;
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>Imagick::decipherImage</function></member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->
Полный официальный базис
Imagick::decipherImage(string $passphrase): boolОфициальная доступность: PECL imagick 2 начиная с 2.3.0 и PECL imagick 3, связанный ImageMagick не ниже 6.3.9. Оба метода действуют в нашей Imagick 3.8.1 с IM7 и не помечены deprecated. Исходные записи целиком сохранены ниже, включая взаимные ссылки; официальных программных примеров и отдельного раздела ошибок на этих двух страницах нет.
decipherImage принимает обязательную строку passphrase; исходное изображение должно быть предварительно обработано encipherImage. Возвращает true при завершении операции. Для правильного восстановления нужны та же фраза и сохранённый соответствующий поток cipher-пикселей. Метод не распознаёт автоматически чужую схему AES, JPEG-повреждение или правильность пароля; неверная фраза в нашем запуске тоже вернула true.
Строка PHP — содержимое фразы, а не путь к файлу. Это отличие от CLI-примеров ImageMagick с -encipher passphrase.txt. Передача имени файла PHP-методу не прочитает этот файл как фразу. Сам метод меняет объект; bool не содержит пиксели и не является именем выходного файла. Для сохранения требуется отдельная операция записи, для сохранения исходника — отдельный clone.
В точном wrapper требуется непустое текущее изображение; оба вызова на пустом Imagick выбросили ImagickException с Can not process empty Imagick object. При выключенной compile-time поддержке Cipher Core выдаёт CipherSupportNotEnabled, хотя имена методов могут существовать. При этом пустая строка passphrase в данной реализации допускается и roundtrip выполняется. Наш запрет пустой/NUL-строки — явно дополнительная политика приложения, не объявление такого ограничения в официальном API.
Exact IM7.1.1-46 использует AES-CTR над экспортированными пиксельными байтами; nonce детерминирован, независимой проверки аутентичности нет. Current official guide относит этот механизм только к обычной обфускации. Не используйте его как обещание конфиденциальности документов, защищённое хранилище или проверку целостности. Полнофайловое шифрование с аутентификацией — другая задача, разобранная в соответствующих уроках Sodium и OpenSSL.
Полный официальный XML: описание, доступность, параметр, return и связанные методы
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.decipherimage">
<refnamediv>
<refname>Imagick::decipherImage</refname>
<refpurpose>Deciphers an image</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<modifier>public</modifier> <type>bool</type><methodname>Imagick::decipherImage</methodname>
<methodparam><type>string</type><parameter>passphrase</parameter></methodparam>
</methodsynopsis>
<para>
Deciphers image that has been enciphered before. The image must be enciphered
using <function>Imagick::encipherImage</function>.
&imagick.method.available.0x639;
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<para>
<variablelist>
<varlistentry>
<term><parameter>passphrase</parameter></term>
<listitem>
<para>
The passphrase
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
&imagick.return.success;
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>Imagick::encipherImage</function></member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->
Для воспроизведения нужны PHP CLI, Imagick, ImageMagick с поддержкой Cipher, PNG и JPEG. Наши примеры действительно исполнялись в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI. Наличие одноимённого метода или похожего шума не заменяет запуск. Если встретили старую систему, сначала сохраните её точные входные файлы и параметры; не запускайте JPEG, resize, alpha-преобразование или уменьшение depth над cipher как над обычной фотографией.
Полный исполняемый fixture, отдельно от коротких вызовов
<?php
declare(strict_types=1);
const IC_DEMO_PHRASE='public-demo-passphrase-2026-only';
function icRestore():void {
$hashes=['source-rgb8.png'=>'b6af02ed97020582a27e993a1565de41e23ea97ff7be11ac132d68204d56d483','encoded-rgb8.png'=>'ea4628f5c5a2a42a75728c94865023f82aea2fb5e33f823a1eb1e3f656a5de2a'];
foreach($hashes as $name=>$hash){
$raw=base64_decode((string)file_get_contents(__DIR__.'/'.$name.'.b64'),true);
if($raw===false||hash('sha256',$raw)!==$hash||file_put_contents(__DIR__.'/'.$name,$raw)===false){throw new RuntimeException('Known demonstration input missing or changed');}
}
}
function icSource():Imagick {
$image=new Imagick(__DIR__.'/source-rgb8.png');
$image->setImageProperty('comment','PUBLIC DEMO ORDER 42');$image->setImageProperty('demo:ticket','PUBLIC-42');return $image;
}
function icPixels(Imagick $image):array {return $image->exportImagePixels(0,0,400,320,'RGB',Imagick::PIXEL_SHORT);}
function icHash(Imagick $image):string {return hash('sha256',json_encode(icPixels($image),JSON_THROW_ON_ERROR));}
function icError(Imagick $a,Imagick $b):array {
$x=icPixels($a);$y=icPixels($b);$max=0;$sum=0.0;$changed=0;
foreach($x as $i=>$v){$d=abs($v-$y[$i]);$max=max($max,$d);$sum+=$d*$d;$changed+=(int)($d!==0);}
return ['max'=>$max,'RMSE'=>sqrt($sum/count($x)),'changed_components'=>$changed,'components'=>count($x)];
}
function icMetadata(Imagick $image):array {
return ['comment'=>$image->getImageProperty('comment'),'ticket'=>$image->getImageProperty('demo:ticket'),
'cipher_type'=>$image->getImageProperty('cipher:type'),'cipher_mode'=>$image->getImageProperty('cipher:mode'),
'cipher_nonce'=>$image->getImageProperty('cipher:nonce'),'width'=>$image->getImageWidth(),'height'=>$image->getImageHeight(),
'depth'=>$image->getImageDepth(),'alpha'=>$image->getImageAlphaChannel()];
}
function icSavePNG(Imagick $image,string $name,int $depth):void {
$copy=clone $image;$copy->setImageFormat('png');$copy->setImageDepth($depth);
$copy->setOption('png:bit-depth',(string)$depth);$copy->setOption('png:color-type','2');
if(!$copy->writeImage(__DIR__.'/'.$name)){throw new RuntimeException('PNG write failed');}
}
function icView(Imagick $image,string $name):void {
$copy=clone $image;$copy->stripImage();$copy->setOption('png:exclude-chunk','all');icSavePNG($copy,$name,8);
}
function icCompare(array $images,string $name):array {
$board=new Imagick();$board->newImage(400*count($images),320,'white');$rects=[];
foreach($images as $i=>$image){
if($image->getImageWidth()!==400||$image->getImageHeight()!==320){throw new RuntimeException('Expected natural 400x320 panel');}
$raw=clone $image;$raw->stripImage();$board->compositeImage($raw,Imagick::COMPOSITE_OVER,400*$i,0);
$rects[]=['x'=>400*$i,'y'=>0,'width'=>400,'height'=>320];
}
icView($board,$name);return $rects;
}
function icPhrase(string $phrase):string {
if($phrase===''||str_contains($phrase,"\0")){throw new InvalidArgumentException('Demo policy requires a nonempty phrase without NUL');}
return $phrase;
}
function icDiagnostic(string $id,array $data):void {
$doc=['schema'=>'aen-image-cipher-program-diagnostics-v1','example_id'=>$id,
'runtime'=>['php'=>PHP_VERSION,'imagick'=>phpversion('imagick'),'image_magick'=>Imagick::getVersion()]]+$data;
if(file_put_contents(__DIR__.'/metrics.json',json_encode($doc,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)."\n")===false){throw new RuntimeException('Diagnostic write failed');}
}
function icFirstProof(Imagick $source,Imagick $cipher,Imagick $loaded,Imagick $restored,bool $enc,bool $dec,string $before):void {
icView($source,'source-preview8.png');icView($cipher,'cipher-view8.png');icView($restored,'restored-view8.png');
$rects=icCompare([$source,$cipher,$restored],'image-cipher-first.png');$error=icError($source,$restored);$meta=icMetadata($loaded);
icDiagnostic('image-cipher-first',['executed_apis'=>['encipherImage','decipherImage'],'encipher_return'=>$enc,'decipher_return'=>$dec,
'source_RGB16_sha256'=>icHash($source),'cipher_RGB16_sha256'=>icHash($cipher),'file_RGB16_sha256'=>icHash($loaded),
'cipher_survives_save'=>icHash($loaded)===icHash($cipher),'restored_exact'=>icHash($restored)===$before,'restored_error'=>$error,
'public_metadata'=>$meta,'source_unchanged'=>icHash($source)===$before,'panel_geometry'=>$rects,
'checks'=>['encipher_true'=>$enc===true,'decipher_true'=>$dec===true,'cipher_changes_pixels'=>icHash($cipher)!==$before,
'lossless_file_keeps_cipher'=>icHash($loaded)===icHash($cipher),'source_restored_exactly'=>$error['max']===0,
'comment_still_public'=>$meta['comment']==='PUBLIC DEMO ORDER 42','ticket_still_public'=>$meta['ticket']==='PUBLIC-42',
'source_preserved'=>icHash($source)===$before]]);
}
function icEncipherProof(Imagick $source,Imagick $cipher,Imagick $loaded,bool $ok,string $before):void {
icView($source,'encipher-source8.png');icView($cipher,'image-cipher-encipher.png');
icDiagnostic('image-cipher-encipher-minimal',['executed_api'=>'encipherImage','encipher_return'=>$ok,
'source_RGB16_sha256'=>$before,'cipher_RGB16_sha256'=>icHash($cipher),'file_RGB16_sha256'=>icHash($loaded),
'metadata'=>icMetadata($loaded),'source_unchanged'=>icHash($source)===$before,
'checks'=>['encipher_true'=>$ok===true,'pixels_changed'=>icHash($cipher)!==$before,'lossless_file_keeps_cipher'=>icHash($loaded)===icHash($cipher),
'known_cipher'=>icHash($cipher)==='e8d282f8db735d0e4ef7fc91539b062eef67da78e3cc4128e2dbf4956db993cc','source_preserved'=>icHash($source)===$before]]);
}
function icDecipherProof(Imagick $source,Imagick $cipher,Imagick $restored,bool $ok,string $before,string $cipherBefore):void {
icView($cipher,'decipher-input8.png');icView($restored,'image-cipher-decipher.png');$error=icError($source,$restored);
icDiagnostic('image-cipher-decipher-minimal',['executed_api'=>'decipherImage','decipher_return'=>$ok,
'cipher_RGB16_sha256'=>$cipherBefore,'restored_RGB16_sha256'=>icHash($restored),'restored_exact'=>icHash($restored)===$before,
'restored_error'=>$error,'metadata_after_decode'=>icMetadata($restored),'cipher_unchanged'=>icHash($cipher)===$cipherBefore,'source_unchanged'=>icHash($source)===$before,
'checks'=>['decipher_true'=>$ok===true,'exact_source_restored'=>$error['max']===0,'cipher_fixture_preserved'=>icHash($cipher)===$cipherBefore,
'source_preserved'=>icHash($source)===$before]]);
}
Helper не скрывает encipher/decipher: оба изучаемых вызова находятся в main-программах. icRestore проверяет байты двух fixtures; icSavePNG пишет данные на отдельной копии с явной глубиной и TrueColor PNG. icView делает отдельное PNG8-представление; icCompare собирает натуральные панели 400 × 320. icHash и icError измеряют полный RGB через PIXEL_SHORT, icDiagnostic сохраняет JSON. Сначала понятный вызов, затем явно названная аппаратура сохранения и проверки — это разные части примера.
Упражнение: заменить в первом примере только decipher-фразу на different-public-demo-phrase и предсказать отдельно return и сравнение пикселей. Решение: в проверенной реализации return останется true, совпадение с эталоном исчезнет. Следующее упражнение — сохранить cipher16 с 16-битной глубиной и затем только его просмотровую копию с 8-битной; для восстановления читать настоящий 16-битный файл. Для защищённого шифрования полного файла изучайте Sodium и OpenSSL; это отдельные контракты, а не взаимозаменяемые названия для decipherImage.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
Imagick::encipherImageПонять существующую обфускацию пикселей и проверить настоящий lossless roundtrip
PECL imagick 2 >= 2.3.0, PECL imagick 3 · imagick
Imagick::encipherImage(string $passphrase): boolПолный официальный базис
Imagick::encipherImage(string $passphrase): boolОфициальная доступность: PECL imagick 2 начиная с 2.3.0 и PECL imagick 3, связанный ImageMagick не ниже 6.3.9. Оба метода действуют в нашей Imagick 3.8.1 с IM7 и не помечены deprecated. Исходные записи целиком сохранены ниже, включая взаимные ссылки; официальных программных примеров и отдельного раздела ошибок на этих двух страницах нет.
encipherImage принимает обязательную строку passphrase и преобразует обычные пиксели в cipher-пиксели текущего изображения. Возвращает true при успехе. Прежнее содержимое нужно восстанавливать paired-методом decipherImage. Результат остаётся изображением с обычным заголовком, геометрией и читаемыми метаданными, а не произвольным зашифрованным бинарным файлом.
Строка PHP — содержимое фразы, а не путь к файлу. Это отличие от CLI-примеров ImageMagick с -encipher passphrase.txt. Передача имени файла PHP-методу не прочитает этот файл как фразу. Сам метод меняет объект; bool не содержит пиксели и не является именем выходного файла. Для сохранения требуется отдельная операция записи, для сохранения исходника — отдельный clone.
В точном wrapper требуется непустое текущее изображение; оба вызова на пустом Imagick выбросили ImagickException с Can not process empty Imagick object. При выключенной compile-time поддержке Cipher Core выдаёт CipherSupportNotEnabled, хотя имена методов могут существовать. При этом пустая строка passphrase в данной реализации допускается и roundtrip выполняется. Наш запрет пустой/NUL-строки — явно дополнительная политика приложения, не объявление такого ограничения в официальном API.
Exact IM7.1.1-46 использует AES-CTR над экспортированными пиксельными байтами; nonce детерминирован, независимой проверки аутентичности нет. Current official guide относит этот механизм только к обычной обфускации. Не используйте его как обещание конфиденциальности документов, защищённое хранилище или проверку целостности. Полнофайловое шифрование с аутентификацией — другая задача, разобранная в соответствующих уроках Sodium и OpenSSL.
Полный официальный XML: описание, доступность, параметр, return и связанные методы
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.encipherimage">
<refnamediv>
<refname>Imagick::encipherImage</refname>
<refpurpose>Enciphers an image</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<modifier>public</modifier> <type>bool</type><methodname>Imagick::encipherImage</methodname>
<methodparam><type>string</type><parameter>passphrase</parameter></methodparam>
</methodsynopsis>
<para>
Converts plain pixels to enciphered pixels. The image is not readable
until it has been deciphered using <function>Imagick::decipherImage</function>
&imagick.method.available.0x639;
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<para>
<variablelist>
<varlistentry>
<term><parameter>passphrase</parameter></term>
<listitem>
<para>
The passphrase
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
&imagick.return.success;
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>Imagick::decipherImage</function></member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->
Одним вызовом преобразовать пиксели и сохранить настоящий PNG
encipherImage вернул true и изменил пиксели рабочей копии. В настоящем encipher-data.png они сохранились точно после повторного чтения. Размер холста, формат изображения и открытые метаданные не стали скрытым файловым контейнером.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=clone $source;
$ok=$cipher->encipherImage($phrase);
icSavePNG($cipher,'encipher-data.png',8);
$loaded=new Imagick(__DIR__.'/encipher-data.png');
icEncipherProof($source,$cipher,$loaded,$ok,$before);
echo "image-cipher-encipher.png\nencipher-source8.png\nencipher-data.png\nmetrics.json\nencipher=true pixels-changed=1 saved-cipher-exact=1 source-unchanged=1\n";
encipherImage вернул true и изменил пиксели рабочей копии. В настоящем encipher-data.png они сохранились точно после повторного чтения. Размер холста, формат изображения и открытые метаданные не стали скрытым файловым контейнером. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-encipher.png
encipher-source8.png
encipher-data.png
metrics.json
encipher=true pixels-changed=1 saved-cipher-exact=1 source-unchanged=1
Найти причину невосстановления: фраза, JPEG, глубина или неподходящая проверка
Правильный lossless-вход восстановился; неверная фраза, JPEG и потеря глубины дали true без правильного результата. PNG16 сохранил 16-битный cipher и восстановил источник. Собственная политика отклонила пустую/NUL-фразу, а API отдельно продемонстрировал принятие пустой строки. Две ошибки пустого объекта пойманы.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=new Imagick(__DIR__.'/encoded-rgb8.png');$cipherBefore=icHash($cipher);
$good=clone $cipher;$goodReturn=$good->decipherImage($phrase);
$wrong=clone $cipher;$wrongReturn=$wrong->decipherImage('different-public-demo-phrase');
$jpeg=clone $cipher;$jpeg->setImageFormat('jpeg');$jpeg->setImageCompressionQuality(90);
$jpeg->writeImage(__DIR__.'/encoded-lossy.jpg');
$jpeg=new Imagick(__DIR__.'/encoded-lossy.jpg');$jpegReturn=$jpeg->decipherImage($phrase);
$source16=clone $source;$source16->setImageDepth(16);
$cipher16=clone $source16;$enc16=$cipher16->encipherImage($phrase);
icSavePNG($cipher16,'workflow-cipher16.png',16);
icSavePNG($cipher16,'workflow-cipher16-lost8.png',8);
$read16=new Imagick(__DIR__.'/workflow-cipher16.png');$saved16Hash=icHash($read16);
$readLost8=new Imagick(__DIR__.'/workflow-cipher16-lost8.png');
$dec16=$read16->decipherImage($phrase);$decLost8=$readLost8->decipherImage($phrase);
$stripped=clone $cipher;$stripped->stripImage();$strippedMetadata=icMetadata($stripped);
$stripReturn=$stripped->decipherImage($phrase);
$emptyPhrase=clone $source;$emptyEnc=$emptyPhrase->encipherImage('');$emptyHash=icHash($emptyPhrase);
$emptyDec=$emptyPhrase->decipherImage('');
$policyRejected=[];
foreach(['',"demo\0suffix"] as $badPhrase){
try{icPhrase($badPhrase);$policyRejected[]=false;}catch(InvalidArgumentException $error){$policyRejected[]=true;}
}
$acceptedPhrase=icPhrase($phrase);
$again=clone $source;$againReturn=$again->encipherImage($acceptedPhrase);
$errors=[];
foreach(['encipher_empty'=>fn()=>(new Imagick())->encipherImage($phrase),
'decipher_empty'=>fn()=>(new Imagick())->decipherImage($phrase)] as $name=>$call){
try{$call();$errors[$name]=['class'=>'not caught','message'=>''];}
catch(Throwable $error){$errors[$name]=['class'=>get_class($error),'message'=>$error->getMessage()];}
}
icView($good,'workflow-correct8.png');icView($wrong,'workflow-wrong8.png');
icView($jpeg,'workflow-jpeg8.png');icView($readLost8,'workflow-depth-loss8.png');
icView($read16,'workflow-png16-restored8.png');
$rects=icCompare([$good,$wrong,$jpeg,$readLost8],'image-cipher-workflow.png');
$wrongError=icError($source,$wrong);$jpegError=icError($source,$jpeg);$depthError=icError($source,$readLost8);
icDiagnostic('image-cipher-workflow',['executed_apis'=>['encipherImage','decipherImage'],
'correct'=>['return'=>$goodReturn,'restored_exact'=>icHash($good)===$before,'error'=>icError($source,$good)],
'wrong_phrase'=>['return'=>$wrongReturn,'restored_exact'=>icHash($wrong)===$before,'error'=>$wrongError],
'jpeg90'=>['return'=>$jpegReturn,'restored_exact'=>icHash($jpeg)===$before,'error'=>$jpegError],
'depth16'=>['encipher_return'=>$enc16,'cipher_RGB16_sha256'=>icHash($cipher16),'saved_RGB16_sha256'=>$saved16Hash,
'decipher16_return'=>$dec16,'restored16_exact'=>icHash($read16)===$before,'error16'=>icError($source,$read16),
'forced8_return'=>$decLost8,'forced8_restored_exact'=>icHash($readLost8)===$before,'forced8_error'=>$depthError],
'strip'=>['properties_before_decode'=>$strippedMetadata,'return'=>$stripReturn,'restored_exact'=>icHash($stripped)===$before],
'empty_phrase'=>['encipher_return'=>$emptyEnc,'cipher_changed'=>$emptyHash!==$before,'decipher_return'=>$emptyDec,'restored_exact'=>icHash($emptyPhrase)===$before],
'application_policy_rejected'=>$policyRejected,'deterministic_cipher'=>icHash($again)===$cipherBefore,
'errors'=>$errors,'public_metadata'=>icMetadata($cipher),'source_unchanged'=>icHash($source)===$before,'cipher_input_unchanged'=>icHash($cipher)===$cipherBefore,'panel_geometry'=>$rects,
'checks'=>['correct_returns_true_and_restores'=>$goodReturn===true&&icHash($good)===$before,
'wrong_phrase_true_is_not_authentication'=>$wrongReturn===true&&$wrongError['max']>0,
'JPEG_true_does_not_restore'=>$jpegReturn===true&&$jpegError['max']>0,
'PNG16_preserves_cipher_and_restores'=>$enc16===true&&$saved16Hash===icHash($cipher16)&&$dec16===true&&icHash($read16)===$before,
'forced_PNG8_loses_16bit_cipher'=>$decLost8===true&&$depthError['max']>0,
'strip_is_not_remove_all_properties'=>$strippedMetadata['comment']===false&&$strippedMetadata['ticket']==='PUBLIC-42'&&$strippedMetadata['cipher_mode']==='CTR',
'stripped_pixels_still_restore'=>$stripReturn===true&&icHash($stripped)===$before,
'API_accepts_empty_phrase'=>$emptyEnc===true&&$emptyHash!==$before&&$emptyDec===true&&icHash($emptyPhrase)===$before,
'own_policy_rejects_empty_and_NUL'=>$policyRejected===[true,true],
'same_phrase_reuses_cipher'=>$againReturn===true&&icHash($again)===$cipherBefore,
'two_empty_object_errors'=>array_column($errors,'class')===['ImagickException','ImagickException'],
'originals_preserved'=>icHash($source)===$before&&icHash($cipher)===$cipherBefore]]);
echo "image-cipher-workflow.png\nworkflow-correct8.png\nworkflow-wrong8.png\nworkflow-jpeg8.png\nworkflow-depth-loss8.png\nworkflow-png16-restored8.png\nworkflow-cipher16.png\nworkflow-cipher16-lost8.png\nencoded-lossy.jpg\nmetrics.json\ncorrect=exact wrong=true-but-invalid JPEG=true-but-invalid PNG16=exact forced8=invalid policy-rejections=2 errors=2\n";
Правильный lossless-вход восстановился; неверная фраза, JPEG и потеря глубины дали true без правильного результата. PNG16 сохранил 16-битный cipher и восстановил источник. Собственная политика отклонила пустую/NUL-фразу, а API отдельно продемонстрировал принятие пустой строки. Две ошибки пустого объекта пойманы. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-workflow.png
workflow-correct8.png
workflow-wrong8.png
workflow-jpeg8.png
workflow-depth-loss8.png
workflow-png16-restored8.png
workflow-cipher16.png
workflow-cipher16-lost8.png
encoded-lossy.jpg
metrics.json
correct=exact wrong=true-but-invalid JPEG=true-but-invalid PNG16=exact forced8=invalid policy-rejections=2 errors=2
Imagick::decipherImageПонять существующую обфускацию пикселей и проверить настоящий lossless roundtrip
PECL imagick 2 >= 2.3.0, PECL imagick 3 · imagick
Imagick::decipherImage(string $passphrase): boolПолный официальный базис
Imagick::decipherImage(string $passphrase): boolОфициальная доступность: PECL imagick 2 начиная с 2.3.0 и PECL imagick 3, связанный ImageMagick не ниже 6.3.9. Оба метода действуют в нашей Imagick 3.8.1 с IM7 и не помечены deprecated. Исходные записи целиком сохранены ниже, включая взаимные ссылки; официальных программных примеров и отдельного раздела ошибок на этих двух страницах нет.
decipherImage принимает обязательную строку passphrase; исходное изображение должно быть предварительно обработано encipherImage. Возвращает true при завершении операции. Для правильного восстановления нужны та же фраза и сохранённый соответствующий поток cipher-пикселей. Метод не распознаёт автоматически чужую схему AES, JPEG-повреждение или правильность пароля; неверная фраза в нашем запуске тоже вернула true.
Строка PHP — содержимое фразы, а не путь к файлу. Это отличие от CLI-примеров ImageMagick с -encipher passphrase.txt. Передача имени файла PHP-методу не прочитает этот файл как фразу. Сам метод меняет объект; bool не содержит пиксели и не является именем выходного файла. Для сохранения требуется отдельная операция записи, для сохранения исходника — отдельный clone.
В точном wrapper требуется непустое текущее изображение; оба вызова на пустом Imagick выбросили ImagickException с Can not process empty Imagick object. При выключенной compile-time поддержке Cipher Core выдаёт CipherSupportNotEnabled, хотя имена методов могут существовать. При этом пустая строка passphrase в данной реализации допускается и roundtrip выполняется. Наш запрет пустой/NUL-строки — явно дополнительная политика приложения, не объявление такого ограничения в официальном API.
Exact IM7.1.1-46 использует AES-CTR над экспортированными пиксельными байтами; nonce детерминирован, независимой проверки аутентичности нет. Current official guide относит этот механизм только к обычной обфускации. Не используйте его как обещание конфиденциальности документов, защищённое хранилище или проверку целостности. Полнофайловое шифрование с аутентификацией — другая задача, разобранная в соответствующих уроках Sodium и OpenSSL.
Полный официальный XML: описание, доступность, параметр, return и связанные методы
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.decipherimage">
<refnamediv>
<refname>Imagick::decipherImage</refname>
<refpurpose>Deciphers an image</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<modifier>public</modifier> <type>bool</type><methodname>Imagick::decipherImage</methodname>
<methodparam><type>string</type><parameter>passphrase</parameter></methodparam>
</methodsynopsis>
<para>
Deciphers image that has been enciphered before. The image must be enciphered
using <function>Imagick::encipherImage</function>.
&imagick.method.available.0x639;
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<para>
<variablelist>
<varlistentry>
<term><parameter>passphrase</parameter></term>
<listitem>
<para>
The passphrase
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
&imagick.return.success;
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>Imagick::encipherImage</function></member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->
Восстановить отдельный заранее подготовленный cipher-вход
Короткий decipher работает с настоящим encoded-rgb8.png, созданным заранее той же версией и публичной фразой. Он не повторяет encipher в этой программе. Рабочая копия восстановилась точно, fixture и эталон сохранены.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=new Imagick(__DIR__.'/encoded-rgb8.png');
$cipherBefore=icHash($cipher);
$restored=clone $cipher;
$ok=$restored->decipherImage($phrase);
icDecipherProof($source,$cipher,$restored,$ok,$before,$cipherBefore);
echo "image-cipher-decipher.png\ndecipher-input8.png\nmetrics.json\ndecipher=true restored-exact=1 cipher-input-unchanged=1 source-unchanged=1\n";
Короткий decipher работает с настоящим encoded-rgb8.png, созданным заранее той же версией и публичной фразой. Он не повторяет encipher в этой программе. Рабочая копия восстановилась точно, fixture и эталон сохранены. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-decipher.png
decipher-input8.png
metrics.json
decipher=true restored-exact=1 cipher-input-unchanged=1 source-unchanged=1
Найти причину невосстановления: фраза, JPEG, глубина или неподходящая проверка
Правильный lossless-вход восстановился; неверная фраза, JPEG и потеря глубины дали true без правильного результата. PNG16 сохранил 16-битный cipher и восстановил источник. Собственная политика отклонила пустую/NUL-фразу, а API отдельно продемонстрировал принятие пустой строки. Две ошибки пустого объекта пойманы.
<?php
declare(strict_types=1);
require __DIR__.'/image-cipher-fixture.php';
icRestore();
$source=icSource(); $before=icHash($source);
$phrase=IC_DEMO_PHRASE;
$cipher=new Imagick(__DIR__.'/encoded-rgb8.png');$cipherBefore=icHash($cipher);
$good=clone $cipher;$goodReturn=$good->decipherImage($phrase);
$wrong=clone $cipher;$wrongReturn=$wrong->decipherImage('different-public-demo-phrase');
$jpeg=clone $cipher;$jpeg->setImageFormat('jpeg');$jpeg->setImageCompressionQuality(90);
$jpeg->writeImage(__DIR__.'/encoded-lossy.jpg');
$jpeg=new Imagick(__DIR__.'/encoded-lossy.jpg');$jpegReturn=$jpeg->decipherImage($phrase);
$source16=clone $source;$source16->setImageDepth(16);
$cipher16=clone $source16;$enc16=$cipher16->encipherImage($phrase);
icSavePNG($cipher16,'workflow-cipher16.png',16);
icSavePNG($cipher16,'workflow-cipher16-lost8.png',8);
$read16=new Imagick(__DIR__.'/workflow-cipher16.png');$saved16Hash=icHash($read16);
$readLost8=new Imagick(__DIR__.'/workflow-cipher16-lost8.png');
$dec16=$read16->decipherImage($phrase);$decLost8=$readLost8->decipherImage($phrase);
$stripped=clone $cipher;$stripped->stripImage();$strippedMetadata=icMetadata($stripped);
$stripReturn=$stripped->decipherImage($phrase);
$emptyPhrase=clone $source;$emptyEnc=$emptyPhrase->encipherImage('');$emptyHash=icHash($emptyPhrase);
$emptyDec=$emptyPhrase->decipherImage('');
$policyRejected=[];
foreach(['',"demo\0suffix"] as $badPhrase){
try{icPhrase($badPhrase);$policyRejected[]=false;}catch(InvalidArgumentException $error){$policyRejected[]=true;}
}
$acceptedPhrase=icPhrase($phrase);
$again=clone $source;$againReturn=$again->encipherImage($acceptedPhrase);
$errors=[];
foreach(['encipher_empty'=>fn()=>(new Imagick())->encipherImage($phrase),
'decipher_empty'=>fn()=>(new Imagick())->decipherImage($phrase)] as $name=>$call){
try{$call();$errors[$name]=['class'=>'not caught','message'=>''];}
catch(Throwable $error){$errors[$name]=['class'=>get_class($error),'message'=>$error->getMessage()];}
}
icView($good,'workflow-correct8.png');icView($wrong,'workflow-wrong8.png');
icView($jpeg,'workflow-jpeg8.png');icView($readLost8,'workflow-depth-loss8.png');
icView($read16,'workflow-png16-restored8.png');
$rects=icCompare([$good,$wrong,$jpeg,$readLost8],'image-cipher-workflow.png');
$wrongError=icError($source,$wrong);$jpegError=icError($source,$jpeg);$depthError=icError($source,$readLost8);
icDiagnostic('image-cipher-workflow',['executed_apis'=>['encipherImage','decipherImage'],
'correct'=>['return'=>$goodReturn,'restored_exact'=>icHash($good)===$before,'error'=>icError($source,$good)],
'wrong_phrase'=>['return'=>$wrongReturn,'restored_exact'=>icHash($wrong)===$before,'error'=>$wrongError],
'jpeg90'=>['return'=>$jpegReturn,'restored_exact'=>icHash($jpeg)===$before,'error'=>$jpegError],
'depth16'=>['encipher_return'=>$enc16,'cipher_RGB16_sha256'=>icHash($cipher16),'saved_RGB16_sha256'=>$saved16Hash,
'decipher16_return'=>$dec16,'restored16_exact'=>icHash($read16)===$before,'error16'=>icError($source,$read16),
'forced8_return'=>$decLost8,'forced8_restored_exact'=>icHash($readLost8)===$before,'forced8_error'=>$depthError],
'strip'=>['properties_before_decode'=>$strippedMetadata,'return'=>$stripReturn,'restored_exact'=>icHash($stripped)===$before],
'empty_phrase'=>['encipher_return'=>$emptyEnc,'cipher_changed'=>$emptyHash!==$before,'decipher_return'=>$emptyDec,'restored_exact'=>icHash($emptyPhrase)===$before],
'application_policy_rejected'=>$policyRejected,'deterministic_cipher'=>icHash($again)===$cipherBefore,
'errors'=>$errors,'public_metadata'=>icMetadata($cipher),'source_unchanged'=>icHash($source)===$before,'cipher_input_unchanged'=>icHash($cipher)===$cipherBefore,'panel_geometry'=>$rects,
'checks'=>['correct_returns_true_and_restores'=>$goodReturn===true&&icHash($good)===$before,
'wrong_phrase_true_is_not_authentication'=>$wrongReturn===true&&$wrongError['max']>0,
'JPEG_true_does_not_restore'=>$jpegReturn===true&&$jpegError['max']>0,
'PNG16_preserves_cipher_and_restores'=>$enc16===true&&$saved16Hash===icHash($cipher16)&&$dec16===true&&icHash($read16)===$before,
'forced_PNG8_loses_16bit_cipher'=>$decLost8===true&&$depthError['max']>0,
'strip_is_not_remove_all_properties'=>$strippedMetadata['comment']===false&&$strippedMetadata['ticket']==='PUBLIC-42'&&$strippedMetadata['cipher_mode']==='CTR',
'stripped_pixels_still_restore'=>$stripReturn===true&&icHash($stripped)===$before,
'API_accepts_empty_phrase'=>$emptyEnc===true&&$emptyHash!==$before&&$emptyDec===true&&icHash($emptyPhrase)===$before,
'own_policy_rejects_empty_and_NUL'=>$policyRejected===[true,true],
'same_phrase_reuses_cipher'=>$againReturn===true&&icHash($again)===$cipherBefore,
'two_empty_object_errors'=>array_column($errors,'class')===['ImagickException','ImagickException'],
'originals_preserved'=>icHash($source)===$before&&icHash($cipher)===$cipherBefore]]);
echo "image-cipher-workflow.png\nworkflow-correct8.png\nworkflow-wrong8.png\nworkflow-jpeg8.png\nworkflow-depth-loss8.png\nworkflow-png16-restored8.png\nworkflow-cipher16.png\nworkflow-cipher16-lost8.png\nencoded-lossy.jpg\nmetrics.json\ncorrect=exact wrong=true-but-invalid JPEG=true-but-invalid PNG16=exact forced8=invalid policy-rejections=2 errors=2\n";
Правильный lossless-вход восстановился; неверная фраза, JPEG и потеря глубины дали true без правильного результата. PNG16 сохранил 16-битный cipher и восстановил источник. Собственная политика отклонила пустую/NUL-фразу, а API отдельно продемонстрировал принятие пустой строки. Две ошибки пустого объекта пойманы. Самостоятельный архив: example.php, два точных PNG-входа в base64 и image-cipher-fixture.php. Сохраните файлы рядом и выполните php example.php. Проверено PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 без HDRI с Cipher, PNG и JPEG. Фраза public-demo-passphrase-2026-only публична и служит только воспроизводимой демонстрации. icRestore восстанавливает входы с проверкой SHA, icSource открывает подготовленный непрозрачный RGB8 и добавляет только публичные свойства. Вызовы изучаемых методов находятся в основном коде. icSavePNG пишет настоящий файл данных, icView — отдельный PNG8-просмотр; обзорные просмотры не используются для decipher.
Ожидаемый вывод проверенной программы
image-cipher-workflow.png
workflow-correct8.png
workflow-wrong8.png
workflow-jpeg8.png
workflow-depth-loss8.png
workflow-png16-restored8.png
workflow-cipher16.png
workflow-cipher16-lost8.png
encoded-lossy.jpg
metrics.json
correct=exact wrong=true-but-invalid JPEG=true-but-invalid PNG16=exact forced8=invalid policy-rejections=2 errors=2
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


