Александр Некрасов · Автономное руководство

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

Разделы этой страницы
  1. Случайные данные: полный справочник Random
  2. Зачем нужна случайность: первый вызов без объектов
  3. Выбрать свойства результата до выбора API
  4. Randomizer: источник отдельно, нужная операция отдельно
  5. Seed, состояние, общий объект и воспроизведение
  6. Engine: низкоуровневый договор байтов
  7. CryptoSafeEngine: смысл и пределы маркера
  8. Secure и системные источники: что гарантирует платформа
  9. Mt19937: назначение и состояние
  10. PcgOneseq128XslRr64: назначение и состояние
  11. Xoshiro256StarStar: назначение и состояние
  12. Включить или исключить края: IntervalBoundary
  13. Почему дробный диапазон нельзя просто растянуть
  14. Оба MT-режима и их версии
  15. Три класса отказов и унаследованная диагностика
  16. Полный пример: подготовить учебный вариант
  17. Все procedural-функции: безопасный API и старый общий поток
  18. Функции и методы этой темы
  19. Код и руководство с собой
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 91 · С нуля → полный справочник

Случайные данные: полный справочник Random

От выбора одной грани до секретных байтов, воспроизводимого состояния, float-интервалов и всех функций, методов, интерфейсов и отказов Random.

Зачем нужна случайность: первый вызов без объектов

Наглядная модель

Задача определяет источник случайности

Повторяемая симуляция и секретный токен требуют разных свойств генератора.

  1. Выберите задачу

    Тест должен повторяться; токен должен быть непредсказуемым.

  2. Engine

    Secure для криптографической случайности; подходящий seedable engine для воспроизводимых экспериментов.

  3. Randomizer

    Получает целые, байты, значения диапазона или перестановки по конкретному контракту.

  4. Проверка приложения

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

Фиксированный seed помогает тесту, но делает последовательность предсказуемой. Такой генератор нельзя переносить в секретные токены.

Иногда программа должна выбрать вариант, который мы заранее не назначили: грань кубика, порядок учебных вопросов или байты одноразового секрета. Случайный результат может повториться. «Случайный» не означает «новый для базы», а одинаковый шанс каждого допустимого результата не означает идеально ровные частоты в короткой серии.

Начнём без собственных функций и объектов. random_int(1, 6) просит целое от 1 до 6 включительно. Знак $ начинает имя переменной, = сохраняет ответ в ней, круглые скобки содержат два аргумента, запятая разделяет их, ; завершает команду. Следующие две строки проверяют границы, чтобы ожидаемый учебный вывод не зависел от выпавшей грани.

Кубик: диапазон → один ответ → проверка
  1. Передали min=1 и max=6
  2. random_int выбрал одну из шести граней
  3. Сохранили её в $face
  4. Проверили, что грань находится между 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 выбирают диапазон с нужным договором, отбрасывая неподходящие значения при необходимости.

Все байты → остаток при делении на 6
  1. Есть 256 исходных вариантов 0…255
  2. 256 = 42×6 + 4
  3. Первые четыре остатка получают лишний вариант
  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.
Источник → операция → диапазон
  1. new Randomizer выбирает Secure
  2. getInt получает от источника байты
  3. Алгоритм сопоставляет им целое 10…20
  4. В приложении проверяем свойство результата
Тот же выбор через объект с безопасным источникомСамостоятельный пример
<?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) не обязаны потребить состояние одинаково.

Два самостоятельных опыта
  1. Два Mt19937 получают seed 41
  2. Первое getInt совпадает
  3. Один объект делает лишний запрос
  4. Его состояние отличается от второго
Почему одинаковый seed ещё не означает одинаковую текущую точкуСамостоятельный пример
<?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 рассмотрены ниже; собственный алгоритм не стоит изобретать для секретов.

Договор источника → договор потребителя
  1. Engine.generate возвращает байты
  2. Randomizer потребляет до 8 байт слова
  3. getInt/getBytes/... выполняет нужную операцию
  4. При неисправном источнике обработчик получает ошибку

Короткие слова могут объединяться; байты после первых восьми в одном слове Randomizer игнорирует. Числовые слова кодируются unsigned little-endian: младший байт первым. Все биты должны быть равномерными и независимыми по договору качества; сигнатура PHP не может это доказать. Конкретный конечный fixture, байтовый порядок и отказ зависимости разобраны в Random\Engine::generate. До собственного класса повторите implements и интерфейс.

