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

Дерево руководства
Глава 91 · С нуля → полный справочник
Случайные данные: полный справочник Random
От выбора одной грани до секретных байтов, воспроизводимого состояния, float-интервалов и всех функций, методов, интерфейсов и отказов Random.
Зачем нужна случайность: первый вызов без объектов
Задача определяет источник случайности
Повторяемая симуляция и секретный токен требуют разных свойств генератора.
- 1Выберите задачу↓
Тест должен повторяться; токен должен быть непредсказуемым.
- 2Engine↓
Secure для криптографической случайности; подходящий seedable engine для воспроизводимых экспериментов.
- 3Randomizer↓
Получает целые, байты, значения диапазона или перестановки по конкретному контракту.
- 4Проверка приложения
Случайность не гарантирует уникальность. Ограничения базы и формат токена проверяются отдельно.
Фиксированный seed помогает тесту, но делает последовательность предсказуемой. Такой генератор нельзя переносить в секретные токены.
Иногда программа должна выбрать вариант, который мы заранее не назначили: грань кубика, порядок учебных вопросов или байты одноразового секрета. Случайный результат может повториться. «Случайный» не означает «новый для базы», а одинаковый шанс каждого допустимого результата не означает идеально ровные частоты в короткой серии.
Начнём без собственных функций и объектов. random_int(1, 6) просит целое от 1 до 6 включительно. Знак $ начинает имя переменной, = сохраняет ответ в ней, круглые скобки содержат два аргумента, запятая разделяет их, ; завершает команду. Следующие две строки проверяют границы, чтобы ожидаемый учебный вывод не зависел от выпавшей грани.
<?php
declare(strict_types=1);
$face = random_int(1, 6);
$inside = $face >= 1 && $face <= 6;
echo $inside ? "Грань находится в 1…6\n" : "Ошибка диапазона\n";
Ожидаемый результат
Грань находится в 1…6
>= и <= сравнивают числа, && требует обе проверки сразу. Выражение условие ? текст1 : текст2 выбирает текст, echo выводит его, завершает строку. Чтобы увидеть саму грань, после примера можно отдельно написать echo $face; её конкретное значение заранее неизвестно. Проверка диапазона не является статистическим тестом качества генератора.
Вызов random_int возвращает int, а random_bytes — бинарную строку заданной длины. Бинарная строка не обязана быть UTF-8. bin2hex показывает каждый байт двумя ASCII-символами: 16 байт станут 32 символами, но новых случайных битов это преобразование не добавляет.
<?php
declare(strict_types=1);
$bytes = random_bytes(16);
$text = bin2hex($bytes);
echo strlen($bytes), " bytes\n";
echo strlen($text), " hex characters\n";
Ожидаемый результат
16 bytes
32 hex characters
strlen считает байты. В этом опыте измеряется результат; сами секретные байты не печатаются. Размер настоящего токена выбирают по требованиям протокола. Генерация не задаёт срок действия, хранение, одноразовость или проверку прав.
Эти две функции доступны с PHP 7.0; классы Random добавлены в 8.2, некоторые операции — в 8.3. Дальше будем вводить объектный синтаксис отдельно. При необходимости повторите вызов и параметры и массив значений. Все run-примеры данной партии проверены на PHP 8.5.11 x64 с проектным php.ini; системная случайность проверяется через устойчивые свойства результата, не через заранее угаданные байты.
Выбрать свойства результата до выбора API
Выбор начинается с требования приложения. Для секрета нужна непредсказуемость для атакующего; для повторяемого теста — возможность получить тот же поток. PRNG разворачивает начальное состояние в последовательность. CSPRNG — источник, пригодный для криптографических задач; Secure использует CSPRNG операционной системы. Очень длинный период PRNG не заменяет это свойство.
| Потребность | Отправная точка |
|---|---|
| Секретный токен / случайное целое для защитного протокола | random_bytes / random_int либо Randomizer с Secure. Не подменять отказ на rand/время. |
| Перемешать вопросы или выбрать элементы без требования повторяемости | Random\Randomizer по умолчанию использует Secure; сначала выбираем простое корректное API. |
| Воспроизвести несекретный тест | Random\Engine\Mt19937, Random\Engine\PcgOneseq128XslRr64 или Random\Engine\Xoshiro256StarStar с явным seed. Алгоритм и порядок запросов фиксируются. |
| Высокая производительность моделирования | Сначала измерить собственную задачу; использовать подходящий PRNG осознанно. Универсального обещания скорости здесь нет. |
| Уникальный ключ записи | Случайность уменьшает вероятность коллизии, но ограничение UNIQUE и правило повторной генерации обеспечиваются хранилищем. |
| Равные целочисленные денежные шаги | Random\Randomizer::getInt для целого количества копеек, затем форматирование. Для настоящего float — Random\Randomizer::getFloat. |
Не делайте остаток % от случайного байта для произвольного диапазона. У 256 возможных байтов нет равного числа представителей для каждой из шести граней: остатки 0…3 встречаются по 43 раза, 4…5 — по 42. random_int/getInt выбирают диапазон с нужным договором, отбрасывая неподходящие значения при необходимости.
- Есть 256 исходных вариантов 0…255
- 256 = 42×6 + 4
- Первые четыре остатка получают лишний вариант
- Для честной грани вызываем random_int(1, 6)
<?php
declare(strict_types=1);
$counts = array_fill(0, 6, 0);
for ($byte = 0; $byte < 256; ++$byte) { ++$counts[$byte % 6]; }
echo implode(', ', $counts), "\n";
Ожидаемый результат
43, 43, 43, 43, 42, 42
array_fill создаёт шесть нулевых счётчиков, цикл перебирает все возможные байты, % получает остаток, ++ увеличивает соответствующий счётчик. Это точный перебор распределения, а не наблюдение случайной выборки. При независимом равномерном источнике различие видно без догадок о seed.
Randomizer: источник отдельно, нужная операция отдельно
Randomizer — final-класс высокоуровневых операций. Engine отвечает за байты, Randomizer превращает их в целое, float, выбранные ключи или перестановку. new создаёт объект; -> вызывает его метод. Префикс Random\ в имени — пространство имён, а не путь к файлу. В нашем самостоятельном скрипте имя записано полностью; внутри собственного namespace используйте ведущий \ либо use Random\Randomizer.
| Запись | Что она означает |
|---|---|
| $r = new Random\Randomizer(); | Создать объект, сохранить его в переменной; пустой список аргументов выбирает Secure. |
| $r->getInt(10, 20) | Вызвать метод этого объекта; получить целое с обеими границами включительно. |
| $r->engine | Прочитать public readonly Engine: какой источник использует объект. Саму ссылку заменить нельзя; состояние источника может изменяться. |
| final | Нельзя наследовать Randomizer; для подмены источника передают собственный Engine, а не наследника Randomizer. |
- new Randomizer выбирает Secure
- getInt получает от источника байты
- Алгоритм сопоставляет им целое 10…20
- В приложении проверяем свойство результата
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$quantity = $r->getInt(10, 20);
echo $quantity >= 10 && $quantity <= 20 ? "Количество в 10…20\n" : "Ошибка\n";
echo $r->engine instanceof Random\Engine\Secure ? "Источник: Secure\n" : "Иной источник\n";
Ожидаемый результат
Количество в 10…20
Источник: Secure
instanceof проверяет класс/интерфейс объекта. getInt не требует вручную превращать байты в число. Свойство engine доступно для осознанной проверки выбранной зависимости; оно не раскрывает и не устанавливает seed Secure.
Для нескольких связанных операций используйте один явно выбранный объект. Его двенадцать методов рассмотрены в карточках: Random\Randomizer::__construct, Random\Randomizer::getInt, Random\Randomizer::getBytes, Random\Randomizer::getBytesFromString, Random\Randomizer::getFloat, Random\Randomizer::nextFloat, Random\Randomizer::nextInt, Random\Randomizer::pickArrayKeys, Random\Randomizer::shuffleArray, Random\Randomizer::shuffleBytes, Random\Randomizer::__serialize, Random\Randomizer::__unserialize. Сам Randomizer не клонируется; отдельные воспроизводимые потоки создают с отдельными engine или поддерживаемым clone engine. Подробный объектный синтаксис — класс и экземпляр.
Seed, состояние, общий объект и воспроизведение
Seed — начальная точка; state — текущее состояние после сделанных шагов. Для воспроизведения мало помнить seed: нужны алгоритм, его режим, версия/платформа при значимых различиях и тот же порядок операций. Один вызов getInt может потребовать несколько слов engine из-за отбрасывания неподходящих значений; один getBytes(4) и четыре getBytes(1) не обязаны потребить состояние одинаково.
- Два Mt19937 получают seed 41
- Первое getInt совпадает
- Один объект делает лишний запрос
- Его состояние отличается от второго
<?php
declare(strict_types=1);
$left = new Random\Randomizer(new Random\Engine\Mt19937(41));
$right = new Random\Randomizer(new Random\Engine\Mt19937(41));
echo $left->getInt(1, 6) === $right->getInt(1, 6) ? "Первый шаг совпал\n" : "Ошибка\n";
$left->getBytes(4);
echo $left->engine->__serialize() !== $right->engine->__serialize() ? "Теперь состояния разные\n" : "Одна точка\n";
Ожидаемый результат
Первый шаг совпал
Теперь состояния разные
Сравниваются снимки состояния, а не следующие значения: разные состояния тоже могут выдать одно и то же маленькое число. Скобки new Engine внутри new Randomizer передают созданный объект как аргумент. Снимки остаются локальными в памяти.
clone native engine копирует текущую точку. Передать один и тот же engine двум Randomizer — разделить одну изменяемую точку; создание второй оболочки не создаёт вторую последовательность. Для сохранения используйте доверенный собственный снимок и записанные параметры; не десериализуйте вход посетителя. Длительный период — количество шагов до повторения состояния, а пространство seed — число разных стартов; эти величины могут очень различаться.
Engine: низкоуровневый договор байтов
Engine — интерфейс с одним публичным методом generate(): string. Он задаёт форму зависимости; интерфейс сам не создаёт объект. implements означает «класс обязуется предоставить этот метод», public делает метод доступным вызывающему коду, : string объявляет тип результата. У генератора один вызов выдаёт непустое слово случайных байт и продвигает состояние. Встроенные engine рассмотрены ниже; собственный алгоритм не стоит изобретать для секретов.
- Engine.generate возвращает байты
- Randomizer потребляет до 8 байт слова
- getInt/getBytes/... выполняет нужную операцию
- При неисправном источнике обработчик получает ошибку
Короткие слова могут объединяться; байты после первых восьми в одном слове Randomizer игнорирует. Числовые слова кодируются unsigned little-endian: младший байт первым. Все биты должны быть равномерными и независимыми по договору качества; сигнатура PHP не может это доказать. Конкретный конечный fixture, байтовый порядок и отказ зависимости разобраны в Random\Engine::generate. До собственного класса повторите implements и интерфейс.
CryptoSafeEngine: смысл и пределы маркера
CryptoSafeEngine — marker interface: он extends Engine, наследует generate и не добавляет метода. Маркер сообщает намерение реализации выдавать криптографически стойкую случайность. Он не проводит испытание алгоритма: пользовательский класс может ошибочно заявить этот интерфейс. Для встроенного проверенного пути выбирайте Secure; наличие instanceof у произвольной зависимости — не аудит её безопасности.
- Принимаем CryptoSafeEngine
- Передаём встроенный Secure
- Randomizer получает ровно 16 байт
- Выводим только число байт
<?php
declare(strict_types=1);
function secretMaterial(Random\CryptoSafeEngine $engine): string
{
return (new Random\Randomizer($engine))->getBytes(16);
}
echo strlen(secretMaterial(new Random\Engine\Secure())), " bytes\n";
try { secretMaterial(new Random\Engine\Mt19937(41)); }
catch (TypeError) { echo "Несекретный PRNG не принят по типу\n"; }
Ожидаемый результат
16 bytes
Несекретный PRNG не принят по типу
Тип аргумента ограничивает вызывающий код, return возвращает результат, catch перехватывает TypeError. Встроенный Mt19937 реализует Engine, но не CryptoSafeEngine. Этот пример не доказывает качество произвольного пользовательского класса с тем же маркером.
Договор и отличие от Random\Engine появились в PHP 8.2. Подробное применение типов параметров — параметры функции; не используйте instanceof как разрешение доверять неизвестному плагину с собственной реализацией криптографии.
Secure и системные источники: что гарантирует платформа
Secure — final engine системной криптографической случайности, реализующий CryptoSafeEngine. Создаётся new Random\Engine\Secure() без seed; его generate возвращает PHP_INT_SIZE байт. Этот источник подходит в том числе для долгоживущих секретов, но формат/длину ключа по-прежнему задаёт криптографический протокол. Получить повторяемый поток по своему seed, клонировать или сериализовать Secure нельзя.
- Secure выбирает системный источник
- Randomizer просит заданную длину
- Длина проверяется без вывода содержимого
- При отказе генерации операция не продолжается с rand
Ниже порядок источников из официального договора random_bytes/random_int. Переход к следующему источнику относится к внутренней платформенной реализации PHP; это не рекомендация приложению читать /dev/urandom самостоятельно или создавать запасной псевдогенератор.
| ОС и версия PHP | Источники в порядке приоритета |
|---|---|
| Linux | getrandom() → /dev/urandom |
| FreeBSD ≥12, PHP ≥7.3 | getrandom() → /dev/urandom |
| Windows, PHP ≥7.2 | CNG API; более старый PHP использовал CryptGenRandom |
| macOS, PHP ≥8.2 / ≥8.1.9 / ≥8.0.22 | CCRandomGenerateBytes(), если доступен при компиляции |
| macOS, PHP ≥8.1 / ≥8.0.2 | arc4random_buf() → /dev/urandom |
| NetBSD ≥7 или OpenBSD ≥5.5; PHP ≥7.1 / ≥7.0.1 | arc4random_buf() → /dev/urandom |
| DragonFly BSD / Solaris, PHP ≥8.1 | getrandom() → /dev/urandom |
| Другие перечисленным правилам не соответствующие сочетания | /dev/urandom |
Если доступные источники не дали случайность, Secure и современные random_* сообщают Random\RandomException. У random_bytes/random_int до PHP 8.2 это был обычный Exception. Наличие источников других ОС в таблице подтверждено первичным договором, а не запуском этих ОС в данной проверке. Примеры Secure выполнены только на Windows x64.
Практика длины и неизменяемого источника: Random\Engine\Secure::generate, Random\Randomizer::getBytes, random_bytes. Расширение random входит в PHP; отдельных настроек INI эта книга не задаёт. Современные классы доступны с 8.2, getFloat/nextFloat/IntervalBoundary/getBytesFromString — с 8.3.
Mt19937: назначение и состояние
Этот final-класс позволяет воспроизвести старый тест на Mersenne Twister с отдельным состоянием. MT19937 хранит большой массив состояния и выдаёт 32-битные слова. Период 2^19937−1 не делает его криптографическим и не увеличивает 32-битное пространство начальных seed.
- new Mt19937(41) создаёт учебный поток
- generate выдаёт 4 байта и продвигает его
- clone копирует текущую точку для сравнения
- В реальной операции Randomizer задаёт нужный диапазон/форму
Класс реализует Engine, но не CryptoSafeEngine. Повторяемость полезна в тестах и моделировании; скрывать seed недостаточно, чтобы сделать этот алгоритм безопасным для токенов. Передача одного объекта нескольким потребителям разделит состояние. Клонирование поддерживается и создаёт копию текущего состояния, а не новую независимую случайную последовательность.
Начните с Random\Engine\Mt19937::__construct, затем прочитайте Random\Engine\Mt19937::generate. Для паузы/возобновления опыта есть Random\Engine\Mt19937::__serialize и Random\Engine\Mt19937::__unserialize; для диагностики — Random\Engine\Mt19937::__debugInfo.
PcgOneseq128XslRr64: назначение и состояние
Этот final-класс позволяет повторять численный опыт с 128-битным состоянием и быстро пропускать шаги. PCG Oneseq хранит одно 128-битное состояние, применяет преобразования XSL/RR и выдаёт 64 бита. Oneseq означает фиксированный выбор последовательности алгоритма; seed задаёт её исходное состояние. Это PRNG, не источник секретов.
- new PcgOneseq128XslRr64(41) создаёт учебный поток
- generate выдаёт 8 байта и продвигает его
- clone копирует текущую точку для сравнения
- В реальной операции Randomizer задаёт нужный диапазон/форму
Класс реализует Engine, но не CryptoSafeEngine. Повторяемость полезна в тестах и моделировании; скрывать seed недостаточно, чтобы сделать этот алгоритм безопасным для токенов. Передача одного объекта нескольким потребителям разделит состояние. Клонирование поддерживается и создаёт копию текущего состояния, а не новую независимую случайную последовательность.
Начните с Random\Engine\PcgOneseq128XslRr64::__construct, затем прочитайте Random\Engine\PcgOneseq128XslRr64::generate. Для паузы/возобновления опыта есть Random\Engine\PcgOneseq128XslRr64::__serialize и Random\Engine\PcgOneseq128XslRr64::__unserialize; для диагностики — Random\Engine\PcgOneseq128XslRr64::__debugInfo.
Xoshiro256StarStar: назначение и состояние
Этот final-класс позволяет делить воспроизводимый опыт на неперекрывающиеся ограниченные потоки через jump. xoshiro256** хранит четыре 64-битных слова, вместе 256 бит, и выдаёт 64-битные результаты. Нулевое состояние целиком запрещено. Большое состояние и функции jump не означают криптографическую стойкость.
- new Xoshiro256StarStar(41) создаёт учебный поток
- generate выдаёт 8 байта и продвигает его
- clone копирует текущую точку для сравнения
- В реальной операции Randomizer задаёт нужный диапазон/форму
Класс реализует Engine, но не CryptoSafeEngine. Повторяемость полезна в тестах и моделировании; скрывать seed недостаточно, чтобы сделать этот алгоритм безопасным для токенов. Передача одного объекта нескольким потребителям разделит состояние. Клонирование поддерживается и создаёт копию текущего состояния, а не новую независимую случайную последовательность.
Начните с Random\Engine\Xoshiro256StarStar::__construct, затем прочитайте Random\Engine\Xoshiro256StarStar::generate. Для паузы/возобновления опыта есть Random\Engine\Xoshiro256StarStar::__serialize и Random\Engine\Xoshiro256StarStar::__unserialize; для диагностики — Random\Engine\Xoshiro256StarStar::__debugInfo.
Включить или исключить края: IntervalBoundary
Обычный числовой диапазон не объясняет, допустимы ли его крайние значения. IntervalBoundary — enum с четырьмя именованными вариантами, а не строка или число. Двойное двоеточие выбирает case: Random\IntervalBoundary::ClosedOpen. Передайте этот объект третьим аргументом getFloat.
- ClosedOpen: нижний да, верхний нет
- ClosedClosed: оба да
- OpenClosed: нижний нет, верхний да
- OpenOpen: оба нет
| Case / запись | Условия для результата |
|---|---|
| ClosedOpen / [a,b) | a≤x<b; стандартный режим getFloat. |
| ClosedClosed / [a,b] | a≤x≤b; a=b допускает один вариант. |
| OpenClosed / (a,b] | a<x≤b. |
| OpenOpen / (a,b) | a<x<b; даже a<b может не оставить представимого float. |
<?php
declare(strict_types=1);
foreach (Random\IntervalBoundary::cases() as $mode) { echo $mode->name, "\n"; }
Ожидаемый результат
ClosedOpen
ClosedClosed
OpenClosed
OpenOpen
cases — стандартный метод enum, name — имя case. Это pure enum без backed value: передавать строку "OpenOpen" вместо case нельзя.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$ratio = $r->getFloat(0.0, 1.0, Random\IntervalBoundary::OpenOpen);
echo $ratio > 0 && $ratio < 1 ? "Строго внутри интервала\n" : "Ошибка\n";
var_dump($r->getFloat(0.75, 0.75, Random\IntervalBoundary::ClosedClosed));
Ожидаемый результат
Строго внутри интервала
float(0.75)
Первое число может быть входом формулы, которой запрещены оба края; сама формула здесь не вводится. Второй вызов показывает единственный допустимый результат замкнутого точечного интервала.
Почему дробный диапазон нельзя просто растянуть
В учебном генераторе весов хочется одинакового шанса для равномерно расположенных значений. Однако double — сетка, которая становится реже при увеличении абсолютного значения. Поэтому одинаковый шаг по битовым представлениям и одинаковый шаг по числовой прямой — разные требования.
- Воображаемый формат: шаг 0.25 между 1 и 2
- Между 2 и 4 шаг уже 0.5
- Для [1.25,3.0) берём редкий шаг 0.5 от края 3.0
- Кандидаты: 2.5,2.0,1.5 и обязательный закрытый край 1.25
| Шаг модели | Почему так |
|---|---|
| Старт от 3.0 | Это край с большей абсолютной величиной; его шаг 0.5 гарантирует точно представимые основные точки. |
| 3.0 исключён | Верхняя скобка круглая; первый основной кандидат 2.5. |
| 2.5 → 2.0 → 1.5 | Основные точки равноотстоят. Другие точно представимые значения вроде 1.75 не входят в выбранную сетку. |
| 1.25 включён | Нижний край закрыт, поэтому включается даже при последнем промежутке 0.25 вместо 0.5. |
| Выбор | Четыре кандидата выбираются с равным шансом; это не все вещественные числа интервала. |
У PHP обычная double имеет 52 явно хранимых дробных бита и 53 бита значащей точности, а не два дробных бита этой модели. γ-section выбирает шаг по менее плотному краю и сохраняет закрытые границы. Пример выше объясняет правило, но не эмулирует PHP и не предлагает собственный генератор.
Если сначала выбрать число на [0,1), затем умножить и прибавить границу, промежуточные значения округлятся. Несколько исходных вариантов могут попасть в один float, а округление может выдать исключённый край. Поэтому интервал передают getFloat напрямую. После этого округлять для вывода можно явно; при требовании «ровно сотые» лучше изначально выбрать целое количество сотых через getInt.
Оба MT-режима и их версии
В этой книге две предопределённые integer-константы режима Mersenne Twister. Они всегда доступны как часть PHP core. Это взаимоисключающие режимы, не битовая маска: не складывайте их и не объединяйте |. Символьное имя читается без кавычек; defined принимает имя строкой, когда проверяется наличие возможности.
| Константа | Договор |
|---|---|
| MT_RAND_MT19937 | Правильный MT19937 для конструктора engine и mt_srand. Современный режим по умолчанию. |
| MT_RAND_PHP | Старый ошибочный алгоритм совместимости с mt_srand до PHP 7.1. Сам режим deprecated с 8.3; в PHP 8.5 чтение устаревшей константы также диагностируется. Для новой задачи не выбирать. |
- Mt19937 получает seed 41
- Указан MT_RAND_MT19937
- Проверяем ожидаемые 4 байта слова
<?php
declare(strict_types=1);
$engine = new Random\Engine\Mt19937(41, MT_RAND_MT19937);
echo strlen($engine->generate()), " bytes\n";
echo defined('MT_RAND_MT19937') ? "Режим доступен\n" : "Нет режима\n";
Ожидаемый результат
4 bytes
Режим доступен
Выбор режима не превращает MT в CSPRNG. При чтении старого кода сначала выясняют, нужна ли совместимость существующей серии; новые секреты получают от Secure/random_bytes.
Отдельные значения для float не являются MT-режимами: Random\IntervalBoundary — enum с четырьмя case. Версии procedural-функций, псевдонимы и изменение seed описаны в mt_srand и srand.
Три класса отказов и унаследованная диагностика
При отказе генерации нельзя молча выдавать предсказуемую строку. Разделим неверный аргумент, неисправную реализацию engine и отказ источника. catch (Exception) не ловит наследников Error; обе ветви объединяет Throwable. Ловить Throwable имеет смысл на границе операции для понятного отказа и журнала, а не для продолжения с небезопасной заменой.
| Тип | Иерархия и значение |
|---|---|
| Random\RandomError | Наследник Error: базовый класс ошибок получения/использования случайности; implements Throwable через Error. |
| Random\BrokenRandomEngineError | Наследник RandomError: engine нарушает нужный договор, например вернул пустую строку или крайне смещённые данные. |
| Random\RandomException | Наследник Exception: базовое исключение случайности, в том числе отказ CSPRNG. |
| ValueError / TypeError | Отдельные стандартные ошибки аргументов: например length=0 либо неверный тип. Они не наследуют RandomError/RandomException. |
- Engine.generate возвращает пустую строку
- Randomizer не выдаёт байты пользователю
- BrokenRandomEngineError перехвачен
- Операция завершается без запасного токена
<?php
declare(strict_types=1);
final class EmptyFixture implements Random\Engine
{
public function generate(): string { return ''; }
}
try { (new Random\Randomizer(new EmptyFixture()))->getBytes(1); }
catch (Random\BrokenRandomEngineError $error) {
echo $error instanceof Random\RandomError ? "Ветка RandomError\n" : "Иная ветка\n";
echo $error instanceof Error ? "Ветка Error\n" : "Иная ветка\n";
echo "Операция остановлена\n";
}
Ожидаемый результат
Ветка RandomError
Ветка Error
Операция остановлена
Fixture намеренно нарушает минимальный договор и не ходит ни в сеть, ни в систему энтропии. При более тонком смещении ошибка может не появиться: Randomizer не является тестом качества всех возможных engine.
Все три класса появились в 8.2. Унаследованный конструктор принимает message="", code=0, previous=null. Общие методы: getMessage, getCode, getPrevious, getFile, getLine, getTrace, getTraceAsString и __toString. Они читают диагностику; не дают возможность заново получить правильные случайные байты. Клонирование исключений/ошибок закрыто приватным __clone.
| Унаследованное состояние | Как пользоваться |
|---|---|
| message, code, file, line | Защищённые поля; читать через методы. Code — код исключения приложения, не обязательно системный errno. |
| string, trace, previous | Приватное служебное представление, стек и причинная цепочка; использовать публичные методы. Не разбирать приватные поля и не публиковать трассу с секретами. |
<?php
declare(strict_types=1);
$cause = new RuntimeException('Учебный источник недоступен');
$error = new Random\RandomException('Не удалось подготовить материал', 17, $cause);
echo $error->getMessage(), "\n";
echo $error->getCode(), "\n";
echo $error->getPrevious() === $cause ? "Причина сохранена\n" : "Причины нет\n";
echo $error instanceof Exception ? "Ветка Exception\n" : "Иная ветка\n";
Ожидаемый результат
Не удалось подготовить материал
17
Причина сохранена
Ветка Exception
Отказ ОС здесь не провоцируется: мы самостоятельно создаём диагностический объект. Сообщение безопасно и не содержит токенов. Реальное оборачивание сбоя зависимости показано в Engine::generate. Имена классов в catch пишутся полностью или импортируются через use.
Подробно try/catch и Throwable объясняются в основах обработки отказа. Не выдавайте getTraceAsString посетителю; отдельно продумайте безопасные поля журнала и ответ об отказе операции.
Полный пример: подготовить учебный вариант
После отдельных вызовов соберём учебный набор вопросов. Массив хранит код → текст; randomizer выбирает две разные позиции, тексты извлекаются по этим ключам. Для варианта набора нужен ASCII-код из явно указанного алфавита. Ни выбранные значения, ни код не объявляются уникальными между запусками.
- Каталог из Q1/Q2/Q3
- pickArrayKeys выбирает два различных ключа
- По ключам получаем два русских текста
- shuffleArray переставляет значения, не байты текста
- Создаём 8-символьный код из A/B/C/2/3/4
<?php
declare(strict_types=1);
$catalog = ['Q1' => 'Имя переменной', 'Q2' => 'Вызов функции', 'Q3' => 'Ключ массива'];
$r = new Random\Randomizer();
$keys = $r->pickArrayKeys($catalog, 2);
$selected = [];
foreach ($keys as $key) { $selected[] = $catalog[$key]; }
$ordered = $r->shuffleArray($selected);
$variant = $r->getBytesFromString('ABC234', 8);
echo count($keys) === 2 && count(array_unique($keys)) === 2 ? "Два разных вопроса\n" : "Ошибка\n";
echo count($ordered) === 2 && count(array_diff($ordered, $selected)) === 0 ? "Тексты сохранены\n" : "Ошибка\n";
echo strlen($variant) === 8 && strspn($variant, 'ABC234') === 8 ? "Код соответствует алфавиту\n" : "Ошибка\n";
Ожидаемый результат
Два разных вопроса
Тексты сохранены
Код соответствует алфавиту
foreach читает выбранные ключи, [] добавляет значение в конец нового массива. Переставляются целые строки: UTF-8 внутри них сохраняется. array_unique проверяет отсутствие повторов ключей, array_diff — что нет посторонних значений; для этого каталога тексты различны. Код варианта здесь не является паролем или долговременным уникальным идентификатором.
Упражнение 1. Нужны три разных вопроса вместо двух. Измените num на 3, обе проверки количества — на 3. Четыре выбрать из трёх нельзя: ожидайте ValueError. Не заменяйте выбор без повторов тремя независимыми getInt: индексы могут совпасть.
Упражнение 2. Для регрессионного теста нужно воспроизводить ту же последовательность. Замените только создание r на new Random\Randomizer(new Random\Engine\Mt19937(41)), сохраняйте каталог, порядок операций и версию; проверьте два самостоятельных запуска. Такой код варианта нельзя потом без пересмотра считать криптографическим секретом.
Код практики использует возможности PHP 8.3+ из-за getBytesFromString. Для Unicode-алфавита храните отдельные символы/графемы элементами массива и выбирайте элементы: Random\Randomizer::shuffleBytes перемешивает байты, а не буквы. По аналогии можно генерировать несекретные тестовые имена, переставлять наборы и выбирать позиции; реальная отправка, БД и сайт в примере отсутствуют.
Все procedural-функции: безопасный API и старый общий поток
Здесь собраны все девять procedural-функций официального ref.random. Старые random_bytes и random_int продолжают открываться по прежним адресам глав; перед их прежней практикой добавлен полный вводный договор. Объектные операции находятся в отдельных карточках Randomizer/Engine выше.
| Задача | Карточки |
|---|---|
| Криптографические байты/целое | random_bytes, random_int |
| Глобальный Mersenne Twister | mt_srand, mt_rand, mt_getrandmax |
| Псевдонимы и совместимость | srand, rand, getrandmax |
| Историческая дробь | lcg_value — deprecated с 8.4; выбирайте современную операцию с явными границами. |
Общий MT-поток влияет также на array_rand, shuffle и str_shuffle в современных PHP; вызов из чужого участка кода может сдвинуть ваш тест. При переходе на Randomizer выбирайте engine явно, если нужна повторяемость: по умолчанию он использует Secure, а не глобальный MT. Версии PHP 7.1/7.2 изменили MT-последовательности; карты функций объясняют совместимость отдельно.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
mt_randПоддержать старый несекретный случайный выбор и понять его общее состояние.
PHP 4+; общий MT с PHP 7.1 · random
mt_rand(): int; mt_rand(int $min, int $max): intПоддержать старый несекретный случайный выбор и понять его общее состояние.
| Часть вызова | Договор |
|---|---|
| Аргументы | Либо ни одного, либо оба: min и max включены. При одном аргументе — ArgumentCountError. |
| Возврат | int; без аргументов от 0 до mt_getrandmax() включительно. |
| Ошибки / версии | max<min: ValueError с PHP 8.0; раньше warning+false. Неверные типы в строгом вызове — TypeError. |
| Состояние | Глобальный MT общ для rand/mt_rand, srand/mt_srand и использующих его array_rand/shuffle/str_shuffle; область функции его не изолирует. |
- Переданы границы 1 и 12
- Получен int из закрытого диапазона
- Отдельный вызов с [4,4] даёт 4
Это старый интерфейс общего Mersenne Twister. Он подходит для воспроизведения несекретных сценариев, но не для токенов. Вызов без аргументов использует 0…mt_getrandmax(); вызов с границами требует сразу два аргумента. Один аргумент не означает «от нуля до него».
<?php
declare(strict_types=1);
$card = mt_rand(1, 12);
echo $card >= 1 && $card <= 12 ? "Карточка входит в набор\n" : "Ошибка\n";
echo mt_rand(4, 4), "\n";
Ожидаемый результат
Карточка входит в набор
4
Это проверка диапазона, не статистический тест качества. Выбранное число может повториться; равномерный выбор с возвращением не является выбором без повторов.
<?php
declare(strict_types=1);
mt_srand(2024);
$first = [mt_rand(1, 12), mt_rand(1, 12), mt_rand(1, 12)];
mt_srand(2024);
$again = [mt_rand(1, 12), mt_rand(1, 12), mt_rand(1, 12)];
echo $first === $again ? "Сценарий повторён\n" : "Другой сценарий\n";
Ожидаемый результат
Сценарий повторён
Повторяемость действует для того же алгоритма, версии и порядка вызовов. Скрытый вызов другой функции общего MT-состояния изменит дальнейшую последовательность. Для нового кода отдельный Randomizer с отдельным engine удобнее.
<?php
declare(strict_types=1);
try { mt_rand(12, 3); }
catch (ValueError) { echo "Диапазон отклонён\n"; }
Ожидаемый результат
Диапазон отклонён
rand сохраняет совместимость с обратными границами, mt_rand в PHP 8 бросает ValueError. Приложение всё равно должно задавать понятный порядок min≤max.
С PHP 7.1 rand использует тот же генератор, что mt_rand; правильный MT19937 заменил прежнюю ошибочную реализацию. PHP 7.2 исправил modulo bias, поэтому старые seeded-последовательности на 64-bit могли измениться. Документированные исторические предостережения о масштабировании за mt_getrandmax не следует превращать в запрет полного int-диапазона современного правильного режима: в PHP 8.5.11 он использует rejection sampling и при необходимости несколько слов генератора. Ошибочный MT_RAND_PHP сохраняет старый путь масштабирования и deprecated с 8.3. Непредсказуемым ни один из этих режимов не становится. Старое сравнение «в четыре раза быстрее libc rand» — историческое описание, не benchmark вашего сервера.
Продолжить: mt_srand, mt_getrandmax, Random\Randomizer::getInt, random_int.
randПоддержать старый несекретный случайный выбор и понять его общее состояние.
PHP 4+; общий MT с PHP 7.1 · random
rand(): int; rand(int $min, int $max): intПоддержать старый несекретный случайный выбор и понять его общее состояние.
| Часть вызова | Договор |
|---|---|
| Аргументы | Либо ни одного, либо оба: min и max включены. При одном аргументе — ArgumentCountError. |
| Возврат | int; без аргументов от 0 до getrandmax() включительно. |
| Ошибки / версии | rand допускает max<min и переставляет их для совместимости. Неверные типы в строгом вызове — TypeError. |
| Состояние | Глобальный MT общ для rand/mt_rand, srand/mt_srand и использующих его array_rand/shuffle/str_shuffle; область функции его не изолирует. |
- Переданы границы 1 и 12
- Получен int из закрытого диапазона
- Отдельный вызов с [4,4] даёт 4
Это старый интерфейс общего Mersenne Twister. Он подходит для воспроизведения несекретных сценариев, но не для токенов. Вызов без аргументов использует 0…getrandmax(); вызов с границами требует сразу два аргумента. Один аргумент не означает «от нуля до него».
<?php
declare(strict_types=1);
$card = rand(1, 12);
echo $card >= 1 && $card <= 12 ? "Карточка входит в набор\n" : "Ошибка\n";
echo rand(4, 4), "\n";
Ожидаемый результат
Карточка входит в набор
4
Это проверка диапазона, не статистический тест качества. Выбранное число может повториться; равномерный выбор с возвращением не является выбором без повторов.
<?php
declare(strict_types=1);
srand(2024);
$first = [rand(1, 12), rand(1, 12), rand(1, 12)];
srand(2024);
$again = [rand(1, 12), rand(1, 12), rand(1, 12)];
echo $first === $again ? "Сценарий повторён\n" : "Другой сценарий\n";
Ожидаемый результат
Сценарий повторён
Повторяемость действует для того же алгоритма, версии и порядка вызовов. Скрытый вызов другой функции общего MT-состояния изменит дальнейшую последовательность. Для нового кода отдельный Randomizer с отдельным engine удобнее.
<?php
declare(strict_types=1);
$value = rand(12, 3);
echo $value >= 3 && $value <= 12 ? "Legacy rand переставил границы\n" : "Ошибка\n";
Ожидаемый результат
Legacy rand переставил границы
rand сохраняет совместимость с обратными границами, mt_rand в PHP 8 бросает ValueError. Приложение всё равно должно задавать понятный порядок min≤max.
С PHP 7.1 rand использует тот же генератор, что mt_rand; правильный MT19937 заменил прежнюю ошибочную реализацию. PHP 7.2 исправил modulo bias, поэтому старые seeded-последовательности на 64-bit могли измениться. Документированные исторические предостережения о масштабировании за getrandmax не следует превращать в запрет полного int-диапазона современного правильного режима: в PHP 8.5.11 он использует rejection sampling и при необходимости несколько слов генератора. Ошибочный MT_RAND_PHP сохраняет старый путь масштабирования и deprecated с 8.3. Непредсказуемым ни один из этих режимов не становится. Старое сравнение «в четыре раза быстрее libc rand» — историческое описание, не benchmark вашего сервера.
До PHP 7.1 на некоторых системах, включая Windows, getrandmax мог быть только 32767. Это не предел нынешнего PHP 8.5.11. Не переписывайте современные требования под старое число.
Продолжить: srand, getrandmax, Random\Randomizer::getInt, random_int.
mt_srandВоспроизвести старый несекретный сценарий, управляя началом общего генератора.
PHP 4+; nullable seed PHP 8.3 · random
mt_srand(?int $seed = null, int $mode = MT_RAND_MT19937): voidВоспроизвести старый несекретный сценарий, управляя началом общего генератора.
| Часть вызова | Договор |
|---|---|
| seed | int, трактуемый как unsigned 32-bit; null/пропуск — случайный seed. Явный null с PHP 8.3. |
| mode | MT_RAND_MT19937 — правильный алгоритм; MT_RAND_PHP — deprecated старый вариант. |
| Возврат | void: полезный эффект — изменение состояния, а не новое число. |
| Ошибки | Неверный тип в strict_types — TypeError. Намеренное использование старого режима сигналит E_DEPRECATED; поведение неизвестного int не следует переносить на класс. |
- Устанавливаем seed 17
- Два вызова mt_rand дают серию a
- Снова seed 17 и те же два вызова
- Серия b совпала с a
Seed — исходное число для запуска последовательности. mt_srand(17) не возвращает случайное число: функция изменяет общий генератор, а следующий mt_rand читает его следующий шаг. Повторная установка того же seed возвращает генератор к началу, а не продолжает прежнюю серию.
<?php
declare(strict_types=1);
mt_srand(17);
$a = [mt_rand(1, 20), mt_rand(1, 20)];
mt_srand(17);
$b = [mt_rand(1, 20), mt_rand(1, 20)];
echo $a === $b ? "Начало повторилось\n" : "Другое начало\n";
Ожидаемый результат
Начало повторилось
Не переустанавливайте seed перед каждым элементом: это повторяло бы первый результат вместо продвижения серии. Явный seed здесь нужен для теста, не для усиления безопасности.
<?php
declare(strict_types=1);
$a = new Random\Randomizer(new Random\Engine\Mt19937(17));
$b = new Random\Randomizer(new Random\Engine\Mt19937(17));
$first = $a->getInt(1, 20);
$a->getInt(1, 20);
echo $first === $b->getInt(1, 20) ? "Второй сценарий не затронут\n" : "Ошибка\n";
Ожидаемый результат
Второй сценарий не затронут
Два разных объекта engine имеют независимые состояния. Передача одного и того же engine в два Randomizer разделила бы состояние вместо изоляции.
Seed трактуется как unsigned 32-bit: всего 2^32 начальных вариантов, хотя период MT составляет 2^19937−1. Период не равен количеству seed. При случайном выборе seed шанс коллизии достигает примерно 10% около 30 тысяч и 50% до 80 тысяч выборов. Для больших воспроизводимых опытов смотрите PCG/Xoshiro с более широкими seed; для секретов — Secure. Автоматический seed уже есть, вызывать srand(time()) для «большей случайности» не требуется. С PHP 8.3 null разрешён явно; до этого можно было просто пропустить seed. srand является alias mt_srand с PHP 7.1.
mode выбирает MT_RAND_MT19937 либо старый MT_RAND_PHP. Второй — ошибочный алгоритм для совместимости, deprecated с PHP 8.3, включая константу. В PHP 8.5.11 процедурный setter для неизвестного int mode фактически выбирает правильный режим; конструктор Random\Engine\Mt19937 вместо этого бросает ValueError. Это различие реализации не повод передавать произвольные числа: используйте документированные константы.
Продолжить: mt_rand, Random\Engine\Mt19937::__construct, Random\Engine\Xoshiro256StarStar::__construct.
srandВоспроизвести старый несекретный сценарий, управляя началом общего генератора.
PHP 4+; nullable seed PHP 8.3 · random
srand(?int $seed = null, int $mode = MT_RAND_MT19937): voidВоспроизвести старый несекретный сценарий, управляя началом общего генератора.
| Часть вызова | Договор |
|---|---|
| seed | int, трактуемый как unsigned 32-bit; null/пропуск — случайный seed. Явный null с PHP 8.3. |
| mode | MT_RAND_MT19937 — правильный алгоритм; MT_RAND_PHP — deprecated старый вариант. |
| Возврат | void: полезный эффект — изменение состояния, а не новое число. |
| Ошибки | Неверный тип в strict_types — TypeError. Намеренное использование старого режима сигналит E_DEPRECATED; поведение неизвестного int не следует переносить на класс. |
- Устанавливаем seed 17
- Два вызова mt_rand дают серию a
- Снова seed 17 и те же два вызова
- Серия b совпала с a
Seed — исходное число для запуска последовательности. srand(17) не возвращает случайное число: функция изменяет общий генератор, а следующий mt_rand читает его следующий шаг. Повторная установка того же seed возвращает генератор к началу, а не продолжает прежнюю серию.
<?php
declare(strict_types=1);
srand(17);
$a = [mt_rand(1, 20), mt_rand(1, 20)];
srand(17);
$b = [mt_rand(1, 20), mt_rand(1, 20)];
echo $a === $b ? "Начало повторилось\n" : "Другое начало\n";
Ожидаемый результат
Начало повторилось
Не переустанавливайте seed перед каждым элементом: это повторяло бы первый результат вместо продвижения серии. Явный seed здесь нужен для теста, не для усиления безопасности.
<?php
declare(strict_types=1);
$a = new Random\Randomizer(new Random\Engine\Mt19937(17));
$b = new Random\Randomizer(new Random\Engine\Mt19937(17));
$first = $a->getInt(1, 20);
$a->getInt(1, 20);
echo $first === $b->getInt(1, 20) ? "Второй сценарий не затронут\n" : "Ошибка\n";
Ожидаемый результат
Второй сценарий не затронут
Два разных объекта engine имеют независимые состояния. Передача одного и того же engine в два Randomizer разделила бы состояние вместо изоляции.
Seed трактуется как unsigned 32-bit: всего 2^32 начальных вариантов, хотя период MT составляет 2^19937−1. Период не равен количеству seed. При случайном выборе seed шанс коллизии достигает примерно 10% около 30 тысяч и 50% до 80 тысяч выборов. Для больших воспроизводимых опытов смотрите PCG/Xoshiro с более широкими seed; для секретов — Secure. Автоматический seed уже есть, вызывать srand(time()) для «большей случайности» не требуется. С PHP 8.3 null разрешён явно; до этого можно было просто пропустить seed. srand является alias mt_srand с PHP 7.1.
mode выбирает MT_RAND_MT19937 либо старый MT_RAND_PHP. Второй — ошибочный алгоритм для совместимости, deprecated с PHP 8.3, включая константу. В PHP 8.5.11 процедурный setter для неизвестного int mode фактически выбирает правильный режим; конструктор Random\Engine\Mt19937 вместо этого бросает ValueError. Это различие реализации не повод передавать произвольные числа: используйте документированные константы.
Продолжить: mt_rand, Random\Engine\Mt19937::__construct, Random\Engine\Xoshiro256StarStar::__construct.
mt_getrandmaxПрочитать верхнюю границу старого генератора без изменения его состояния.
PHP 4+ · random
mt_getrandmax(): intПрочитать верхнюю границу старого генератора без изменения его состояния.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | 2147483647 в текущем PHP: максимум mt_rand() без аргументов. |
| Границы | Не максимум явного min/max диапазона и не доказательство качества генератора. Не требует seed и не продвигает последовательность. |
- mt_getrandmax() сообщает 2147483647
- mt_rand() получает один образец
- Проверяем 0≤образец≤предел
Пустые скобки читают предел старого вызова без границ. Само случайное число эта функция не генерирует. В PHP 8.5.11 предел равен 2^31−1, то есть 2147483647; это не PHP_INT_MAX на 64-bit.
<?php
declare(strict_types=1);
$limit = mt_getrandmax();
echo $limit, "\n";
$sample = mt_rand();
echo $sample >= 0 && $sample <= $limit ? "Значение в пределах\n" : "Ошибка\n";
Ожидаемый результат
2147483647
Значение в пределах
Нельзя заключить по одному образцу, что все значения равномерны; проверяется форма договора. Предел относится к вызову без аргументов.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$quantity = $r->getInt(20, 30);
echo $quantity >= 20 && $quantity <= 30 ? "20..30 включительно\n" : "Ошибка\n";
Ожидаемый результат
20..30 включительно
Не масштабируйте целое делением ради дроби: для неё есть getFloat/nextFloat с явными границами. Для целого getInt принимает нужный диапазон напрямую.
getrandmax — alias mt_getrandmax начиная с PHP 7.1. Старые Windows-варианты rand имели предел 32767. Историческое пояснение «больший диапазон менее случаен из-за масштабирования» не описывает правильный MT-путь PHP 8.5.11: он умеет собирать несколько слов и отвергать лишние значения. Но MT остаётся некриптографическим, а ошибочный deprecated MT_RAND_PHP сохраняет старые ограничения.
Продолжить: mt_rand, Random\Randomizer::getInt, Random\Randomizer::getFloat.
getrandmaxПрочитать верхнюю границу старого генератора без изменения его состояния.
PHP 4+ · random
getrandmax(): intПрочитать верхнюю границу старого генератора без изменения его состояния.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | 2147483647 в текущем PHP: максимум rand() без аргументов. |
| Границы | Не максимум явного min/max диапазона и не доказательство качества генератора. Не требует seed и не продвигает последовательность. |
- getrandmax() сообщает 2147483647
- rand() получает один образец
- Проверяем 0≤образец≤предел
Пустые скобки читают предел старого вызова без границ. Само случайное число эта функция не генерирует. В PHP 8.5.11 предел равен 2^31−1, то есть 2147483647; это не PHP_INT_MAX на 64-bit.
<?php
declare(strict_types=1);
$limit = getrandmax();
echo $limit, "\n";
$sample = rand();
echo $sample >= 0 && $sample <= $limit ? "Значение в пределах\n" : "Ошибка\n";
Ожидаемый результат
2147483647
Значение в пределах
Нельзя заключить по одному образцу, что все значения равномерны; проверяется форма договора. Предел относится к вызову без аргументов.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$quantity = $r->getInt(20, 30);
echo $quantity >= 20 && $quantity <= 30 ? "20..30 включительно\n" : "Ошибка\n";
Ожидаемый результат
20..30 включительно
Не масштабируйте целое делением ради дроби: для неё есть getFloat/nextFloat с явными границами. Для целого getInt принимает нужный диапазон напрямую.
getrandmax — alias mt_getrandmax начиная с PHP 7.1. Старые Windows-варианты rand имели предел 32767. Историческое пояснение «больший диапазон менее случаен из-за масштабирования» не описывает правильный MT-путь PHP 8.5.11: он умеет собирать несколько слов и отвергать лишние значения. Но MT остаётся некриптографическим, а ошибочный deprecated MT_RAND_PHP сохраняет старые ограничения.
Продолжить: rand, Random\Randomizer::getInt, Random\Randomizer::getFloat.
lcg_valueРаспознать устаревший источник дробной случайности и заменить его явным диапазоном.
PHP 4+; deprecated PHP 8.4 · random
lcg_value(): floatРаспознать устаревший источник дробной случайности и заменить его явным диапазоном.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет; собственное внутреннее legacy-состояние. |
| Возврат | Псевдослучайный float в (0,1) по реализации PHP 8.5.11; противоречие старой строки Return Values разобрано ниже. |
| Версия / ошибки | PHP 4+; с PHP 8.4 E_DEPRECATED. Не криптографический источник. |
- lcg_value получает дробь
- Наблюдаем E_DEPRECATED
- Новый getFloat задаёт OpenOpen явно
lcg_value — исторический комбинированный линейный конгруэнтный генератор; deprecated с PHP 8.4. Он не подходит для секретов. Первый пример показывает сигнал устаревания отдельно от числового результата, второй — современную замену с явно открытыми границами.
<?php
declare(strict_types=1);
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
if ($level === E_DEPRECATED) { $deprecated = true; return true; }
return false;
});
try { $value = lcg_value(); }
finally { restore_error_handler(); }
echo $value > 0.0 && $value < 1.0 ? "Между 0 и 1\n" : "Иная граница\n";
echo $deprecated ? "Legacy API\n" : "Без сигнала\n";
Ожидаемый результат
Между 0 и 1
Legacy API
Перехватывается только намеренный E_DEPRECATED; глобальное подавление ошибок не используется. Проверка диапазона не делает слабый генератор безопасным.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$chance = $r->getFloat(0.0, 1.0, Random\IntervalBoundary::OpenOpen);
echo $chance > 0.0 && $chance < 1.0 ? "Оба края исключены\n" : "Ошибка\n";
Ожидаемый результат
Оба края исключены
Нужен PHP 8.3 для getFloat/IntervalBoundary; текущий пример проверен на 8.5.11. Default engine Secure обеспечивает криптографический источник, однако выбор числа не реализует политику приложения.
PHP.net противоречиво называет границы то открытыми (0,1), то включёнными. Tagged-реализация PHP 8.5.11 использует открытый интервал. Там же уточнены периоды составляющих: 2^31−85−1 и 2^31−249−1; комбинированный период учитывает общие делители и имеет порядок 2.3×10^18, а не просто произведение двух названных в старом описании простых чисел. Это историческая информация для чтения legacy-кода. Масштабирование float умножением/сложением может сместить вероятности и попасть на исключённый край: выбирайте нужный интервал сразу в getFloat, для целых — getInt.
Продолжить: Random\Randomizer::getFloat, Random\Randomizer::getInt.
Random\Randomizer::__constructПолучить объект, который превращает байты выбранного источника в числа и перестановки.
PHP 8.2+ · random
new Random\Randomizer(?Random\Engine $engine = null)Получить объект, который превращает байты выбранного источника в числа и перестановки.
| Часть вызова | Договор |
|---|---|
| engine | Объект Engine либо null. null/пропуск создаёт новый Secure. Переданный объект хранится по общей объектной ссылке. |
| Результат | Новый final Randomizer; public readonly Engine $engine. Конструктор не возвращает случайное значение. |
| Ошибка | Неподходящий объект — TypeError. Свой engine может отказать позднее при generate. |
- new Randomizer() создаёт Secure
- getInt(1,6) запрашивает число
- Возвращается int из шести вариантов
new создаёт объект, а круглые скобки передают аргумент конструктору. $r хранит объект; стрелка -> вызывает его метод. Без аргумента либо с null создаётся новый Secure engine. Свойство $r->engine позволяет прочитать источник, но readonly запрещает заменить его после инициализации. Сам объект engine при этом продолжает менять своё внутреннее состояние.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
echo $r->engine instanceof Random\Engine\Secure ? "Источник: Secure\n" : "Другой источник\n";
$n = $r->getInt(1, 6);
echo $n >= 1 && $n <= 6 ? "Один из шести вариантов\n" : "Ошибка\n";
Ожидаемый результат
Источник: Secure
Один из шести вариантов
instanceof проверяет класс объекта, не производит статистического аудита случайности. Метод getInt ставит границы поверх выдачи выбранного источника.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Mt19937(41);
$a = new Random\Randomizer($engine);
$b = new Random\Randomizer($engine);
echo $a->engine === $b->engine ? "Общий engine\n" : "Разные engine\n";
$independent = new Random\Randomizer(clone $engine);
echo $independent->engine !== $engine ? "Копия состояния отдельно\n" : "Общий объект\n";
Ожидаемый результат
Общий engine
Копия состояния отдельно
Обёртка не копирует переданный engine. Для воспроизводимого отделения MT/PCG/Xoshiro можно клонировать сам engine. Randomizer и Secure в PHP 8.5.11 не клонируются; отдельный Secure создают заново. Клонирование PRNG не делает два потока статистически независимыми — они сначала повторяют одну последовательность.
Продолжить: Random\Engine, Random\Engine\Secure, Random\Randomizer::getInt.
Random\Randomizer::getIntВыбрать целое в явно заданных границах от своего источника случайности.
PHP 8.2+ · random
Random\Randomizer::getInt(int $min, int $max): intВыбрать целое в явно заданных границах от своего источника случайности.
| Часть вызова | Договор |
|---|---|
| min / max | Целые нижняя/верхняя границы, min≤max; обе включены. |
| Возврат | Равномерный int в [min,max], включая равные min/max. |
| Ошибки | ValueError при max<min. Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- getInt(10,20)
- Возвращается один из 11 int
- getInt(12,12) возвращает 12
getInt(10, 20) возвращает целое с обеими включёнными границами. Randomizer получает столько байт engine, сколько нужно диапазону; число вызовов generate не обязано быть равно одному. Безопасность результата определяется engine, а не названием метода.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$stock = $r->getInt(10, 20);
echo $stock >= 10 && $stock <= 20 ? "Допустимый остаток\n" : "Ошибка\n";
echo $r->getInt(12, 12), "\n";
Ожидаемый результат
Допустимый остаток
12
Учебный остаток не записывается в магазин. Равные границы дают одно значение. Для настоящего учёта запас берут из складской операции, а случайность применяют только к явно тестовым данным.
<?php
declare(strict_types=1);
function sampleStocks(Random\Randomizer $r): array
{
return ['BR41' => $r->getInt(10, 20), 'NT20' => $r->getInt(10, 20)];
}
$a = sampleStocks(new Random\Randomizer(new Random\Engine\Mt19937(41)));
$b = sampleStocks(new Random\Randomizer(new Random\Engine\Mt19937(41)));
echo $a === $b ? "Тестовая партия воспроизводится\n" : "Партии различаются\n";
try { (new Random\Randomizer())->getInt(20, 10); }
catch (ValueError) { echo "Перепутанные границы отклонены\n"; }
Ожидаемый результат
Тестовая партия воспроизводится
Перепутанные границы отклонены
Тип параметра требует готовый Randomizer; функция не трогает общий mt_srand. Те же seed/алгоритм/порядок вызовов воспроизводят сценарий в проверенном окружении, но это не секретный токен.
Доступен весь диапазон PHP int. Rejection sampling отбрасывает неподходящие битовые комбинации ради равномерного выбора; самодельный остаток % обычно смещает вероятности. Повторение значения допустимо. Если нужны разные элементы без возвращения, используйте pickArrayKeys или shuffleArray.
Продолжить: random_int, Random\Randomizer::getFloat, Random\Randomizer::pickArrayKeys.
Random\Randomizer::getBytesПолучить нужное количество бинарных байт, сохранив выбор источника.
PHP 8.2+ · random
Random\Randomizer::getBytes(int $length): stringПолучить нужное количество бинарных байт, сохранив выбор источника.
| Часть вызова | Договор |
|---|---|
| length | Целое ≥1; размер ответа в байтах. Ограничьте его по договору приложения и памяти. |
| Возврат | Строка ровно length случайных байт; криптографичность зависит от engine. |
| Ошибки | ValueError при length<1. Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- getBytes(12) возвращает бинарную строку
- strlen=12
- bin2hex удваивает длину, не добавляя случайности
getBytes(12) просит 12 байт; это не строка из 12 букв. Она может содержать нулевые байты и неправильный UTF-8. Для подписываемого протокола нужны исходные байты, для текстового поля — их обратимое кодирование.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$bytes = $r->getBytes(12);
echo strlen($bytes), '/', strlen(bin2hex($bytes)), "\n";
Ожидаемый результат
12/24
Default Secure делает байты криптографическими. Если передать MT/PCG/Xoshiro, тот же метод будет воспроизводимым, но не безопасным для секретов. Значение не печатается.
<?php
declare(strict_types=1);
$a = new Random\Randomizer(new Random\Engine\Mt19937(41));
$b = new Random\Randomizer(new Random\Engine\Mt19937(41));
$whole = $a->getBytes(4);
$parts = $b->getBytes(1) . $b->getBytes(3);
echo strlen($whole), '/', strlen($parts), "\n";
echo $whole === $parts ? "Одинаковое потребление\n" : "Запросы потребляют engine иначе\n";
Ожидаемый результат
4/4
Запросы потребляют engine иначе
В PHP 8.5.11 неиспользованный хвост выданного слова engine не становится обещанным буфером для следующего вызова. Фиксируйте порядок и размеры запросов при воспроизведении. Этот опыт ограничен данным MT seed и версией, а не доказывает, что любые две формы обязательно различаются.
Продолжить: random_bytes, Random\Randomizer::getBytesFromString, bin2hex.
Random\Randomizer::getBytesFromStringСоставить читаемый код из заданного байтового алфавита без фильтрации случайной строки.
PHP 8.3+ · random
Random\Randomizer::getBytesFromString(string $string, int $length): stringСоставить читаемый код из заданного байтового алфавита без фильтрации случайной строки.
| Часть вызова | Договор |
|---|---|
| string | Непустая строка байтов-кандидатов. Повтор байта увеличивает вероятность пропорционально его доле. |
| length | Целое ≥1, итоговое количество выбранных байт. Выбор с возвращением: повторы разрешены. |
| Возврат | Строка длины length из байтов string. Не обещание UTF-8, уникальности или одного экземпляра каждого символа. |
| Ошибки | ValueError при пустом string или length<1. Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- ASCII-алфавит без I/O/0/1
- Выбираем 10 позиций с возвращением
- Проверяем длину и принадлежность алфавиту
В getBytesFromString первый аргумент — алфавит байтов, второй — длина результата. Для ABCDEF каждый байт занимает одну позицию и имеет одинаковый шанс. Если записать AAB, A будет выбираться примерно вдвое чаще B: равномерны позиции исходной строки, а не различные значения.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$code = $r->getBytesFromString($alphabet, 10);
echo strlen($code), " characters\n";
echo strspn($code, $alphabet) === strlen($code) ? "Только разрешённый алфавит\n" : "Ошибка\n";
Ожидаемый результат
10 characters
Только разрешённый алфавит
Алфавит намеренно ASCII и без повторов. Удалены похожие I/O/0/1. Это удобство чтения, не гарантия уникальности; достаточную длину выбирают для конкретного протокола.
<?php
declare(strict_types=1);
$r = new Random\Randomizer(new Random\Engine\Mt19937(41));
echo $r->getBytesFromString('X', 5), "\n";
try { $r->getBytesFromString('', 5); }
catch (ValueError) { echo "Пустой алфавит отклонён\n"; }
try { $r->getBytesFromString('ABC', 0); }
catch (ValueError) { echo "Нулевая длина отклонена\n"; }
Ожидаемый результат
XXXXX
Пустой алфавит отклонён
Нулевая длина отклонена
Один символ алфавита допускает единственный ответ, сколько бы байт ни просили. UTF-8 буквы передавать строкой алфавита нельзя: метод выбирал бы отдельные байты и мог разрушить текст. Для букв подготовьте массив целых графем/символов и выбирайте его элементы.
Продолжить: Random\Randomizer::getBytes, Random\Randomizer::shuffleBytes, Random\Randomizer::getInt.
Random\Randomizer::pickArrayKeysВыбрать несколько разных элементов словаря, сохранив доступ к их исходным ключам.
PHP 8.2+ · random
Random\Randomizer::pickArrayKeys(array $array, int $num): arrayВыбрать несколько разных элементов словаря, сохранив доступ к их исходным ключам.
| Часть вызова | Договор |
|---|---|
| array | Массив кандидатов; одинаковый шанс у каждого ключа. Вход не изменяется. |
| num | Целое от 1 до count(array). |
| Возврат | Список num различных int/string ключей, подмножество array_keys(array). |
| Ошибки | ValueError при num<1 или num>count(array). Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- BR41/NT20/WS08 — три кандидата
- num=1 даёт список из одного ключа
- Этот ключ существует в products
pickArrayKeys возвращает ключи, по которым затем читают значения. Даже num=1 возвращает список ключей, а не один scalar. Ключи одного вызова различны; два независимых вызова могут выбрать один ключ снова.
<?php
declare(strict_types=1);
$products = ['BR41' => 'Болт', 'NT20' => 'Гайка', 'WS08' => 'Шайба'];
$r = new Random\Randomizer();
$keys = $r->pickArrayKeys($products, 1);
echo count($keys), '/', array_is_list($keys) ? 'list' : 'other', "\n";
echo array_key_exists($keys[0], $products) ? "Выбран существующий товар\n" : "Ошибка\n";
Ожидаемый результат
1/list
Выбран существующий товар
В $keys[0] находится артикул, а $products[$keys[0]] дал бы его название. В expected не зафиксирован случайный товар. Исходный словарь не изменяется.
<?php
declare(strict_types=1);
$products = ['BR41' => 120, 'NT20' => 50, 'WS08' => 20];
$r = new Random\Randomizer();
$selection = [];
foreach ($r->pickArrayKeys($products, 2) as $sku) { $selection[$sku] = $products[$sku]; }
echo count($selection), ' / ', count($products), "\n";
try { $r->pickArrayKeys([], 1); }
catch (ValueError) { echo "Из пустого набора выбирать нечего\n"; }
Ожидаемый результат
2 / 3
Из пустого набора выбирать нечего
Ключи selection сохраняют связь с ценой. Это две разные исходные позиции; одинаковые значения под разными ключами всё равно были бы разными кандидатами.
Результат зависит не только от значений массива и состояния engine, но и от внутренней структуры массива: одинаковые массивы, собранные разными способами, могут дать разные seeded-ключи. Поэтому стабильный seed не является обещанием стабильной выборки после рефакторинга построения массива. Не используйте порядок ответа как отдельное случайное перемешивание: для этого есть shuffleArray.
Продолжить: array_rand, Random\Randomizer::shuffleArray.
Random\Randomizer::shuffleArrayПолучить новый случайный порядок карточек, сохранив исходный набор.
PHP 8.2+ · random
Random\Randomizer::shuffleArray(array $array): arrayПолучить новый случайный порядок карточек, сохранив исходный набор.
| Часть вызова | Договор |
|---|---|
| array | Массив значений; входной массив не меняется. |
| Возврат | Равномерно выбранная перестановка значений в списке 0…n−1; исходные ключи не сохраняются. Пустой вход даёт []. |
| Ошибки | Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- Ключи исходника 10/20/30
- Новый результат получает 0/1/2
- Сверяем те же три значения независимо от случайного порядка
shuffleArray возвращает новый список значений. Прежние ключи отбрасываются, а исходный массив остаётся прежним. Если артикул нужен после перемешивания, храните его внутри каждой записи либо перемешивайте отдельный список ключей.
<?php
declare(strict_types=1);
$questions = [10 => 'Оплата', 20 => 'Доставка', 30 => 'Возврат'];
$r = new Random\Randomizer();
$shuffled = $r->shuffleArray($questions);
echo json_encode(array_keys($questions)), "\n";
echo json_encode(array_keys($shuffled)), "\n";
$check = $shuffled; sort($check);
$original = array_values($questions); sort($original);
echo $check === $original ? "Состав сохранён\n" : "Потеря значения\n";
Ожидаемый результат
[10,20,30]
[0,1,2]
Состав сохранён
Порядок случайный, состав детерминированно проверяется после сортировки копий. Не требуется, чтобы порядок обязательно изменился: исходная перестановка тоже допустима.
<?php
declare(strict_types=1);
$cards = [['id' => 'BR41', 'qty' => 12], ['id' => 'NT20', 'qty' => 8]];
$result = (new Random\Randomizer())->shuffleArray($cards);
$byId = array_column($result, 'qty', 'id'); ksort($byId);
echo json_encode($byId), "\n";
echo count((new Random\Randomizer())->shuffleArray([])), "\n";
Ожидаемый результат
{"BR41":12,"NT20":8}
0
Каждая запись переносится целиком, связь ID→qty сохраняется. Пустой массив допустим и возвращается пустым списком. Если элементы — объекты, перестановка не клонирует сами объекты.
Продолжить: shuffle, Random\Randomizer::pickArrayKeys.
Random\Randomizer::shuffleBytesПереставить байтовый набор и понять, когда для текста нужен массив целых элементов.
PHP 8.2+ · random
Random\Randomizer::shuffleBytes(string $bytes): stringПереставить байтовый набор и понять, когда для текста нужен массив целых элементов.
| Часть вызова | Договор |
|---|---|
| bytes | Строка любых байт, включая пустую; вход не меняется. |
| Возврат | Перестановка тех же байт с той же длиной и кратностью каждого значения. Не обещание корректного UTF-8. |
| Ошибки | Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- Четыре позиции, две содержат A
- shuffleBytes меняет порядок
- Сверяем количество каждого байта, исходник AABC прежний
shuffleBytes переставляет отдельные байты строки. Для ASCII кодов A/B/C каждый байт совпадает с символом. Для русского текста одна буква занимает несколько байт, а составной видимый символ может включать несколько Unicode code points: байтовое перемешивание не подходит.
<?php
declare(strict_types=1);
$input = 'AABC';
$result = (new Random\Randomizer())->shuffleBytes($input);
echo strlen($result), '/', $input, "\n";
echo count_chars($result, 1) === count_chars($input, 1) ? "Количество каждого байта сохранено\n" : "Ошибка\n";
Ожидаемый результат
4/AABC
Количество каждого байта сохранено
Два A остаются двумя A. Повторяющиеся байты означают, что разные перестановки позиций могут выглядеть одинаково. Ответ может совпасть с исходной строкой.
<?php
declare(strict_types=1);
$symbols = ['Б', 'О', 'Л', 'Т'];
$result = (new Random\Randomizer())->shuffleArray($symbols);
$text = implode('', $result);
echo mb_strlen($text, 'UTF-8'), ' / ', count($result), "\n";
$check = $result; sort($check); $expected = $symbols; sort($expected);
echo $check === $expected ? "Буквы сохранены целиком\n" : "Ошибка\n";
Ожидаемый результат
4 / 4
Буквы сохранены целиком
Здесь границы элементов заданы вручную и каждая буква — один элемент. Для произвольного текста предварительно выбирают Unicode code points или графемные кластеры по задаче; mb_str_split не обещает разбиение по видимым графемам.
Продолжить: str_shuffle, Random\Randomizer::shuffleArray, Random\Randomizer::getBytesFromString.
Random\Randomizer::nextIntПрочитать неотрицательное целое из слова engine, когда известна его ширина.
PHP 8.2+ · random
Random\Randomizer::nextInt(): intПрочитать неотрицательное целое из слова engine, когда известна его ширина.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | int ≥0; максимум зависит от длины слова engine, а не выбранного вами диапазона. |
| Ошибки | Random\RandomException, если слово шире PHP int, например 8 байт на 32-bit. Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- Mt19937 генерирует 32 бита
- nextInt использует 31 бит
- Проверяем 0…2147483647
nextInt получает одно слово engine и сдвигает его вправо на один бит, чтобы вернуть неотрицательное целое. Ноль возможен: термин positive в кратком английском описании не исключает 0. Граница зависит от количества выданных байт, поэтому это не удобная замена getInt(min,max).
<?php
declare(strict_types=1);
$r = new Random\Randomizer(new Random\Engine\Mt19937(41));
$value = $r->nextInt();
echo $value >= 0 && $value <= 2147483647 ? "0..2^31-1\n" : "Ошибка\n";
Ожидаемый результат
0..2^31-1
MT generate выдаёт 4 байта, поэтому полезный предел после сдвига — 31 бит. Метод не делит результат по вашему бизнес-диапазону.
<?php
declare(strict_types=1);
final class ZeroByteEngine implements Random\Engine
{
public function generate(): string { return "\0"; }
}
$r = new Random\Randomizer(new ZeroByteEngine());
echo $r->nextInt(), "\n";
Ожидаемый результат
0
Это заведомо неслучайный test double, только для проверки нижней границы, не источник для приложения. Один байт даёт диапазон до 127 при качественном engine; здесь всегда ноль. Пользовательский Engine разобран отдельно.
Для b байт эффективного слова максимум равен 2^(8b−1)−1; Randomizer интерпретирует не более 8 байт слова. На 32-bit PHP выдача engine длиннее 4 байт приводит к Random\RandomException, включая native PCG/Xoshiro. На 64-bit их nextInt может достигать PHP_INT_MAX. Для переносимого нужного диапазона используйте getInt. Нельзя оценивать качество источника по одному удачному числу.
Продолжить: Random\Engine::generate, Random\Randomizer::getInt.
Random\Randomizer::__serializeЗафиксировать текущее состояние воспроизводимого несекретного опыта.
PHP 8.2+ · random
Random\Randomizer::__serialize(): arrayЗафиксировать текущее состояние воспроизводимого несекретного опыта.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив свойств; вложенный engine пока остаётся объектом. Для настоящего снимка используется полный serialize. |
| Ошибки | Полный serialize отказывает, если engine не сериализуется; Secure не поддерживает сериализацию. |
- MT seed 41 уже сделал один шаг
- serialize сохраняет текущую точку
- unserialize создаёт отдельный объект
- Следующие 16 байт совпали
Обычно эти магические методы вызывает PHP при serialize/unserialize. Сохраняется выбранный engine и его текущее состояние, когда сам engine поддерживает сериализацию. Это механизм воспроизведения локального несекретного опыта, не универсальное хранилище секретов.
<?php
declare(strict_types=1);
$r = new Random\Randomizer(new Random\Engine\Mt19937(41));
$r->getBytes(4);
$snapshot = serialize($r);
$restored = unserialize($snapshot, ['allowed_classes' => [Random\Randomizer::class, Random\Engine\Mt19937::class]]);
echo $r->getBytes(16) === $restored->getBytes(16) ? "Продолжение совпало\n" : "Другая точка\n";
echo $r->engine !== $restored->engine ? "Состояния в отдельных объектах\n" : "Общий engine\n";
Ожидаемый результат
Продолжение совпало
Состояния в отдельных объектах
snapshot создан здесь же из доверенного объекта. Даже allowed_classes не делает произвольный внешний serialized-текст безопасным. Общее правило: не передавать untrusted данные в unserialize. Для обмена данными нужен отдельный безопасный протокол.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
try { serialize($r); }
catch (Exception) { echo "Secure не сериализуется\n"; }
Ожидаемый результат
Secure не сериализуется
Прямой __serialize возвращает массив свойств с engine-объектом и сам по себе не создаёт глубокий замороженный снимок. Полный serialize пытается сериализовать вложенный Secure и отказывает. Не заменяйте engine на MT только ради сохранения секретного потока.
PHP.net пока публикует для этих методов главным образом сигнатуру. Уточнения о составе/проверках и границе Secure сверены с tagged PHP 8.5.11 source и локальными probes. Внутренний массив не предлагается как стабильный внешний формат между произвольными версиями PHP. $engine остаётся readonly; для обычного восстановления используйте новый объект, созданный unserialize, а не переинициализацию уже работающей обёртки.
Продолжить: Random\Randomizer::__unserialize, serialize, unserialize.
Random\Randomizer::__unserializeВосстановить текущее состояние воспроизводимого несекретного опыта.
PHP 8.2+ · random
Random\Randomizer::__unserialize(array $data): voidВосстановить текущее состояние воспроизводимого несекретного опыта.
| Часть вызова | Договор |
|---|---|
| data | Массив формата __serialize этого класса; передаётся механизмом PHP. Не произвольная конфигурация или пользовательский JSON. |
| Возврат | void; настраивает объект. |
| Ошибки | Exception для неверной структуры в PHP 8.5.11; типы/свойства также проверяются. |
- MT seed 41 уже сделал один шаг
- serialize сохраняет текущую точку
- unserialize создаёт отдельный объект
- Следующие 16 байт совпали
Обычно эти магические методы вызывает PHP при serialize/unserialize. Сохраняется выбранный engine и его текущее состояние, когда сам engine поддерживает сериализацию. Это механизм воспроизведения локального несекретного опыта, не универсальное хранилище секретов.
<?php
declare(strict_types=1);
$r = new Random\Randomizer(new Random\Engine\Mt19937(41));
$r->getBytes(4);
$snapshot = serialize($r);
$restored = unserialize($snapshot, ['allowed_classes' => [Random\Randomizer::class, Random\Engine\Mt19937::class]]);
echo $r->getBytes(16) === $restored->getBytes(16) ? "Продолжение совпало\n" : "Другая точка\n";
echo $r->engine !== $restored->engine ? "Состояния в отдельных объектах\n" : "Общий engine\n";
Ожидаемый результат
Продолжение совпало
Состояния в отдельных объектах
snapshot создан здесь же из доверенного объекта. Даже allowed_classes не делает произвольный внешний serialized-текст безопасным. Общее правило: не передавать untrusted данные в unserialize. Для обмена данными нужен отдельный безопасный протокол.
<?php
declare(strict_types=1);
$r = new Random\Randomizer(new Random\Engine\Mt19937(41));
try { $r->__unserialize([]); }
catch (Exception) { echo "Некорректное состояние отклонено\n"; }
Ожидаемый результат
Некорректное состояние отклонено
В PHP 8.5.11 неправильная структура вызывает Exception. Это намеренная проверка ошибки, а не рецепт ручного конструирования внутреннего массива; после неудачного восстановления не следует полагаться на частично изменённый объект.
PHP.net пока публикует для этих методов главным образом сигнатуру. Уточнения о составе/проверках и границе Secure сверены с tagged PHP 8.5.11 source и локальными probes. Внутренний массив не предлагается как стабильный внешний формат между произвольными версиями PHP. $engine остаётся readonly; для обычного восстановления используйте новый объект, созданный unserialize, а не переинициализацию уже работающей обёртки.
Продолжить: Random\Randomizer::__serialize, serialize, unserialize.
Random\Randomizer::getFloatВыбрать дробное тестовое значение в точном интервале без самодельного растягивания числа.
PHP 8.3+ · random
Random\Randomizer::getFloat(float $min, float $max, Random\IntervalBoundary $boundary = Random\IntervalBoundary::ClosedOpen): floatВыбрать дробное тестовое значение в точном интервале без самодельного растягивания числа.
| Часть вызова | Договор |
|---|---|
| min / max | Конечные float: NAN и ±INF запрещены. Границы должны оставлять хотя бы один выбираемый float. |
| boundary | Enum-case ClosedOpen по умолчанию; четыре режима включения краёв разобраны отдельно. |
| Возврат | Равномерно выбранный float из документированного набора внутри заданных границ. Закрытые края являются кандидатами. |
| Ошибки | ValueError для не-finite границы либо пустого множества значений. Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- Переданы 0.5 и 1.5
- ClosedOpen включает 0.5 и исключает 1.5
- Проверяем float и оба условия
Для учебного времени обработки нужно число от 0.5 секунды до 1.5, причём верхний край исключён. Это записывается [0.5,1.5): квадратная скобка включает край, круглая исключает. getFloat принимает границы напрямую, поэтому не приходится растягивать результат другой функции и получать неожиданные округления.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
$seconds = $r->getFloat(0.5, 1.5);
echo is_float($seconds) && $seconds >= 0.5 && $seconds < 1.5 ? "0.5 ≤ seconds < 1.5\n" : "Ошибка\n";
Ожидаемый результат
0.5 ≤ seconds < 1.5
Default boundary — ClosedOpen. Никакого ожидания нет: мы создаём тестовое поле длительности, а не вызываем sleep. Форматировать число для человека можно отдельно в конце.
<?php
declare(strict_types=1);
$r = new Random\Randomizer();
var_dump($r->getFloat(2.5, 2.5, Random\IntervalBoundary::ClosedClosed));
try { $r->getFloat(2.5, 2.5); }
catch (ValueError) { echo "Полуоткрытый интервал пуст\n"; }
try { $r->getFloat(1.0, 1.0000000000000002, Random\IntervalBoundary::OpenOpen); }
catch (ValueError) { echo "Между соседними float нет варианта\n"; }
try { $r->getFloat(0.0, INF); }
catch (ValueError) { echo "Бесконечная граница отклонена\n"; }
Ожидаемый результат
float(2.5)
Полуоткрытый интервал пуст
Между соседними float нет варианта
Бесконечная граница отклонена
ClosedClosed разрешает единственное число. Во втором OpenOpen-примере min<max, но между соседними представимыми double нет третьего: одной проверки min<max недостаточно. NAN тоже не является допустимой границей.
Равномерность относится к максимально большому набору равноотстоящих точно представимых чисел выбранного интервала, с включением закрытых краёв. Это не выбор любого вещественного числа и не одинаковая вероятность всех существующих double: их плотность меняется на степенях двух. Полный числовой механизм показан в модели шага и округления. Метод использует γ-section; выбор очень малых границ около |x|<2^-1020 (примерно 8.9×10^-308) имеет документированную оговорку о необработанном underflow и может вернуть неверные значения. Такие масштабы требуют отдельного численного анализа, не данного учебного диапазона.
Не делайте после выбора неявное масштабирование ради другого диапазона: оно может нарушить распределение и открыть/закрыть край вопреки договору. Для денег и фиксированного десятичного шага удобнее выбрать целое число минимальных единиц через getInt, затем форматировать. Вывод с округлением — представление, а не новое доказательство равномерности округлённых значений.
Продолжить: Random\IntervalBoundary, Random\Randomizer::nextFloat, Random\Randomizer::getInt.
Random\Randomizer::nextFloatПринять случайное решение с заданной вероятностью на шкале от 0 до 1.
PHP 8.3+ · random
Random\Randomizer::nextFloat(): floatПринять случайное решение с заданной вероятностью на шкале от 0 до 1.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Равномерный, равнораспределённый float из [0.0,1.0); 0 возможен, 1 нет. |
| Ошибки | Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов. |
- Получен sample из [0,1)
- Условие sample<0.25 выделяет четверть шкалы
- Результат сравнения — bool
nextFloat() не принимает границ и возвращает число от 0 включительно до 1 исключительно. Такая шкала удобна для выбора с заданной вероятностью: сравнение result<0.25 выделяет нижнюю четверть шкалы. Одна удачная проверка не подтверждает частоту на реальных пользователях; пример проверяет код правила.
<?php
declare(strict_types=1);
$sample = (new Random\Randomizer())->nextFloat();
echo $sample >= 0.0 && $sample < 1.0 ? "0 ≤ sample < 1\n" : "Ошибка\n";
echo is_bool($sample < 0.25) ? "Решение для 25% получено\n" : "Ошибка типа\n";
Ожидаемый результат
0 ≤ sample < 1
Решение для 25% получено
Ноль разрешён, единица исключена. Поэтому сравнение с p=0 никогда не принимает, а с p=1 всегда принимает. Это случайный выбор на каждый вызов, не постоянное распределение одного пользователя по группе.
<?php
declare(strict_types=1);
function sampleRequest(Random\Randomizer $r, float $chance): bool
{
if (!is_finite($chance) || $chance < 0.0 || $chance > 1.0) { throw new ValueError('chance'); }
return $r->nextFloat() < $chance;
}
$r = new Random\Randomizer();
var_dump(sampleRequest($r, 0.0), sampleRequest($r, 1.0));
try { sampleRequest($r, 1.2); }
catch (ValueError) { echo "Вероятность вне 0..1\n"; }
Ожидаемый результат
bool(false)
bool(true)
Вероятность вне 0..1
Функция возвращает решение, но ничего не пишет в журнал. Реальный сбор диагностики отдельно должен исключать секреты и учитывать согласованную политику данных. Границы политики проверяются до вызова источника.
Множество результатов совпадает с getFloat(0.0,1.0,ClosedOpen), но nextFloat специализирован для этого случая. В PHP 8.5.11 оно основано на 53 битах и шаге 2^-53. Не умножайте эту шкалу на произвольный диапазон вместо getFloat: на другой плотности double появятся округления. Для целого варианта нужен getInt, для непрерывного распределения с иной плотностью — отдельная математическая модель, а не переименование равномерного результата.
Продолжить: Random\Randomizer::getFloat, Random\Randomizer::getInt.
Random\Engine::generateОтделить низкоуровневый источник байт от операции выбора и корректно передать отказ.
PHP 8.2+ · random
Random\Engine::generate(): stringОтделить низкоуровневый источник байт от операции выбора и корректно передать отказ.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. Реализуется классом Engine. |
| Возврат | Непустые случайные байты и следующий шаг состояния. Числовые алгоритмы возвращают little-endian; рекомендуются равномерные независимые биты. |
| Ошибки | При отказе — Random\RandomException; чужой Exception следует обернуть с previous. Randomizer отвергает пустую/непригодную выдачу через BrokenRandomEngineError. |
- Fixture выдаёт 0x12345678 как 78563412
- Позиция переходит к 0x1234567a
- nextInt читает уже второе слово и возвращает 152709949
Engine — интерфейс источника: его generate() должен вернуть непустую бинарную строку и продвинуть состояние алгоритма на один шаг. Randomizer трактует числовой ответ как unsigned little-endian. В таком порядке младший байт идёт первым: число 0x12345678 упаковывается байтами 78 56 34 12. pack("V", ...) — явно little-endian 32-bit; для unsigned 64-bit есть pack("P", ...).
<?php
declare(strict_types=1);
final class PacketFixture implements Random\Engine
{
private array $words = [0x12345678, 0x1234567a];
private int $position = 0;
public function generate(): string
{
if (!isset($this->words[$this->position])) { throw new Random\RandomException('Конец fixture'); }
return pack('V', $this->words[$this->position++]);
}
}
$engine = new PacketFixture();
echo bin2hex($engine->generate()), "\n";
echo (new Random\Randomizer($engine))->nextInt(), "\n";
Ожидаемый результат
78563412
152709949
Это конечная тестовая последовательность, не случайный алгоритм. Первый вызов потребил первое слово. nextInt затем прочитал второе и сдвинул его вправо на один бит: младший бит потерян, результат неотрицателен. В настоящем источнике каждый бит должен выбираться качественно; меняющиеся числа сами по себе этого не доказывают.
<?php
declare(strict_types=1);
final class FailingFixture implements Random\Engine
{
public function generate(): string
{
try { throw new RuntimeException('Учебная зависимость недоступна'); }
catch (Exception $cause) { throw new Random\RandomException('Источник не ответил', 0, $cause); }
}
}
try { (new Random\Randomizer(new FailingFixture()))->getBytes(4); }
catch (Random\RandomException $error) {
echo $error->getMessage(), "\n";
echo $error->getPrevious() instanceof RuntimeException ? "Причина сохранена\n" : "Без причины\n";
}
Ожидаемый результат
Источник не ответил
Причина сохранена
Никакая зависимость не вызывается: отказ намеренно смоделирован. Договор рекомендует оборачивать Exception источника в RandomException, сохраняя previous. Randomizer передаст такой отказ; он не должен выдавать предсказуемую запасную строку.
Randomizer использует до 8 байт каждого выданного слова; излишек игнорируется. Короткие слова при необходимости объединяются. Движок может выдавать больше байт для другого потребителя, но Randomizer не обязан их сохранять. Пустой ответ вызывает BrokenRandomEngineError. Сильно смещённая выдача может также привести к этой ошибке при rejection sampling, чтобы не крутиться бесконечно; отсутствие ошибки не является проверкой статистического или криптографического качества.
Продолжить: Random\Randomizer, Random\BrokenRandomEngineError, Random\CryptoSafeEngine.
Random\Engine\Secure::generateПолучить одно слово системной криптографической случайности для низкоуровневого потребителя.
PHP 8.2+ · random
Random\Engine\Secure::generate(): stringПолучить одно слово системной криптографической случайности для низкоуровневого потребителя.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Строка ровно PHP_INT_SIZE криптографически стойких байт; не текст и не пользовательская длина. |
| Ошибка | Random\RandomException, если доступные системные источники не дали случайность. Источники и версии описаны в общей таблице главы. |
- Secure читает CSPRNG ОС
- generate возвращает бинарное слово
- Сверяем длину с PHP_INT_SIZE, не печатая байты
Secure::generate() читает системный криптографический источник и возвращает одно машинное слово: PHP_INT_SIZE байт. Этот размер — ширина платформы, а не выбранная приложением длина токена. Для конкретного количества байт удобнее Randomizer::getBytes или random_bytes.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Secure();
$word = $engine->generate();
echo strlen($word) === PHP_INT_SIZE ? "Размер слова соответствует PHP_INT_SIZE\n" : "Ошибка\n";
echo $engine instanceof Random\CryptoSafeEngine ? "CryptoSafeEngine\n" : "Другой контракт\n";
Ожидаемый результат
Размер слова соответствует PHP_INT_SIZE
CryptoSafeEngine
На проверенной Windows x64 PHP_INT_SIZE=8; пример сравнивает контракт, а не объявляет 8 универсальным значением. Никакое содержимое байт не попадает в вывод.
<?php
declare(strict_types=1);
$r = new Random\Randomizer(new Random\Engine\Secure());
$token = bin2hex($r->getBytes(24));
echo strlen($token), " hex characters\n";
Ожидаемый результат
48 hex characters
Randomizer получает нужное количество слов и выдаёт запрошенные 24 байта, hex затем занимает 48 символов. Получить один и тот же Secure-поток по seed нельзя; параметра seed у этого класса нет.
Продолжить: random_bytes, Random\Randomizer::getBytes, Random\Engine\Secure.
Random\Engine\Mt19937::__constructНастроить начальную точку, чтобы воспроизвести старый тест на Mersenne Twister с отдельным состоянием.
PHP 8.2+ · random
new Random\Engine\Mt19937(?int $seed = null, int $mode = MT_RAND_MT19937)Настроить начальную точку, чтобы воспроизвести старый тест на Mersenne Twister с отдельным состоянием.
| Часть вызова | Договор |
|---|---|
| seed | int или null; int трактуется как unsigned 32-bit. null/пропуск заполняет seed из CSPRNG. |
| mode | MT_RAND_MT19937 — правильный алгоритм. MT_RAND_PHP — ошибочный режим совместимости до PHP 7.1, deprecated с 8.3. |
| Ошибки | ValueError при неизвестном mode; E_DEPRECATED у старого режима. Отказ CSPRNG при null-seed — Random\RandomException. Неверный тип в strict_types — TypeError. |
- Mt19937(41) создаётся дважды
- Объекты разные
- Первые generate возвращают одинаковые байты
new Mt19937(41) создаёт отдельный engine. В первом опыте два новых объекта получают один и тот же явный seed и выполняют одинаковую первую операцию; это проверка воспроизводимости, а не утверждение о непредсказуемости.
<?php
declare(strict_types=1);
$a = new Random\Engine\Mt19937(41);
$b = new Random\Engine\Mt19937(41);
echo $a !== $b ? "Разные объекты\n" : "Один объект\n";
echo $a->generate() === $b->generate() ? "Одинаковый старт\n" : "Разный старт\n";
Ожидаемый результат
Разные объекты
Одинаковый старт
Если один объект сделает лишний generate/getInt, дальнейшие ответы разойдутся. Размеры и порядок высокоуровневых запросов являются частью воспроизводимого сценария.
<?php
declare(strict_types=1);
try { new Random\Engine\Mt19937(41, 999); }
catch (ValueError) { echo "Неизвестный режим отклонён\n"; }
$a = new Random\Engine\Mt19937(41);
$b = new Random\Engine\Mt19937(41 + 4294967296);
echo $a->generate() === $b->generate() ? "Используются младшие 32 бита seed\n" : "Иной seed\n";
Ожидаемый результат
Неизвестный режим отклонён
Используются младшие 32 бита seed
Второй опыт требует 64-bit PHP, чтобы представить 41+2^32; это проверенная среда PHP 8.5.11 x64. У процедурного mt_srand неизвестный int mode ведёт себя иначе. Используйте документированные константы, не этот факт как конфигурационный интерфейс.
Всего 2^32 начальных seed, несмотря на огромный период MT. При случайном выборе seed коллизии вероятны гораздо раньше исчерпания пространства: примерно 10% около 30 тысяч, 50% менее 80 тысяч. Для большого пространства воспроизводимых начальных состояний смотрите PCG/Xoshiro. Безопасный seed из CSPRNG не превращает дальнейший MT-поток в CSPRNG.
Продолжить: Random\Engine\Mt19937::generate, Random\Randomizer::__construct.
Random\Engine\Mt19937::generateПолучить следующее слово воспроизводимого engine и продвинуть его состояние.
PHP 8.2+ · random
Random\Engine\Mt19937::generate(): stringПолучить следующее слово воспроизводимого engine и продвинуть его состояние.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Ровно 4 байта, unsigned 32-bit little-endian. Состояние продвигается на один шаг. |
| Граница | Это не криптографические байты. Для преобразования в диапазон используйте Randomizer, а не int cast или остаток %. |
- Mt19937(41)
- generate выдаёт 4 байта
- bin2hex показывает c0873c40
generate возвращает сырые 4 байта, представляющие unsigned 32-битное слово в little-endian. bin2hex делает каждый байт видимым, но порядок байт остаётся тем же: это не обычная шестнадцатеричная запись всего числового слова. Для нужного диапазона передайте engine в Randomizer.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Mt19937(41);
$word = $engine->generate();
echo strlen($word), '/', bin2hex($word), "\n";
Ожидаемый результат
4/c0873c40
Это точный первый ответ seed 41 в проверенной PHP 8.5.11, не вывод свежего секрета и не универсальный ответ вызова без seed. Сырые байты не нужно трактовать как UTF-8 или приводить string к int.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Mt19937(41);
$engine->generate();
$copy = clone $engine;
echo $engine->generate() === $copy->generate() ? "Следующий шаг совпал\n" : "Ошибка\n";
Ожидаемый результат
Следующий шаг совпал
Копируется текущее состояние после уже сделанного шага. Это удобно для сравнения двух вариантов теста с одной стартовой точкой, но не создаёт два непересекающихся потока автоматически.
Продолжить: Random\Engine\Mt19937::__construct, Random\Randomizer::getInt, Random\Engine::generate.
Random\Engine\Mt19937::__debugInfoПонять диагностический снимок engine, не путая его со случайным результатом.
PHP 8.2+ · random
Random\Engine\Mt19937::__debugInfo(): arrayПонять диагностический снимок engine, не путая его со случайным результатом.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив внутреннего диагностического состояния. В PHP 8.5.11 содержит __states; это source-bounded деталь. |
| Граница | Просмотр не продвигает engine. Состояние не следует публиковать; устойчивый внешний формат документацией не обещан. |
- Mt19937 с seed 41
- __debugInfo читает состояние
- В выводе только факт наличия __states
__debugInfo — магический метод для диагностического представления, например при var_dump. Он возвращает массив сведений о состоянии, а не следующий случайный результат. Формат внутренних полей не стоит делать протоколом приложения; полный dump раскрывает состояние и делает будущую выдачу воспроизводимой.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Mt19937(41);
$info = $engine->__debugInfo();
echo is_array($info) && isset($info['__states']) ? "Диагностическое состояние доступно\n" : "Иной формат\n";
Ожидаемый результат
Диагностическое состояние доступно
Имя __states здесь подтверждено source PHP 8.5.11, а не обещано как стабильный внешний формат PHP.net. Содержимое не печатается в общий журнал.
<?php
declare(strict_types=1);
$a = new Random\Engine\Mt19937(41);
$b = clone $a;
$a->__debugInfo();
echo $a->generate() === $b->generate() ? "Просмотр не сделал шаг\n" : "Состояние изменилось\n";
Ожидаемый результат
Просмотр не сделал шаг
Для диагностики проверьте версию, класс и явно выбранные параметры, не раскрывая всю серию. Отдельный generate изменил бы её; __debugInfo в этой реализации только читает состояние.
Продолжить: Random\Engine\Mt19937::__serialize, Random\Engine\Mt19937::generate.
Random\Engine\Mt19937::__serializeСохранить промежуточное состояние Mt19937 для продолжения несекретного опыта.
PHP 8.2+ · random
Random\Engine\Mt19937::__serialize(): arrayСохранить промежуточное состояние Mt19937 для продолжения несекретного опыта.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив снимка свойств/состояния; обычно обрабатывается serialize. |
| Ошибка / границы | Внутренний сериализатор может бросить Exception; формат не предлагается как стабильный внешний протокол. |
- Mt19937 seed 41 делает шаг
- __serialize фиксирует эту точку
- __unserialize передаёт её объекту с seed 7
- Следующие generate совпадают
Обычный serialize/unserialize вызывает эти методы автоматически. У native PRNG массив снимка содержит свойства и числовое состояние, поэтому сохранённая точка позволяет продолжить учебную серию. Не конструируйте внутренние поля вручную и не принимайте serialized-текст от посетителя.
<?php
declare(strict_types=1);
$a = new Random\Engine\Mt19937(41);
$a->generate();
$data = $a->__serialize();
$b = new Random\Engine\Mt19937(7);
$b->__unserialize($data);
echo $a->generate() === $b->generate() ? "Точка восстановлена\n" : "Другая точка\n";
Ожидаемый результат
Точка восстановлена
В data лежит собственный trusted снимок того же класса/версии. Исходный seed b=7 заменён сохранённым состоянием a. Метод __unserialize возвращает void; полезный результат — изменённое состояние b, а не новый объект.
<?php
declare(strict_types=1);
$a = new Random\Engine\Mt19937(41);
$saved = serialize($a);
$b = unserialize($saved, ['allowed_classes' => [Random\Engine\Mt19937::class]]);
echo $a !== $b && $a->generate() === $b->generate() ? "Отдельный объект продолжает ту же серию\n" : "Ошибка\n";
Ожидаемый результат
Отдельный объект продолжает ту же серию
Снимок сделан локально из известного объекта; allowed_classes дополнительно сужает допустимые классы, но не является разрешением парсить произвольные внешние serialized-данные. Совместимость между любыми версиями формата не обещается.
Для метода PHP.net пока даёт прежде всего сигнатуру. Описанные проверки, массив состояния и диагностируемый Exception дополнены чтением tagged source PHP 8.5.11 и запуском этих примеров. При сохранении длительного опыта запишите алгоритм, версию PHP, seed/снимок и последовательность операций; не считайте внутренний формат долговечным межъязыковым стандартом.
Продолжить: Random\Engine\Mt19937::__unserialize, Random\Randomizer::__serialize.
Random\Engine\Mt19937::__unserializeВосстановить промежуточное состояние Mt19937 для продолжения несекретного опыта.
PHP 8.2+ · random
Random\Engine\Mt19937::__unserialize(array $data): voidВосстановить промежуточное состояние Mt19937 для продолжения несекретного опыта.
| Часть вызова | Договор |
|---|---|
| data | Собственный массив __serialize этого же native-класса; не произвольные seed-параметры. |
| Возврат | void: изменяет состояние объекта. |
| Ошибка / границы | Exception при неверной структуре в PHP 8.5.11; не полагайтесь на объект после неудачной загрузки. |
- Mt19937 seed 41 делает шаг
- __serialize фиксирует эту точку
- __unserialize передаёт её объекту с seed 7
- Следующие generate совпадают
Обычный serialize/unserialize вызывает эти методы автоматически. У native PRNG массив снимка содержит свойства и числовое состояние, поэтому сохранённая точка позволяет продолжить учебную серию. Не конструируйте внутренние поля вручную и не принимайте serialized-текст от посетителя.
<?php
declare(strict_types=1);
$a = new Random\Engine\Mt19937(41);
$a->generate();
$data = $a->__serialize();
$b = new Random\Engine\Mt19937(7);
$b->__unserialize($data);
echo $a->generate() === $b->generate() ? "Точка восстановлена\n" : "Другая точка\n";
Ожидаемый результат
Точка восстановлена
В data лежит собственный trusted снимок того же класса/версии. Исходный seed b=7 заменён сохранённым состоянием a. Метод __unserialize возвращает void; полезный результат — изменённое состояние b, а не новый объект.
<?php
declare(strict_types=1);
$candidate = new Random\Engine\Mt19937(41);
try { $candidate->__unserialize([]); }
catch (Exception) { echo "Снимок отклонён\n"; }
Ожидаемый результат
Снимок отклонён
PHP 8.5.11 проверяет структуру и поля состояния и бросает Exception при неверных данных. После отказа восстановления отбросьте candidate: общий протокол не обещает атомарного сохранения всех прежних полей при любой ошибке.
Для метода PHP.net пока даёт прежде всего сигнатуру. Описанные проверки, массив состояния и диагностируемый Exception дополнены чтением tagged source PHP 8.5.11 и запуском этих примеров. При сохранении длительного опыта запишите алгоритм, версию PHP, seed/снимок и последовательность операций; не считайте внутренний формат долговечным межъязыковым стандартом.
Продолжить: Random\Engine\Mt19937::__serialize, Random\Randomizer::__serialize.
Random\Engine\PcgOneseq128XslRr64::__constructНастроить начальную точку, чтобы повторять численный опыт с 128-битным состоянием и быстро пропускать шаги.
PHP 8.2+ · random
new Random\Engine\PcgOneseq128XslRr64(string|int|null $seed = null)Настроить начальную точку, чтобы повторять численный опыт с 128-битным состоянием и быстро пропускать шаги.
| Часть вызова | Договор |
|---|---|
| seed=null | Автоматически заполняет 16 байт состояния из CSPRNG. |
| seed=int | Состояние сначала 0, шаг, прибавление seed как unsigned 64-bit, ещё один шаг. |
| seed=string | Ровно 16 байт, трактуемых как одно little-endian unsigned 128-bit число. |
| Ошибки | ValueError при неверной длине string seed. Отказ CSPRNG при null-seed — Random\RandomException. Неверный тип в строгом вызове — TypeError. |
- PcgOneseq128XslRr64(41) создаётся дважды
- Объекты разные
- Первые generate возвращают одинаковые байты
new PcgOneseq128XslRr64(41) создаёт отдельный engine. В первом опыте два новых объекта получают один и тот же явный seed и выполняют одинаковую первую операцию; это проверка воспроизводимости, а не утверждение о непредсказуемости.
<?php
declare(strict_types=1);
$a = new Random\Engine\PcgOneseq128XslRr64(41);
$b = new Random\Engine\PcgOneseq128XslRr64(41);
echo $a !== $b ? "Разные объекты\n" : "Один объект\n";
echo $a->generate() === $b->generate() ? "Одинаковый старт\n" : "Разный старт\n";
Ожидаемый результат
Разные объекты
Одинаковый старт
Если один объект сделает лишний generate/getInt, дальнейшие ответы разойдутся. Размеры и порядок высокоуровневых запросов являются частью воспроизводимого сценария.
<?php
declare(strict_types=1);
$seed = str_repeat("\x01", 16);
$a = new Random\Engine\PcgOneseq128XslRr64($seed);
$b = new Random\Engine\PcgOneseq128XslRr64($seed);
echo $a->generate() === $b->generate() ? "Бинарный seed повторяет поток\n" : "Ошибка\n";
try { new Random\Engine\PcgOneseq128XslRr64('41'); }
catch (ValueError) { echo "Текст числа не заменяет бинарный seed\n"; }
Ожидаемый результат
Бинарный seed повторяет поток
Текст числа не заменяет бинарный seed
str_repeat создаёт здесь фиксированный учебный seed, не случайный секрет. int 41 выше и эти бинарные байты задают разные исходные состояния.
int и string — разные способы инициализации, не взаимозаменяемая десятичная запись. Строка "41" не означает integer 41: её длина не подходит бинарному состоянию. Для хранения бинарного seed текстом можно использовать hex и при восстановлении проверенный hex2bin; нужны 32 hex-символа для 16 байт. Выбранные байты сохраняйте вместе с алгоритмом и порядком операций.
Продолжить: Random\Engine\PcgOneseq128XslRr64::generate, Random\Randomizer::__construct.
Random\Engine\PcgOneseq128XslRr64::generateПолучить следующее слово воспроизводимого engine и продвинуть его состояние.
PHP 8.2+ · random
Random\Engine\PcgOneseq128XslRr64::generate(): stringПолучить следующее слово воспроизводимого engine и продвинуть его состояние.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Ровно 8 байта, unsigned 64-bit little-endian. Состояние продвигается на один шаг. |
| Граница | Это не криптографические байты. Для преобразования в диапазон используйте Randomizer, а не int cast или остаток %. |
- PcgOneseq128XslRr64(41)
- generate выдаёт 8 байта
- bin2hex показывает 455d859df4ac48ae
generate возвращает сырые 8 байта, представляющие unsigned 64-битное слово в little-endian. bin2hex делает каждый байт видимым, но порядок байт остаётся тем же: это не обычная шестнадцатеричная запись всего числового слова. Для нужного диапазона передайте engine в Randomizer.
<?php
declare(strict_types=1);
$engine = new Random\Engine\PcgOneseq128XslRr64(41);
$word = $engine->generate();
echo strlen($word), '/', bin2hex($word), "\n";
Ожидаемый результат
8/455d859df4ac48ae
Это точный первый ответ seed 41 в проверенной PHP 8.5.11, не вывод свежего секрета и не универсальный ответ вызова без seed. Сырые байты не нужно трактовать как UTF-8 или приводить string к int.
<?php
declare(strict_types=1);
$engine = new Random\Engine\PcgOneseq128XslRr64(41);
$engine->generate();
$copy = clone $engine;
echo $engine->generate() === $copy->generate() ? "Следующий шаг совпал\n" : "Ошибка\n";
Ожидаемый результат
Следующий шаг совпал
Копируется текущее состояние после уже сделанного шага. Это удобно для сравнения двух вариантов теста с одной стартовой точкой, но не создаёт два непересекающихся потока автоматически.
Продолжить: Random\Engine\PcgOneseq128XslRr64::__construct, Random\Randomizer::getInt, Random\Engine::generate.
Random\Engine\PcgOneseq128XslRr64::__debugInfoПонять диагностический снимок engine, не путая его со случайным результатом.
PHP 8.2+ · random
Random\Engine\PcgOneseq128XslRr64::__debugInfo(): arrayПонять диагностический снимок engine, не путая его со случайным результатом.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив внутреннего диагностического состояния. В PHP 8.5.11 содержит __states; это source-bounded деталь. |
| Граница | Просмотр не продвигает engine. Состояние не следует публиковать; устойчивый внешний формат документацией не обещан. |
- PcgOneseq128XslRr64 с seed 41
- __debugInfo читает состояние
- В выводе только факт наличия __states
__debugInfo — магический метод для диагностического представления, например при var_dump. Он возвращает массив сведений о состоянии, а не следующий случайный результат. Формат внутренних полей не стоит делать протоколом приложения; полный dump раскрывает состояние и делает будущую выдачу воспроизводимой.
<?php
declare(strict_types=1);
$engine = new Random\Engine\PcgOneseq128XslRr64(41);
$info = $engine->__debugInfo();
echo is_array($info) && isset($info['__states']) ? "Диагностическое состояние доступно\n" : "Иной формат\n";
Ожидаемый результат
Диагностическое состояние доступно
Имя __states здесь подтверждено source PHP 8.5.11, а не обещано как стабильный внешний формат PHP.net. Содержимое не печатается в общий журнал.
<?php
declare(strict_types=1);
$a = new Random\Engine\PcgOneseq128XslRr64(41);
$b = clone $a;
$a->__debugInfo();
echo $a->generate() === $b->generate() ? "Просмотр не сделал шаг\n" : "Состояние изменилось\n";
Ожидаемый результат
Просмотр не сделал шаг
Для диагностики проверьте версию, класс и явно выбранные параметры, не раскрывая всю серию. Отдельный generate изменил бы её; __debugInfo в этой реализации только читает состояние.
Продолжить: Random\Engine\PcgOneseq128XslRr64::__serialize, Random\Engine\PcgOneseq128XslRr64::generate.
Random\Engine\PcgOneseq128XslRr64::__serializeСохранить промежуточное состояние PcgOneseq128XslRr64 для продолжения несекретного опыта.
PHP 8.2+ · random
Random\Engine\PcgOneseq128XslRr64::__serialize(): arrayСохранить промежуточное состояние PcgOneseq128XslRr64 для продолжения несекретного опыта.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив снимка свойств/состояния; обычно обрабатывается serialize. |
| Ошибка / границы | Внутренний сериализатор может бросить Exception; формат не предлагается как стабильный внешний протокол. |
- PcgOneseq128XslRr64 seed 41 делает шаг
- __serialize фиксирует эту точку
- __unserialize передаёт её объекту с seed 7
- Следующие generate совпадают
Обычный serialize/unserialize вызывает эти методы автоматически. У native PRNG массив снимка содержит свойства и числовое состояние, поэтому сохранённая точка позволяет продолжить учебную серию. Не конструируйте внутренние поля вручную и не принимайте serialized-текст от посетителя.
<?php
declare(strict_types=1);
$a = new Random\Engine\PcgOneseq128XslRr64(41);
$a->generate();
$data = $a->__serialize();
$b = new Random\Engine\PcgOneseq128XslRr64(7);
$b->__unserialize($data);
echo $a->generate() === $b->generate() ? "Точка восстановлена\n" : "Другая точка\n";
Ожидаемый результат
Точка восстановлена
В data лежит собственный trusted снимок того же класса/версии. Исходный seed b=7 заменён сохранённым состоянием a. Метод __unserialize возвращает void; полезный результат — изменённое состояние b, а не новый объект.
<?php
declare(strict_types=1);
$a = new Random\Engine\PcgOneseq128XslRr64(41);
$saved = serialize($a);
$b = unserialize($saved, ['allowed_classes' => [Random\Engine\PcgOneseq128XslRr64::class]]);
echo $a !== $b && $a->generate() === $b->generate() ? "Отдельный объект продолжает ту же серию\n" : "Ошибка\n";
Ожидаемый результат
Отдельный объект продолжает ту же серию
Снимок сделан локально из известного объекта; allowed_classes дополнительно сужает допустимые классы, но не является разрешением парсить произвольные внешние serialized-данные. Совместимость между любыми версиями формата не обещается.
Для метода PHP.net пока даёт прежде всего сигнатуру. Описанные проверки, массив состояния и диагностируемый Exception дополнены чтением tagged source PHP 8.5.11 и запуском этих примеров. При сохранении длительного опыта запишите алгоритм, версию PHP, seed/снимок и последовательность операций; не считайте внутренний формат долговечным межъязыковым стандартом.
Продолжить: Random\Engine\PcgOneseq128XslRr64::__unserialize, Random\Randomizer::__serialize.
Random\Engine\PcgOneseq128XslRr64::__unserializeВосстановить промежуточное состояние PcgOneseq128XslRr64 для продолжения несекретного опыта.
PHP 8.2+ · random
Random\Engine\PcgOneseq128XslRr64::__unserialize(array $data): voidВосстановить промежуточное состояние PcgOneseq128XslRr64 для продолжения несекретного опыта.
| Часть вызова | Договор |
|---|---|
| data | Собственный массив __serialize этого же native-класса; не произвольные seed-параметры. |
| Возврат | void: изменяет состояние объекта. |
| Ошибка / границы | Exception при неверной структуре в PHP 8.5.11; не полагайтесь на объект после неудачной загрузки. |
- PcgOneseq128XslRr64 seed 41 делает шаг
- __serialize фиксирует эту точку
- __unserialize передаёт её объекту с seed 7
- Следующие generate совпадают
Обычный serialize/unserialize вызывает эти методы автоматически. У native PRNG массив снимка содержит свойства и числовое состояние, поэтому сохранённая точка позволяет продолжить учебную серию. Не конструируйте внутренние поля вручную и не принимайте serialized-текст от посетителя.
<?php
declare(strict_types=1);
$a = new Random\Engine\PcgOneseq128XslRr64(41);
$a->generate();
$data = $a->__serialize();
$b = new Random\Engine\PcgOneseq128XslRr64(7);
$b->__unserialize($data);
echo $a->generate() === $b->generate() ? "Точка восстановлена\n" : "Другая точка\n";
Ожидаемый результат
Точка восстановлена
В data лежит собственный trusted снимок того же класса/версии. Исходный seed b=7 заменён сохранённым состоянием a. Метод __unserialize возвращает void; полезный результат — изменённое состояние b, а не новый объект.
<?php
declare(strict_types=1);
$candidate = new Random\Engine\PcgOneseq128XslRr64(41);
try { $candidate->__unserialize([]); }
catch (Exception) { echo "Снимок отклонён\n"; }
Ожидаемый результат
Снимок отклонён
PHP 8.5.11 проверяет структуру и поля состояния и бросает Exception при неверных данных. После отказа восстановления отбросьте candidate: общий протокол не обещает атомарного сохранения всех прежних полей при любой ошибке.
Для метода PHP.net пока даёт прежде всего сигнатуру. Описанные проверки, массив состояния и диагностируемый Exception дополнены чтением tagged source PHP 8.5.11 и запуском этих примеров. При сохранении длительного опыта запишите алгоритм, версию PHP, seed/снимок и последовательность операций; не считайте внутренний формат долговечным межъязыковым стандартом.
Продолжить: Random\Engine\PcgOneseq128XslRr64::__serialize, Random\Randomizer::__serialize.
Random\Engine\Xoshiro256StarStar::__constructНастроить начальную точку, чтобы делить воспроизводимый опыт на неперекрывающиеся ограниченные потоки через jump.
PHP 8.2+ · random
new Random\Engine\Xoshiro256StarStar(string|int|null $seed = null)Настроить начальную точку, чтобы делить воспроизводимый опыт на неперекрывающиеся ограниченные потоки через jump.
| Часть вызова | Договор |
|---|---|
| seed=null | Автоматически заполняет 32 байт состояния из CSPRNG. |
| seed=int | Seed трактуется как unsigned 64-bit и запускает SplitMix64; четыре последовательных результата заполняют состояние. |
| seed=string | Ровно 32 байта, трактуемых как четыре little-endian unsigned 64-bit слова. Все 32 NUL одновременно запрещены. |
| Ошибки | ValueError при неверной длине string seed либо полностью нулевом string seed. Отказ CSPRNG при null-seed — Random\RandomException. Неверный тип в строгом вызове — TypeError. |
- Xoshiro256StarStar(41) создаётся дважды
- Объекты разные
- Первые generate возвращают одинаковые байты
new Xoshiro256StarStar(41) создаёт отдельный engine. В первом опыте два новых объекта получают один и тот же явный seed и выполняют одинаковую первую операцию; это проверка воспроизводимости, а не утверждение о непредсказуемости.
<?php
declare(strict_types=1);
$a = new Random\Engine\Xoshiro256StarStar(41);
$b = new Random\Engine\Xoshiro256StarStar(41);
echo $a !== $b ? "Разные объекты\n" : "Один объект\n";
echo $a->generate() === $b->generate() ? "Одинаковый старт\n" : "Разный старт\n";
Ожидаемый результат
Разные объекты
Одинаковый старт
Если один объект сделает лишний generate/getInt, дальнейшие ответы разойдутся. Размеры и порядок высокоуровневых запросов являются частью воспроизводимого сценария.
<?php
declare(strict_types=1);
$seed = str_repeat("\x01", 32);
$a = new Random\Engine\Xoshiro256StarStar($seed);
$b = new Random\Engine\Xoshiro256StarStar($seed);
echo $a->generate() === $b->generate() ? "Бинарный seed повторяет поток\n" : "Ошибка\n";
try { new Random\Engine\Xoshiro256StarStar('41'); }
catch (ValueError) { echo "Текст числа не заменяет бинарный seed\n"; }
Ожидаемый результат
Бинарный seed повторяет поток
Текст числа не заменяет бинарный seed
str_repeat создаёт здесь фиксированный учебный seed, не случайный секрет. int 41 выше и эти бинарные байты задают разные исходные состояния.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Xoshiro256StarStar(0);
echo strlen($engine->generate()), " bytes\n";
try { new Random\Engine\Xoshiro256StarStar(str_repeat("\0", 32)); }
catch (ValueError) { echo "Нулевое состояние запрещено\n"; }
Ожидаемый результат
8 bytes
Нулевое состояние запрещено
Числовой seed 0 сначала преобразуется SplitMix64; это не то же, что 32 нулевых байта готового состояния.
int и string — разные способы инициализации, не взаимозаменяемая десятичная запись. Строка "41" не означает integer 41: её длина не подходит бинарному состоянию. Для хранения бинарного seed текстом можно использовать hex и при восстановлении проверенный hex2bin; нужны 64 hex-символа для 32 байт. Выбранные байты сохраняйте вместе с алгоритмом и порядком операций.
Продолжить: Random\Engine\Xoshiro256StarStar::generate, Random\Randomizer::__construct.
Random\Engine\Xoshiro256StarStar::generateПолучить следующее слово воспроизводимого engine и продвинуть его состояние.
PHP 8.2+ · random
Random\Engine\Xoshiro256StarStar::generate(): stringПолучить следующее слово воспроизводимого engine и продвинуть его состояние.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Ровно 8 байта, unsigned 64-bit little-endian. Состояние продвигается на один шаг. |
| Граница | Это не криптографические байты. Для преобразования в диапазон используйте Randomizer, а не int cast или остаток %. |
- Xoshiro256StarStar(41)
- generate выдаёт 8 байта
- bin2hex показывает 6e320bcb93748ab7
generate возвращает сырые 8 байта, представляющие unsigned 64-битное слово в little-endian. bin2hex делает каждый байт видимым, но порядок байт остаётся тем же: это не обычная шестнадцатеричная запись всего числового слова. Для нужного диапазона передайте engine в Randomizer.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Xoshiro256StarStar(41);
$word = $engine->generate();
echo strlen($word), '/', bin2hex($word), "\n";
Ожидаемый результат
8/6e320bcb93748ab7
Это точный первый ответ seed 41 в проверенной PHP 8.5.11, не вывод свежего секрета и не универсальный ответ вызова без seed. Сырые байты не нужно трактовать как UTF-8 или приводить string к int.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Xoshiro256StarStar(41);
$engine->generate();
$copy = clone $engine;
echo $engine->generate() === $copy->generate() ? "Следующий шаг совпал\n" : "Ошибка\n";
Ожидаемый результат
Следующий шаг совпал
Копируется текущее состояние после уже сделанного шага. Это удобно для сравнения двух вариантов теста с одной стартовой точкой, но не создаёт два непересекающихся потока автоматически.
Продолжить: Random\Engine\Xoshiro256StarStar::__construct, Random\Randomizer::getInt, Random\Engine::generate.
Random\Engine\Xoshiro256StarStar::__debugInfoПонять диагностический снимок engine, не путая его со случайным результатом.
PHP 8.2+ · random
Random\Engine\Xoshiro256StarStar::__debugInfo(): arrayПонять диагностический снимок engine, не путая его со случайным результатом.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив внутреннего диагностического состояния. В PHP 8.5.11 содержит __states; это source-bounded деталь. |
| Граница | Просмотр не продвигает engine. Состояние не следует публиковать; устойчивый внешний формат документацией не обещан. |
- Xoshiro256StarStar с seed 41
- __debugInfo читает состояние
- В выводе только факт наличия __states
__debugInfo — магический метод для диагностического представления, например при var_dump. Он возвращает массив сведений о состоянии, а не следующий случайный результат. Формат внутренних полей не стоит делать протоколом приложения; полный dump раскрывает состояние и делает будущую выдачу воспроизводимой.
<?php
declare(strict_types=1);
$engine = new Random\Engine\Xoshiro256StarStar(41);
$info = $engine->__debugInfo();
echo is_array($info) && isset($info['__states']) ? "Диагностическое состояние доступно\n" : "Иной формат\n";
Ожидаемый результат
Диагностическое состояние доступно
Имя __states здесь подтверждено source PHP 8.5.11, а не обещано как стабильный внешний формат PHP.net. Содержимое не печатается в общий журнал.
<?php
declare(strict_types=1);
$a = new Random\Engine\Xoshiro256StarStar(41);
$b = clone $a;
$a->__debugInfo();
echo $a->generate() === $b->generate() ? "Просмотр не сделал шаг\n" : "Состояние изменилось\n";
Ожидаемый результат
Просмотр не сделал шаг
Для диагностики проверьте версию, класс и явно выбранные параметры, не раскрывая всю серию. Отдельный generate изменил бы её; __debugInfo в этой реализации только читает состояние.
Продолжить: Random\Engine\Xoshiro256StarStar::__serialize, Random\Engine\Xoshiro256StarStar::generate.
Random\Engine\Xoshiro256StarStar::__serializeСохранить промежуточное состояние Xoshiro256StarStar для продолжения несекретного опыта.
PHP 8.2+ · random
Random\Engine\Xoshiro256StarStar::__serialize(): arrayСохранить промежуточное состояние Xoshiro256StarStar для продолжения несекретного опыта.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. |
| Возврат | Массив снимка свойств/состояния; обычно обрабатывается serialize. |
| Ошибка / границы | Внутренний сериализатор может бросить Exception; формат не предлагается как стабильный внешний протокол. |
- Xoshiro256StarStar seed 41 делает шаг
- __serialize фиксирует эту точку
- __unserialize передаёт её объекту с seed 7
- Следующие generate совпадают
Обычный serialize/unserialize вызывает эти методы автоматически. У native PRNG массив снимка содержит свойства и числовое состояние, поэтому сохранённая точка позволяет продолжить учебную серию. Не конструируйте внутренние поля вручную и не принимайте serialized-текст от посетителя.
<?php
declare(strict_types=1);
$a = new Random\Engine\Xoshiro256StarStar(41);
$a->generate();
$data = $a->__serialize();
$b = new Random\Engine\Xoshiro256StarStar(7);
$b->__unserialize($data);
echo $a->generate() === $b->generate() ? "Точка восстановлена\n" : "Другая точка\n";
Ожидаемый результат
Точка восстановлена
В data лежит собственный trusted снимок того же класса/версии. Исходный seed b=7 заменён сохранённым состоянием a. Метод __unserialize возвращает void; полезный результат — изменённое состояние b, а не новый объект.
<?php
declare(strict_types=1);
$a = new Random\Engine\Xoshiro256StarStar(41);
$saved = serialize($a);
$b = unserialize($saved, ['allowed_classes' => [Random\Engine\Xoshiro256StarStar::class]]);
echo $a !== $b && $a->generate() === $b->generate() ? "Отдельный объект продолжает ту же серию\n" : "Ошибка\n";
Ожидаемый результат
Отдельный объект продолжает ту же серию
Снимок сделан локально из известного объекта; allowed_classes дополнительно сужает допустимые классы, но не является разрешением парсить произвольные внешние serialized-данные. Совместимость между любыми версиями формата не обещается.
Для метода PHP.net пока даёт прежде всего сигнатуру. Описанные проверки, массив состояния и диагностируемый Exception дополнены чтением tagged source PHP 8.5.11 и запуском этих примеров. При сохранении длительного опыта запишите алгоритм, версию PHP, seed/снимок и последовательность операций; не считайте внутренний формат долговечным межъязыковым стандартом.
Продолжить: Random\Engine\Xoshiro256StarStar::__unserialize, Random\Randomizer::__serialize.
Random\Engine\Xoshiro256StarStar::__unserializeВосстановить промежуточное состояние Xoshiro256StarStar для продолжения несекретного опыта.
PHP 8.2+ · random
Random\Engine\Xoshiro256StarStar::__unserialize(array $data): voidВосстановить промежуточное состояние Xoshiro256StarStar для продолжения несекретного опыта.
| Часть вызова | Договор |
|---|---|
| data | Собственный массив __serialize этого же native-класса; не произвольные seed-параметры. |
| Возврат | void: изменяет состояние объекта. |
| Ошибка / границы | Exception при неверной структуре в PHP 8.5.11; не полагайтесь на объект после неудачной загрузки. |
- Xoshiro256StarStar seed 41 делает шаг
- __serialize фиксирует эту точку
- __unserialize передаёт её объекту с seed 7
- Следующие generate совпадают
Обычный serialize/unserialize вызывает эти методы автоматически. У native PRNG массив снимка содержит свойства и числовое состояние, поэтому сохранённая точка позволяет продолжить учебную серию. Не конструируйте внутренние поля вручную и не принимайте serialized-текст от посетителя.
<?php
declare(strict_types=1);
$a = new Random\Engine\Xoshiro256StarStar(41);
$a->generate();
$data = $a->__serialize();
$b = new Random\Engine\Xoshiro256StarStar(7);
$b->__unserialize($data);
echo $a->generate() === $b->generate() ? "Точка восстановлена\n" : "Другая точка\n";
Ожидаемый результат
Точка восстановлена
В data лежит собственный trusted снимок того же класса/версии. Исходный seed b=7 заменён сохранённым состоянием a. Метод __unserialize возвращает void; полезный результат — изменённое состояние b, а не новый объект.
<?php
declare(strict_types=1);
$candidate = new Random\Engine\Xoshiro256StarStar(41);
try { $candidate->__unserialize([]); }
catch (Exception) { echo "Снимок отклонён\n"; }
Ожидаемый результат
Снимок отклонён
PHP 8.5.11 проверяет структуру и поля состояния и бросает Exception при неверных данных. После отказа восстановления отбросьте candidate: общий протокол не обещает атомарного сохранения всех прежних полей при любой ошибке.
Для метода PHP.net пока даёт прежде всего сигнатуру. Описанные проверки, массив состояния и диагностируемый Exception дополнены чтением tagged source PHP 8.5.11 и запуском этих примеров. При сохранении длительного опыта запишите алгоритм, версию PHP, seed/снимок и последовательность операций; не считайте внутренний формат долговечным межъязыковым стандартом.
Продолжить: Random\Engine\Xoshiro256StarStar::__serialize, Random\Randomizer::__serialize.
Random\Engine\PcgOneseq128XslRr64::jumpПерейти к нужному шагу воспроизводимого PCG-опыта, не вычисляя каждый пропущенный ответ.
PHP 8.2+ · random
Random\Engine\PcgOneseq128XslRr64::jump(int $advance): voidПерейти к нужному шагу воспроизводимого PCG-опыта, не вычисляя каждый пропущенный ответ.
| Часть вызова | Договор |
|---|---|
| advance | Целое ≥0, количество пропускаемых шагов generate. Максимум ограничен PHP int. |
| Возврат | void; изменяет состояние. jump(0) его сохраняет. |
| Ошибка | ValueError при отрицательном advance. |
- fast.jump(5)
- slow.generate повторён 5 раз
- Следующие слова одинаковы
jump(5) продвигает состояние ровно так, как пять последовательных generate, но не возвращает и не хранит пропущенные значения. Это полезно при воспроизведении теста начиная с известного номера шага. Число измеряется шагами engine, а не вызовами getInt: высокоуровневой операции может понадобиться несколько шагов.
<?php
declare(strict_types=1);
$fast = new Random\Engine\PcgOneseq128XslRr64(41);
$slow = clone $fast;
$fast->jump(5);
for ($i = 0; $i < 5; ++$i) { $slow->generate(); }
echo $fast->generate() === $slow->generate() ? "Одинаковая позиция 5\n" : "Ошибка\n";
Ожидаемый результат
Одинаковая позиция 5
Всего выполняются пять обычных шагов, не огромный benchmark. Ускоренный переход меняет только состояние, а следующий generate уже читает результат после пропуска.
<?php
declare(strict_types=1);
$a = new Random\Engine\PcgOneseq128XslRr64(41);
$b = clone $a;
$a->jump(0);
echo $a->generate() === $b->generate() ? "Ноль не продвинул поток\n" : "Ошибка\n";
try { $a->jump(-1); }
catch (ValueError) { echo "Назад jump не ходит\n"; }
Ожидаемый результат
Ноль не продвинул поток
Назад jump не ходит
advance — не отрицательное смещение и не отдельный seed. При очень длинном опыте согласуйте разбиение на шаги и не выдавайте такой PRNG за независимые криптографические источники.
Продолжить: Random\Engine\PcgOneseq128XslRr64::generate, Random\Engine\Xoshiro256StarStar::jump.
Random\Engine\Xoshiro256StarStar::jumpВыделить воспроизводимый блок Xoshiro-последовательности без перебора всех предшествующих шагов.
PHP 8.2+ · random
Random\Engine\Xoshiro256StarStar::jump(): voidВыделить воспроизводимый блок Xoshiro-последовательности без перебора всех предшествующих шагов.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. Длина перехода фиксирована: 2^128 шагов generate. |
| Возврат | void; изменяет состояние engine. |
| Граница | Разделение блоков имеет ограниченную длину и не гарантирует уникальность каждого значения; не создаёт процессы или CSPRNG. |
- Два clone исходного seed 41
- Обе копии вызывают jump
- Следующий generate совпадает
jump() выполняет специальный переход состояния, эквивалентный 2^128 вызовам generate. Он не запускает такой огромный цикл. Исходный engine можно использовать как шаблон, клонировать и затем переходить к следующему блоку, чтобы заранее распределить опыт по потокам.
<?php
declare(strict_types=1);
$template = new Random\Engine\Xoshiro256StarStar(41);
$a = clone $template;
$b = clone $template;
$a->jump();
$b->jump();
echo $a->generate() === $b->generate() ? "Одинаковый переход воспроизводится\n" : "Ошибка\n";
Ожидаемый результат
Одинаковый переход воспроизводится
Две копии делают один и тот же переход, поэтому повторяют друг друга. Чтобы раздать разные блоки, нужно последовательно продвигать шаблон между клонированиями, как в следующем опыте.
<?php
declare(strict_types=1);
$template = new Random\Engine\Xoshiro256StarStar(41);
$first = clone $template;
$template->jump();
$second = clone $template;
echo $first->__serialize() !== $second->__serialize() ? "Разные стартовые состояния\n" : "Ошибка\n";
Ожидаемый результат
Разные стартовые состояния
Сравниваются состояния, а не два случайных значения, которые вправе совпасть. Математическое непересечение блоков не означает, что числовые ответы разных шагов не могут повторяться.
jump позволяет отвести 2^128 блоков длиной 2^128 шагов. Эти пределы относятся к разбиению одной последовательности по алгоритму: не превышайте назначенную длину блока и не делайте повторный полный обход шаблона. Это не запуск параллельных PHP-процессов; планирование работы остаётся задачей приложения. Алгоритм по-прежнему не криптографический.
Продолжить: Random\Engine\Xoshiro256StarStar::generate, Random\Engine\Xoshiro256StarStar::jumpLong.
Random\Engine\Xoshiro256StarStar::jumpLongВыделить воспроизводимый блок Xoshiro-последовательности без перебора всех предшествующих шагов.
PHP 8.2+ · random
Random\Engine\Xoshiro256StarStar::jumpLong(): voidВыделить воспроизводимый блок Xoshiro-последовательности без перебора всех предшествующих шагов.
| Часть вызова | Договор |
|---|---|
| Аргументы | Нет. Длина перехода фиксирована: 2^192 шагов generate. |
| Возврат | void; изменяет состояние engine. |
| Граница | Разделение блоков имеет ограниченную длину и не гарантирует уникальность каждого значения; не создаёт процессы или CSPRNG. |
- Два clone исходного seed 41
- Обе копии вызывают jumpLong
- Следующий generate совпадает
jumpLong() выполняет специальный переход состояния, эквивалентный 2^192 вызовам generate. Он не запускает такой огромный цикл. Исходный engine можно использовать как шаблон, клонировать и затем переходить к следующему блоку, чтобы заранее распределить опыт по потокам.
<?php
declare(strict_types=1);
$template = new Random\Engine\Xoshiro256StarStar(41);
$a = clone $template;
$b = clone $template;
$a->jumpLong();
$b->jumpLong();
echo $a->generate() === $b->generate() ? "Одинаковый переход воспроизводится\n" : "Ошибка\n";
Ожидаемый результат
Одинаковый переход воспроизводится
Две копии делают один и тот же переход, поэтому повторяют друг друга. Чтобы раздать разные блоки, нужно последовательно продвигать шаблон между клонированиями, как в следующем опыте.
<?php
declare(strict_types=1);
$template = new Random\Engine\Xoshiro256StarStar(41);
$first = clone $template;
$template->jumpLong();
$second = clone $template;
echo $first->__serialize() !== $second->__serialize() ? "Разные стартовые состояния\n" : "Ошибка\n";
Ожидаемый результат
Разные стартовые состояния
Сравниваются состояния, а не два случайных значения, которые вправе совпасть. Математическое непересечение блоков не означает, что числовые ответы разных шагов не могут повторяться.
jumpLong отводит 2^64 больших блоков по 2^192 шагов. Внутри каждого можно применять jump, разделяя его ещё на 2^64 блоков по 2^128 шагов. Эти пределы относятся к разбиению одной последовательности по алгоритму: не превышайте назначенную длину блока и не делайте повторный полный обход шаблона. Это не запуск параллельных PHP-процессов; планирование работы остаётся задачей приложения. Алгоритм по-прежнему не криптографический.
Продолжить: Random\Engine\Xoshiro256StarStar::generate, Random\Engine\Xoshiro256StarStar::jump.
Источники и обсуждения
- PHP: book.random
- PHP: class.random-brokenrandomengineerror
- PHP: class.random-cryptosafeengine
- PHP: class.random-engine
- PHP: class.random-engine-mt19937
- PHP: class.random-engine-pcgoneseq128xslrr64
- PHP: class.random-engine-secure
- PHP: class.random-engine-xoshiro256starstar
- PHP: class.random-randomerror
- PHP: class.random-randomexception
- PHP: class.random-randomizer
- PHP: enum.random-intervalboundary
- PHP: random.constants
- PHP: random.examples
- PHP: ref.random
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