CryptoSafeEngine: смысл и пределы маркера

CryptoSafeEngine — marker interface: он extends Engine, наследует generate и не добавляет метода. Маркер сообщает намерение реализации выдавать криптографически стойкую случайность. Он не проводит испытание алгоритма: пользовательский класс может ошибочно заявить этот интерфейс. Для встроенного проверенного пути выбирайте Secure; наличие instanceof у произвольной зависимости — не аудит её безопасности.

Проверка зависимости → источник Secure → длина
  1. Принимаем CryptoSafeEngine
  2. Передаём встроенный Secure
  3. Randomizer получает ровно 16 байт
  4. Выводим только число байт
Выразить требование к зависимости в функцииСамостоятельный пример
<?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 → 24 байта
  1. Secure выбирает системный источник
  2. Randomizer просит заданную длину
  3. Длина проверяется без вывода содержимого
  4. При отказе генерации операция не продолжается с rand

Ниже порядок источников из официального договора random_bytes/random_int. Переход к следующему источнику относится к внутренней платформенной реализации PHP; это не рекомендация приложению читать /dev/urandom самостоятельно или создавать запасной псевдогенератор.

ОС и версия PHPИсточники в порядке приоритета
Linuxgetrandom() → /dev/urandom
FreeBSD ≥12, PHP ≥7.3getrandom() → /dev/urandom
Windows, PHP ≥7.2CNG API; более старый PHP использовал CryptGenRandom
macOS, PHP ≥8.2 / ≥8.1.9 / ≥8.0.22CCRandomGenerateBytes(), если доступен при компиляции
macOS, PHP ≥8.1 / ≥8.0.2arc4random_buf() → /dev/urandom
NetBSD ≥7 или OpenBSD ≥5.5; PHP ≥7.1 / ≥7.0.1arc4random_buf() → /dev/urandom
DragonFly BSD / Solaris, PHP ≥8.1getrandom() → /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.

Отдельный объект → отдельное состояние
  1. new Mt19937(41) создаёт учебный поток
  2. generate выдаёт 4 байта и продвигает его
  3. clone копирует текущую точку для сравнения
  4. В реальной операции 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, не источник секретов.

Отдельный объект → отдельное состояние
  1. new PcgOneseq128XslRr64(41) создаёт учебный поток
  2. generate выдаёт 8 байта и продвигает его
  3. clone копирует текущую точку для сравнения
  4. В реальной операции 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 не означают криптографическую стойкость.

Отдельный объект → отдельное состояние
  1. new Xoshiro256StarStar(41) создаёт учебный поток
  2. generate выдаёт 8 байта и продвигает его
  3. clone копирует текущую точку для сравнения
  4. В реальной операции 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.

Два края → четыре договора
  1. ClosedOpen: нижний да, верхний нет
  2. ClosedClosed: оба да
  3. OpenClosed: нижний нет, верхний да
  4. 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 — сетка, которая становится реже при увеличении абсолютного значения. Поэтому одинаковый шаг по битовым представлениям и одинаковый шаг по числовой прямой — разные требования.

Упрощённая сетка, не реальная точность PHP
  1. Воображаемый формат: шаг 0.25 между 1 и 2
  2. Между 2 и 4 шаг уже 0.5
  3. Для [1.25,3.0) берём редкий шаг 0.5 от края 3.0
  4. Кандидаты: 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 чтение устаревшей константы также диагностируется. Для новой задачи не выбирать.
Именованный режим → engine → слово
  1. Mt19937 получает seed 41
  2. Указан MT_RAND_MT19937
  3. Проверяем ожидаемые 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.
Пустой fixture → нарушение договора → безопасный отказ
  1. Engine.generate возвращает пустую строку
  2. Randomizer не выдаёт байты пользователю
  3. BrokenRandomEngineError перехвачен
  4. Операция завершается без запасного токена
Поймать реальную ошибку заведомо неисправного fixtureСамостоятельный пример
<?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-код из явно указанного алфавита. Ни выбранные значения, ни код не объявляются уникальными между запусками.

Каталог → ключи → тексты → вариант
  1. Каталог из Q1/Q2/Q3
  2. pickArrayKeys выбирает два различных ключа
  3. По ключам получаем два русских текста
  4. shuffleArray переставляет значения, не байты текста
  5. Создаём 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 Twistermt_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. Переданы границы 1 и 12
  2. Получен int из закрытого диапазона
  3. Отдельный вызов с [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

Это проверка диапазона, не статистический тест качества. Выбранное число может повториться; равномерный выбор с возвращением не является выбором без повторов.

Повторить несекретный тест с тем же seedСамостоятельный пример
<?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. Переданы границы 1 и 12
  2. Получен int из закрытого диапазона
  3. Отдельный вызов с [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

Это проверка диапазона, не статистический тест качества. Выбранное число может повториться; равномерный выбор с возвращением не является выбором без повторов.

Повторить несекретный тест с тем же seedСамостоятельный пример
<?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

Воспроизвести старый несекретный сценарий, управляя началом общего генератора.

Часть вызоваДоговор
seedint, трактуемый как unsigned 32-bit; null/пропуск — случайный seed. Явный null с PHP 8.3.
modeMT_RAND_MT19937 — правильный алгоритм; MT_RAND_PHP — deprecated старый вариант.
Возвратvoid: полезный эффект — изменение состояния, а не новое число.
ОшибкиНеверный тип в strict_types — TypeError. Намеренное использование старого режима сигналит E_DEPRECATED; поведение неизвестного int не следует переносить на класс.
Seed → две одинаковые серии
  1. Устанавливаем seed 17
  2. Два вызова mt_rand дают серию a
  3. Снова seed 17 и те же два вызова
  4. Серия 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

Воспроизвести старый несекретный сценарий, управляя началом общего генератора.

Часть вызоваДоговор
seedint, трактуемый как unsigned 32-bit; null/пропуск — случайный seed. Явный null с PHP 8.3.
modeMT_RAND_MT19937 — правильный алгоритм; MT_RAND_PHP — deprecated старый вариант.
Возвратvoid: полезный эффект — изменение состояния, а не новое число.
ОшибкиНеверный тип в strict_types — TypeError. Намеренное использование старого режима сигналит E_DEPRECATED; поведение неизвестного int не следует переносить на класс.
Seed → две одинаковые серии
  1. Устанавливаем seed 17
  2. Два вызова mt_rand дают серию a
  3. Снова seed 17 и те же два вызова
  4. Серия 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 и не продвигает последовательность.
Предел → контроль образца
  1. mt_getrandmax() сообщает 2147483647
  2. mt_rand() получает один образец
  3. Проверяем 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 и не продвигает последовательность.
Предел → контроль образца
  1. getrandmax() сообщает 2147483647
  2. rand() получает один образец
  3. Проверяем 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. Не криптографический источник.
Старый вызов → миграция
  1. lcg_value получает дробь
  2. Наблюдаем E_DEPRECATED
  3. Новый 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.
Источник → инструмент выбора
  1. new Randomizer() создаёт Secure
  2. getInt(1,6) запрашивает число
  3. Возвращается 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 ставит границы поверх выдачи выбранного источника.

Один engine в двух обёртках остаётся одним объектомСамостоятельный пример
<?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 для секретов.
Диапазон тестового остатка
  1. getInt(10,20)
  2. Возвращается один из 11 int
  3. 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 для секретов.
12 байт → 24 символа hex
  1. getBytes(12) возвращает бинарную строку
  2. strlen=12
  3. bin2hex удваивает длину, не добавляя случайности

getBytes(12) просит 12 байт; это не строка из 12 букв. Она может содержать нулевые байты и неправильный UTF-8. Для подписываемого протокола нужны исходные байты, для текстового поля — их обратимое кодирование.

Подготовить короткий бинарный образец и его hex-формуСамостоятельный пример
<?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, тот же метод будет воспроизводимым, но не безопасным для секретов. Значение не печатается.

Граница запроса может влиять на потребление engineСамостоятельный пример
<?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 для секретов.
Алфавит → печатный код
  1. ASCII-алфавит без I/O/0/1
  2. Выбираем 10 позиций с возвращением
  3. Проверяем длину и принадлежность алфавиту

В 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. Это удобство чтения, не гарантия уникальности; достаточную длину выбирают для конкретного протокола.

Байтовый контракт не является Unicode-контрактомСамостоятельный пример
<?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 для секретов.
Словарь → ключ → товар
  1. BR41/NT20/WS08 — три кандидата
  2. num=1 даёт список из одного ключа
  3. Этот ключ существует в 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 для секретов.
Порядок меняется, состав остаётся
  1. Ключи исходника 10/20/30
  2. Новый результат получает 0/1/2
  3. Сверяем те же три значения независимо от случайного порядка

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]
Состав сохранён

Порядок случайный, состав детерминированно проверяется после сортировки копий. Не требуется, чтобы порядок обязательно изменился: исходная перестановка тоже допустима.

Сохранить ID внутри перемешиваемой записиСамостоятельный пример
<?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 для секретов.
AABC → перестановка байтов
  1. Четыре позиции, две содержат A
  2. shuffleBytes меняет порядок
  3. Сверяем количество каждого байта, исходник AABC прежний

shuffleBytes переставляет отдельные байты строки. Для ASCII кодов A/B/C каждый байт совпадает с символом. Для русского текста одна буква занимает несколько байт, а составной видимый символ может включать несколько Unicode code points: байтовое перемешивание не подходит.

Перемешать ASCII-набор без потерьСамостоятельный пример
<?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 для секретов.
Слово MT → неотрицательное int
  1. Mt19937 генерирует 32 бита
  2. nextInt использует 31 бит
  3. Проверяем 0…2147483647

nextInt получает одно слово engine и сдвигает его вправо на один бит, чтобы вернуть неотрицательное целое. Ноль возможен: термин positive в кратком английском описании не исключает 0. Граница зависит от количества выданных байт, поэтому это не удобная замена getInt(min,max).

Увидеть предел 32-битного engineСамостоятельный пример
<?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 бит. Метод не делит результат по вашему бизнес-диапазону.

Ноль входит в договор: детерминированный тестовый engineСамостоятельный пример
<?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 не поддерживает сериализацию.
Снимок → продолжение серии
  1. MT seed 41 уже сделал один шаг
  2. serialize сохраняет текущую точку
  3. unserialize создаёт отдельный объект
  4. Следующие 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. Для обмена данными нужен отдельный безопасный протокол.

Secure нельзя сохранить для повторенияСамостоятельный пример
<?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; типы/свойства также проверяются.
Снимок → продолжение серии
  1. MT seed 41 уже сделал один шаг
  2. serialize сохраняет текущую точку
  3. unserialize создаёт отдельный объект
  4. Следующие 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.
boundaryEnum-case ClosedOpen по умолчанию; четыре режима включения краёв разобраны отдельно.
ВозвратРавномерно выбранный float из документированного набора внутри заданных границ. Закрытые края являются кандидатами.
ОшибкиValueError для не-finite границы либо пустого множества значений. Исключение/ошибка из Engine::generate передаётся вызывающему коду. Пустой ответ или неисправная выдача движка могут привести к Random\BrokenRandomEngineError; отказ источника Secure — к Random\RandomException. Это не повод переходить к rand для секретов.
Диапазон тестовой длительности
  1. Переданы 0.5 и 1.5
  2. ClosedOpen включает 0.5 и исключает 1.5
  3. Проверяем 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 для секретов.
Единичная шкала → решение
  1. Получен sample из [0,1)
  2. Условие sample<0.25 выделяет четверть шкалы
  3. Результат сравнения — 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.
Слово → байты → потребитель
  1. Fixture выдаёт 0x12345678 как 78563412
  2. Позиция переходит к 0x1234567a
  3. 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, если доступные системные источники не дали случайность. Источники и версии описаны в общей таблице главы.
ОС → слово → измерение
  1. Secure читает CSPRNG ОС
  2. generate возвращает бинарное слово
  3. Сверяем длину с 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 универсальным значением. Никакое содержимое байт не попадает в вывод.

Запросить нужную длину через высокоуровневый APIСамостоятельный пример
<?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 с отдельным состоянием.

Часть вызоваДоговор
seedint или null; int трактуется как unsigned 32-bit. null/пропуск заполняет seed из CSPRNG.
modeMT_RAND_MT19937 — правильный алгоритм. MT_RAND_PHP — ошибочный режим совместимости до PHP 7.1, deprecated с 8.3.
ОшибкиValueError при неизвестном mode; E_DEPRECATED у старого режима. Отказ CSPRNG при null-seed — Random\RandomException. Неверный тип в strict_types — TypeError.
Одинаковый seed → повторяемый старт
  1. Mt19937(41) создаётся дважды
  2. Объекты разные
  3. Первые 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, дальнейшие ответы разойдутся. Размеры и порядок высокоуровневых запросов являются частью воспроизводимого сценария.

Проверить особенность seed и границу аргументаСамостоятельный пример
<?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 или остаток %.
Seed 41 → первое слово
  1. Mt19937(41)
  2. generate выдаёт 4 байта
  3. 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.

Зафиксировать текущую точку через cloneСамостоятельный пример
<?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. Состояние не следует публиковать; устойчивый внешний формат документацией не обещан.
Engine → диагностический массив
  1. Mt19937 с seed 41
  2. __debugInfo читает состояние
  3. В выводе только факт наличия __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; формат не предлагается как стабильный внешний протокол.
Снимок → восстановленный следующий шаг
  1. Mt19937 seed 41 делает шаг
  2. __serialize фиксирует эту точку
  3. __unserialize передаёт её объекту с seed 7
  4. Следующие 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; не полагайтесь на объект после неудачной загрузки.
Снимок → восстановленный следующий шаг
  1. Mt19937 seed 41 делает шаг
  2. __serialize фиксирует эту точку
  3. __unserialize передаёт её объекту с seed 7
  4. Следующие 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, а не новый объект.

Неверный массив не является состоянием engineСамостоятельный пример
<?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.
Одинаковый seed → повторяемый старт
  1. PcgOneseq128XslRr64(41) создаётся дважды
  2. Объекты разные
  3. Первые 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, дальнейшие ответы разойдутся. Размеры и порядок высокоуровневых запросов являются частью воспроизводимого сценария.

Проверить особенность seed и границу аргументаСамостоятельный пример
<?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 или остаток %.
Seed 41 → первое слово
  1. PcgOneseq128XslRr64(41)
  2. generate выдаёт 8 байта
  3. 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.

Зафиксировать текущую точку через cloneСамостоятельный пример
<?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. Состояние не следует публиковать; устойчивый внешний формат документацией не обещан.
Engine → диагностический массив
  1. PcgOneseq128XslRr64 с seed 41
  2. __debugInfo читает состояние
  3. В выводе только факт наличия __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; формат не предлагается как стабильный внешний протокол.
Снимок → восстановленный следующий шаг
  1. PcgOneseq128XslRr64 seed 41 делает шаг
  2. __serialize фиксирует эту точку
  3. __unserialize передаёт её объекту с seed 7
  4. Следующие 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; не полагайтесь на объект после неудачной загрузки.
Снимок → восстановленный следующий шаг
  1. PcgOneseq128XslRr64 seed 41 делает шаг
  2. __serialize фиксирует эту точку
  3. __unserialize передаёт её объекту с seed 7
  4. Следующие 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, а не новый объект.

Неверный массив не является состоянием engineСамостоятельный пример
<?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=intSeed трактуется как 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.
Одинаковый seed → повторяемый старт
  1. Xoshiro256StarStar(41) создаётся дважды
  2. Объекты разные
  3. Первые 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, дальнейшие ответы разойдутся. Размеры и порядок высокоуровневых запросов являются частью воспроизводимого сценария.

Проверить особенность seed и границу аргументаСамостоятельный пример
<?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 или остаток %.
Seed 41 → первое слово
  1. Xoshiro256StarStar(41)
  2. generate выдаёт 8 байта
  3. 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.

Зафиксировать текущую точку через cloneСамостоятельный пример
<?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. Состояние не следует публиковать; устойчивый внешний формат документацией не обещан.
Engine → диагностический массив
  1. Xoshiro256StarStar с seed 41
  2. __debugInfo читает состояние
  3. В выводе только факт наличия __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; формат не предлагается как стабильный внешний протокол.
Снимок → восстановленный следующий шаг
  1. Xoshiro256StarStar seed 41 делает шаг
  2. __serialize фиксирует эту точку
  3. __unserialize передаёт её объекту с seed 7
  4. Следующие 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; не полагайтесь на объект после неудачной загрузки.
Снимок → восстановленный следующий шаг
  1. Xoshiro256StarStar seed 41 делает шаг
  2. __serialize фиксирует эту точку
  3. __unserialize передаёт её объекту с seed 7
  4. Следующие 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, а не новый объект.

Неверный массив не является состоянием engineСамостоятельный пример
<?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.
Пять шагов двумя путями
  1. fast.jump(5)
  2. slow.generate повторён 5 раз
  3. Следующие слова одинаковы

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.
Шаблон → две одинаково продвинутые копии
  1. Два clone исходного seed 41
  2. Обе копии вызывают jump
  3. Следующий 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.
Шаблон → две одинаково продвинутые копии
  1. Два clone исходного seed 41
  2. Обе копии вызывают jumpLong
  3. Следующий 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.

Как выбрать случайность и прочитать первый вызов.

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

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

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

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

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