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

Дерево руководства
Глава 84 · Практика
Каталоги: полный справочник Directory и функций
Все функции и методы перечисления каталогов, рабочая директория, переносимые пути и реальные задачи со списками файлов.
Из папки выдачи — к списку документов
Имя файла и его содержимое — два разных шага
Сервису нужен список полученных документов. Сначала он читает каталог, затем открывает нужный документ.
- 1Папка catalog↓
На диске лежат a.txt и b.txt; внутри каждого есть данные.
- 2scandir('catalog')↓
Возвращает массив имён, а не содержимое документов.
- 3Имя a.txt↓
Соединяем основание catalog и имя: catalog/a.txt.
- 4Чтение документа
file_get_contents получает содержимое выбранного файла отдельным действием.
Для большого каталога можно читать имена по одному через opendir/readdir. Имена и пути не заменяют проверку доступа к документу.
У сотрудника есть папка documents с памятками заказов 104 и 105. Нужно показать, какие документы готовы. Каталог хранит имена файлов и подкаталогов; scandir читает эти имена, но пока не читает сами памятки. Сначала получим список, затем откроем каждый подходящий документ и обойдём архив.
- documents/104.txt: «Выдать две дрели»
- documents/105.txt: «Выдать одну пилу»
- scandir получает имена рядом с . и ..
- PHP выводит два имени документов
| Запись | Что она делает |
|---|---|
| scandir(__DIR__ . '/documents') | __DIR__ задаёт папку исходника; точка соединяет путь. Функция возвращает массив имён либо false. |
| $name === '.' || $name === '..' | Точное сравнение отбрасывает служебные имена текущей и родительской папки. || означает «или». |
| foreach ($names as $name) | Берёт имена из массива по одному; continue переходит к следующему имени. |
<?php
declare(strict_types=1);
$names = scandir(__DIR__ . '/documents');
if ($names === false) { throw new RuntimeException('Не прочитать папку'); }
foreach ($names as $name) {
if ($name === '.' || $name === '..') { continue; }
echo 'Документ: ', $name, "\n";
}
Ожидаемый результат
Документ: 104.txt
Документ: 105.txt
В архив включены два текстовых файла. scandir сортирует имена и возвращает также . и ..; мы пропускаем их. Это список одного уровня. Следующие карточки объясняют отбор только файлов, содержимое и постепенное чтение большого каталога.
Для маленького списка удобно scandir. Для обработки по одному имени — opendir, readdir и closedir. Объект Directory объединяет тот же доступ в свойства и методы. Массивы; Циклы; Пути и чтение.
Все константы и переносимость путей
Константа — готовое именованное значение. DIRECTORY_SEPARATOR относится к разделителю внутри пути, PATH_SEPARATOR — к разделителю списка нескольких путей. Это разные задачи; менять одним другое нельзя.
- Каталог + разделитель + имя → один путь
- DIRECTORY_SEPARATOR выбирает системный разделитель
- Путь1 + PATH_SEPARATOR + путь2 → список путей
- SCANDIR_SORT_* управляет порядком имён, а не путями
| Константа | Смысл |
|---|---|
| DIRECTORY_SEPARATOR | Системный разделитель компонентов пути: обратная косая черта в Windows, / в Unix. PHP-файловые вызовы Windows обычно принимают также /. |
| PATH_SEPARATOR | ; в Windows и : в остальных системах; например для include_path. Не является URL-разделителем. |
| SCANDIR_SORT_ASCENDING | scandir сортирует имена по возрастанию; стандартный режим. |
| SCANDIR_SORT_DESCENDING | Сортировка имён по убыванию. |
| SCANDIR_SORT_NONE | Без сортировки: порядок файловой системы. |
<?php
declare(strict_types=1);
$path = 'catalog' . DIRECTORY_SEPARATOR . 'a.txt';
echo file_get_contents($path);
var_dump(strlen(PATH_SEPARATOR) === 1);
Ожидаемый результат
A
bool(true)
Точка в PHP склеивает строки. Построение пути само по себе не проверяет, что пользовательский ввод не вышел из разрешённой папки; здесь обе части заданы автором.
Список файлов на сайте: безопасность и стоимость
Каталог не является таблицей доверенных пользователей. Имя файла может содержать пробел, кавычку, HTML-символы, быть "0" или начинаться с точки. Не выводите имя без HTML-экранирования и не превращайте произвольный GET-параметр в путь сервера. Для скачивания удобно выдавать публичный идентификатор документа и по нему находить разрешённый путь после проверки доступа.
- Пользователь выбрал ID документа
- Сервис проверил владельца/разрешение
- Нашёл свой фиксированный путь
- Открыл файл; HTML-подпись экранируется отдельно
<?php
declare(strict_types=1);
$name = 'report & draft.txt';
echo htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
Ожидаемый результат
report & draft.txt
HTML-экранирование защищает текстовую подпись в HTML. Оно не кодирует URL, не проверяет владельца и не делает путь безопасным для открытия.
Отбрасывайте . и .. точным сравнением. Проверка первого символа на точку имеет другой смысл — скрыть все Unix-style dotfiles; Windows использует также атрибут скрытости. is_file/is_dir требует путь с основанием каталога; проверка только имени ошибочно смотрит в cwd. Проверка пути и последующее открытие могут быть разделены гонкой изменений; в недоверенной общей папке нужны ограничения записи и подходящая модель доступа.
scandir выделяет массив всех имён и по умолчанию сортирует его. opendir/readdir с обработкой по одному имени ограничивает накопление списка, но сортировка всего набора снова требует памяти. Рекурсивный обход дополнительно требует решения о символических ссылках, глубине и циклах; рекурсия объясняет остановку обхода. DirectoryIterator и RecursiveDirectoryIterator из SPL предоставляют другой интерфейс; они не делают произвольную рекурсию автоматически безопасной. Для высокой нагрузки измеряйте свой каталог: тип файловой системы, число файлов и сетевой доступ меняют стоимость.
Упражнение: в catalog лежат файлы 0, a.txt, b.txt и каталог archive. Получите количество только обычных файлов верхнего уровня, не загружая все имена в массив. Ожидаем 3: archive/old.txt не относится к верхнему уровню.
Разобрать решение упражнения
<?php
declare(strict_types=1);
$handle = opendir('catalog');
if ($handle === false) { throw new RuntimeException('Cannot open catalog'); }
$files = 0;
try {
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
if (is_file('catalog/' . $name)) { ++$files; }
}
} finally { closedir($handle); }
echo $files, "\n";
Ожидаемый результат
3
Различие с простым подсчётом записей — is_file на пути с основанием catalog. В большом каталоге в памяти остаются счётчик и текущее имя. Если задача меняется на рекурсивный обход, заранее решите правила перехода по ссылкам и максимальную глубину.
Готовая опись: пройти дерево документов с ограничением глубины
Теперь документы лежат и в archive. Получим опись всех обычных файлов внутри своей папки: относительный путь и первую строку памятки. Функция вызывает сама себя для подкаталога — это рекурсия. Параметр $depth считает вложенность; предел 3 прекращает неожиданно глубокий обход. Символические ссылки пропускаются до проверки каталога, чтобы не уйти к внешней цели или по циклу.
- listDocuments(documents, "", 0)
- 104.txt → вывести памятку
- archive → listDocuments(documents, "archive/", 1)
- 103.txt → вывести архивную строку
- Возврат на уровень 0, затем завершение
<?php
declare(strict_types=1);
function listDocuments(string $root, string $relative = '', int $depth = 0): void
{
if ($depth > 3) { throw new RuntimeException('Слишком глубокое дерево'); }
$names = scandir($root . '/' . $relative);
if ($names === false) { throw new RuntimeException('Не прочитать каталог'); }
foreach ($names as $name) {
if ($name === '.' || $name === '..') { continue; }
$local = $relative . $name;
$path = $root . '/' . $local;
if (is_link($path)) { continue; }
if (is_dir($path)) {
listDocuments($root, $local . '/', $depth + 1);
continue;
}
if (!is_file($path)) { continue; }
$stream = fopen($path, 'rb');
if ($stream === false) { throw new RuntimeException('Не открыть документ'); }
try {
$preview = fgets($stream, 256);
if ($preview === false) {
if (!feof($stream)) { throw new RuntimeException('Ошибка чтения документа'); }
$preview = '[пустой документ]';
}
echo $local, ': ', rtrim($preview, "\r\n"), "\n";
} finally { fclose($stream); }
}
}
listDocuments(__DIR__ . '/documents');
Ожидаемый результат
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
archive/103.txt: Заказ закрыт
scandir даёт сортировку в каждом каталоге. $relative хранит путь только внутри выбранного корня; новый вызов получает глубину +1, а после возврата старый продолжает свой foreach. Первая строка ограничена 255 байтами: это короткий предварительный просмотр, не полный текст. В недоверенном изменяемом дереве проверки могут устареть, поэтому процессу нужны ограниченные права и отдельная модель доступа.
Для огромного дерева добавляют общий предел числа файлов, времени и суммарного объёма; либо используют итераторы SPL. Здесь выполнен небольшой локальный обход заранее известной папки. Попробуйте добавить archive/102.txt и сравните опись; затем установите предел глубины 0 и получите контролируемый отказ на archive. Порядок рекурсивных вызовов и возвратов.
Что ломалось в реальных задачах и как это воспроизвести
Следующие обсуждения использованы как постановки проблем. Примеры написаны для этого учебника; старые ответы не исполнялись и не перенесены как готовые решения.
| Проблема | Причина и современное решение |
|---|---|
| Почему обход останавливается на файле 0 | Имя0 допустимо, но ложно при нестрогой проверке. Сравниваем результат readdir строго с false. Договор PHP. |
| Почему is_dir проверяет не тот каталог | readdir возвращает имя; проверку делают по исходной папке плюс это имя. Относительный путь может быть корректным, если известна его точка отсчёта. Договор PHP. |
| Когда нужен readdir, а когда scandir | Выбор между поочерёдным получением имён и готовым массивом со своим порядком. Вопрос о скорости не даёт универсального коэффициента ускорения. Договор PHP. |
| Почему tempnam создал файл в другой папке | При недоступном каталоге возможен системный fallback. Пример проверяет фактический каталог, а публикация отчёта требует совместимого хранилища. Договор PHP. |
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
scandirПолучить массив имён файлов и подкаталогов одного каталога.
PHP 5+ · standard
scandir(string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING, ?resource $context = null): array|falseПолучить массив имён файлов и подкаталогов одного каталога.
| Часть вызова | Договор |
|---|---|
| directory | Путь к каталогу. Возвращаются имена внутри него, без префикса пути; вложенные каталоги автоматически не обходятся. |
| sorting_order | SCANDIR_SORT_ASCENDING по умолчанию: алфавитный порядок. DESCENDING — обратный, NONE — без сортировки. Числовые суффиксы имён не превращаются в естественную сортировку. |
| context | Необязательный контекст потока, null разрешён с PHP 8.0. URL работает только у обёртки, которая поддерживает операции каталога; HTTP-страница не становится каталогом. |
| Возврат | array имён, включая . и .. у обычной файловой системы, либо false. Недоступный/неверный путь вызывает E_WARNING. |
| Цена | Читает все имена в память, по умолчанию ещё сортирует. Для очень большого каталога opendir/readdir позволяет обрабатывать по одному имени. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- catalog содержит a.txt и b.txt
- scandir("catalog") читает имена
- Получаем массив [".", "..", "a.txt", "b.txt"]
- Отбрасываем только служебные . и ..
Каталог — контейнер имён в файловой системе. Массив — уже данные внутри PHP. Точка означает текущий каталог, две точки — родительский; это не пустые файлы. Сначала простой вывод двух известных имён:
<?php
declare(strict_types=1);
$names = scandir('catalog');
if ($names === false) { throw new RuntimeException('Cannot read catalog'); }
echo implode(', ', $names), "\n";
Ожидаемый результат
., .., a.txt, b.txt
implode соединяет элементы массива строкой-разделителем. Если каталог изменится, его список тоже изменится; здесь используются собственные неизменяемые учебные файлы.
<?php
declare(strict_types=1);
$names = scandir('catalog');
if ($names === false) { throw new RuntimeException('Cannot read catalog'); }
$files = [];
foreach ($names as $name) {
if ($name === '.' || $name === '..') { continue; }
if (is_file('catalog/' . $name)) { $files[] = $name; }
}
echo implode(', ', $files), "\n";
Ожидаемый результат
0, a.txt, b.txt
is_file получает полный путь относительно текущего каталога, а не голое имя из списка. archive пропущен как каталог; имя 0 остаётся файлом. Это отбор для отчёта, не гарантия безопасности будущего открытия при одновременном изменении файлов.
<?php
declare(strict_types=1);
$ascending = scandir('catalog', SCANDIR_SORT_ASCENDING);
$descending = scandir('catalog', SCANDIR_SORT_DESCENDING);
if ($ascending === false || $descending === false) { throw new RuntimeException('Cannot read catalog'); }
echo implode(', ', $ascending), "\n", implode(', ', $descending), "\n";
Ожидаемый результат
., .., a.txt, b.txt
b.txt, a.txt, .., .
SCANDIR_SORT_NONE экономит сортировку, но не обещает конкретный порядок. Если затем всё равно сортировать полный массив, выигрыша в памяти не будет.
Рядом: opendir, readdir, dir, is_file, sort. Как читать каталог.
opendirОткрыть каталог и получить ресурс для последовательного чтения.
PHP 4+ · standard
opendir(string $directory, ?resource $context = null): resource|falseОткрыть каталог и получить ресурс для последовательного чтения.
| Часть вызова | Договор |
|---|---|
| directory | Путь к существующему доступному каталогу. Пустой/неверный путь, права и ошибки файловой системы мешают открытию. |
| context | Контекст потока или null; nullable с 8.0. Возможности зависят от обёртки. |
| Возврат | Ресурс каталога, не массив имён. При неуспехе false и E_WARNING. Сначала проверяют результат, затем вызывают readdir. |
| Ресурс | Хранит позицию чтения. Передавайте конкретный handle дальше и закрывайте его в finally. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Открыть documents
- Получать имена до строгого false
- Закрыть перечисление и отсортировать имена
- Показать содержимое каждого обычного файла
<?php
declare(strict_types=1);
$handle = opendir(__DIR__ . '/documents');
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
$names = [];
try {
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
if (is_file(__DIR__ . '/documents/' . $name)) { $names[] = $name; }
}
} finally { closedir($handle); }
sort($names, SORT_STRING);
foreach ($names as $name) {
echo $name, ': ', file_get_contents(__DIR__ . '/documents/' . $name);
}
Ожидаемый результат
0: Заказ без присвоенного номера
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
opendir возвращает доступ к каталогу. readdir последовательно даёт имена; is_file получает путь с основанием documents. Для воспроизводимого вывода имена сортируются после чтения; ниже показана обработка без накопления списка.
<?php
declare(strict_types=1);
$folder = __DIR__ . '/documents';
$handle = opendir($folder);
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
$bytes = 0;
try {
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
$path = $folder . '/' . $name;
if (!is_file($path)) { continue; }
$size = filesize($path);
if ($size === false) { throw new RuntimeException('Не получить размер'); }
$bytes += $size;
}
} finally { closedir($handle); }
echo 'Документы текущей выдачи: ', $bytes, " байт\n";
Ожидаемый результат
Документы текущей выдачи: 22 байт
Это прикладной размер подборки: только обычные файлы первого уровня, без archive/103.txt. В памяти остаётся текущая запись и сумма; имена не накапливаются. Размеры относятся к моментам отдельных проверок, не к атомарному снимку.
<?php
declare(strict_types=1);
$warning = false;
set_error_handler(static function (int $level) use (&$warning): bool {
if ($level !== E_WARNING) { return false; }
$warning = true;
return true;
});
try { $handle = opendir(__DIR__ . '/missing-documents'); }
finally { restore_error_handler(); }
if ($handle === false) { echo "Выдача недоступна: папка документов не подготовлена.\n"; }
if ($warning) { echo "PHP также сообщил файловое предупреждение.\n"; }
Ожидаемый результат
Выдача недоступна: папка документов не подготовлена.
PHP также сообщил файловое предупреждение.
Отсутствующая папка даёт два сигнала: false и E_WARNING. В опыте handler регистрирует только warning, затем восстанавливается. В приложении техническую причину направляют в журнал, а сотруднику дают действие; не скрывают все предупреждения глобально.
Рядом: readdir, closedir, rewinddir, scandir. Как читать каталог.
readdirПрочитать имя следующей записи или получить false при окончании/неуспехе.
PHP 4+ · standard
readdir(?resource $dir_handle = null): string|falseПрочитать имя следующей записи или получить false при окончании/неуспехе.
| Часть вызова | Договор |
|---|---|
| dir_handle | Ресурс opendir. Исторический null выбирает последний открытый handle; с 8.0 nullable, с 8.5 null и пропуск deprecated. Передавайте ресурс явно. |
| Возврат | Имя string без пути либо false. Строка "0" — корректное имя, но в нестрогой логической проверке ложна. |
| Порядок | Порядок файловой системы, не обещанная сортировка. Список не является атомарным снимком изменяемого каталога. |
| Глубина | Один уровень; . и .. надо пропускать явно. Для is_file/is_dir соединяют путь каталога и имя. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Позиция у следующей записи
- read возвращает строку, например "0"
- Позиция перемещается дальше
- Только значение false завершает перебор
<?php
declare(strict_types=1);
$handle = opendir('catalog');
if ($handle === false) { throw new RuntimeException('Cannot open catalog'); }
$names = [];
try {
while (($name = readdir($handle)) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
} finally { closedir($handle); }
sort($names, SORT_STRING);
echo implode(', ', $names), "\n";
Ожидаемый результат
0, a.txt, archive, b.txt
Сортировка выполнена только после чтения ради одинакового учебного вывода. Для потоковой обработки обычно обрабатывают имя сразу, не добавляют его в массив.
<?php
declare(strict_types=1);
$handle = opendir(__DIR__ . '/documents');
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
$names = [];
try {
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
if (is_file(__DIR__ . '/documents/' . $name)) { $names[] = $name; }
}
} finally { closedir($handle); }
sort($names, SORT_STRING);
foreach ($names as $name) {
echo $name, ': ', file_get_contents(__DIR__ . '/documents/' . $name);
}
Ожидаемый результат
0: Заказ без присвоенного номера
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
В папке действительно есть файл 0 с содержимым. Строгое !== false позволяет обработать его; нестрогое while ($name = ...) остановилось бы на этом имени. Сортировка здесь служит только стабильному показу.
Рядом: opendir, dir, scandir, is_dir. Как читать каталог.
Directory::readПрочитать имя следующей записи или получить false при окончании/неуспехе.
PHP 4+ · standard
public function read(): string|falseПрочитать имя следующей записи или получить false при окончании/неуспехе.
| Часть вызова | Договор |
|---|---|
| Параметры | Нет с PHP 8.0; раньше метод допускал аргумент handle. Используется handle объекта. |
| Возврат | Имя string без пути либо false. Строка "0" — корректное имя, но в нестрогой логической проверке ложна. |
| Порядок | Порядок файловой системы, не обещанная сортировка. Список не является атомарным снимком изменяемого каталога. |
| Глубина | Один уровень; . и .. надо пропускать явно. Для is_file/is_dir соединяют путь каталога и имя. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". dir(...) создаёт объект; ->read() вызывает его метод, ->path читает свойство. Сам Directory через new здесь не создают. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Позиция у следующей записи
- read возвращает строку, например "0"
- Позиция перемещается дальше
- Только значение false завершает перебор
<?php
declare(strict_types=1);
$directory = dir('catalog');
if ($directory === false) { throw new RuntimeException('Cannot open catalog'); }
$names = [];
try {
while (($name = $directory->read()) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
} finally { $directory->close(); }
sort($names, SORT_STRING);
echo implode(', ', $names), "\n";
Ожидаемый результат
0, a.txt, archive, b.txt
Сортировка выполнена только после чтения ради одинакового учебного вывода. Для потоковой обработки обычно обрабатывают имя сразу, не добавляют его в массив.
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
$names = [];
try {
while (($name = $directory->read()) !== false) {
if ($name === '.' || $name === '..') { continue; }
if (is_file(__DIR__ . '/documents/' . $name)) { $names[] = $name; }
}
} finally { $directory->close(); }
sort($names, SORT_STRING);
foreach ($names as $name) {
echo $name, ': ', file_get_contents(__DIR__ . '/documents/' . $name);
}
Ожидаемый результат
0: Заказ без присвоенного номера
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
В папке действительно есть файл 0 с содержимым. Строгое !== false позволяет обработать его; нестрогое while ($name = ...) остановилось бы на этом имени. Сортировка здесь служит только стабильному показу.
Рядом: opendir, dir, scandir, is_dir. Как читать каталог.
closedirЗакрыть открытый ресурс каталога, когда чтение закончено.
PHP 4+ · standard
closedir(?resource $dir_handle = null): voidЗакрыть открытый ресурс каталога, когда чтение закончено.
| Часть вызова | Договор |
|---|---|
| dir_handle | Ресурс opendir. null/пропуск исторически выбирает последний открытый ресурс, deprecated с PHP 8.5; nullable с 8.0. |
| Возврат | void: полезного результата нет; присвоенное значение наблюдается как null. |
| После закрытия | Дескриптор уже нельзя читать или закрывать повторно. Переменная может оставаться, но ресурс больше не действителен. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Открыт documents
- Имена104.txt и105.txt получены
- Закрыт ресурс перечисления
- Сам документ104.txt остаётся доступным
<?php
declare(strict_types=1);
$handle = opendir(__DIR__ . '/documents');
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
$names = [];
try {
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
$names[] = $name;
}
} finally { closedir($handle); }
sort($names, SORT_STRING);
echo 'Прочитанные имена: ', implode(', ', $names), "\n";
echo 'Документ после закрытия каталога: ', file_get_contents(__DIR__ . '/documents/104.txt');
Ожидаемый результат
Прочитанные имена: 104.txt, 105.txt
Документ после закрытия каталога: Выдать две дрели
Закрывается ресурс перечисления каталога; файлы сохраняются. У close нет полезного возвращаемого значения — void. Для нового перечисления открывают новый ресурс, не повторно используют закрытый.
<?php
declare(strict_types=1);
$handle = opendir(__DIR__ . '/documents');
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
try {
try {
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
$text = file_get_contents(__DIR__ . '/documents/' . $name);
json_decode($text, true, 8, JSON_THROW_ON_ERROR);
}
} finally { closedir($handle); }
} catch (JsonException $error) { echo "Импорт остановлен: документ содержит неверный JSON.\n"; }
echo is_resource($handle) ? "Каталог ещё открыт.\n" : "Каталог закрыт после отказа.\n";
Ожидаемый результат
Импорт остановлен: документ содержит неверный JSON.
Каталог закрыт после отказа.
В файле 104.json отсутствует значение после двоеточия. finally закрывает каталог при реальном отказе парсера; внешний catch объясняет исправление. После закрытия ресурс не читают и не закрывают второй раз.
Рядом: opendir, dir, readdir. Как читать каталог.
Directory::closeЗакрыть открытый ресурс каталога, когда чтение закончено.
PHP 4+ · standard
public function close(): voidЗакрыть открытый ресурс каталога, когда чтение закончено.
| Часть вызова | Договор |
|---|---|
| Параметры | Нет с PHP 8.0; раньше допускался handle. Закрывается handle объекта. |
| Возврат | void: полезного результата нет; присвоенное значение наблюдается как null. |
| После закрытия | Дескриптор уже нельзя читать или закрывать повторно. Переменная может оставаться, но ресурс больше не действителен. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". dir(...) создаёт объект; ->read() вызывает его метод, ->path читает свойство. Сам Directory через new здесь не создают. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Открыт documents
- Имена104.txt и105.txt получены
- Закрыт ресурс перечисления
- Сам документ104.txt остаётся доступным
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
$names = [];
try {
while (($name = $directory->read()) !== false) {
if ($name === '.' || $name === '..') { continue; }
$names[] = $name;
}
} finally { $directory->close(); }
sort($names, SORT_STRING);
echo 'Прочитанные имена: ', implode(', ', $names), "\n";
echo 'Документ после закрытия каталога: ', file_get_contents(__DIR__ . '/documents/104.txt');
Ожидаемый результат
Прочитанные имена: 104.txt, 105.txt
Документ после закрытия каталога: Выдать две дрели
Закрывается ресурс перечисления каталога; файлы сохраняются. У close нет полезного возвращаемого значения — void. Для нового перечисления открывают новый ресурс, не повторно используют закрытый.
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
try {
try {
while (($name = $directory->read()) !== false) {
if ($name === '.' || $name === '..') { continue; }
$text = file_get_contents(__DIR__ . '/documents/' . $name);
json_decode($text, true, 8, JSON_THROW_ON_ERROR);
}
} finally { $directory->close(); }
} catch (JsonException $error) { echo "Импорт остановлен: документ содержит неверный JSON.\n"; }
echo is_resource($directory->handle) ? "Каталог ещё открыт.\n" : "Каталог закрыт после отказа.\n";
Ожидаемый результат
Импорт остановлен: документ содержит неверный JSON.
Каталог закрыт после отказа.
В файле 104.json отсутствует значение после двоеточия. finally закрывает каталог при реальном отказе парсера; внешний catch объясняет исправление. После закрытия ресурс не читают и не закрывают второй раз.
Рядом: opendir, dir, readdir. Как читать каталог.
rewinddirВернуть позицию открытого каталога к началу для повторного прохода.
PHP 4+ · standard
rewinddir(?resource $dir_handle = null): voidВернуть позицию открытого каталога к началу для повторного прохода.
| Часть вызова | Договор |
|---|---|
| dir_handle | Ресурс opendir; nullable с 8.0. null/пропуск выбора последнего handle deprecated с PHP 8.5. |
| Возврат | void; метод не возвращает первое имя. Чтобы получить его, отдельно вызывают read/readdir. |
| Ограничение | Не является откатом изменений файловой системы или стабильным снимком. При изменении каталога повторный проход может дать другой набор/порядок. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Первый проход дошёл до false
- rewind переносит курсор к началу
- Следующий read снова читает первое имя
- Изменения файлов на диске при этом не отменяются
<?php
declare(strict_types=1);
$handle = opendir(__DIR__ . '/documents');
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
try {
foreach (['Предпросмотр', 'Подборка'] as $label) {
$names = [];
while (($name = readdir($handle)) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
sort($names, SORT_STRING);
echo $label, ': ', implode(', ', $names), "\n";
rewinddir($handle);
}
} finally { closedir($handle); }
Ожидаемый результат
Предпросмотр: 104.txt, 105.txt
Подборка: 104.txt, 105.txt
rewind возвращает только позицию перечисления. Папка здесь не меняется, поэтому оба списка совпадают; на рабочей ФС повторный проход может увидеть изменения. Для постоянного снимка проще сохранить первый массив. Второй проход полезен лишь когда требуется новое чтение.
<?php
declare(strict_types=1);
$handle = opendir(__DIR__ . '/documents');
if ($handle === false) { throw new RuntimeException('Не открыть документы'); }
try {
$names = [];
while (($name = readdir($handle)) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
sort($names, SORT_STRING);
echo 'Предпросмотр: ', implode(', ', $names), "\n";
rewinddir($handle);
$documents = [];
while (($name = readdir($handle)) !== false) {
if ($name === '.' || $name === '..') { continue; }
$text = file_get_contents(__DIR__ . '/documents/' . $name);
if ($text === false) { throw new RuntimeException('Документ недоступен'); }
$documents[$name] = $text;
}
ksort($documents, SORT_STRING);
foreach ($documents as $name => $text) { echo $name, ': ', $text; }
} finally { closedir($handle); }
Ожидаемый результат
Предпросмотр: 104.txt, 105.txt
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
Первый проход получает только имена, второй заново перечисляет каталог и читает содержимое. Без перемотки второй цикл уже находится в конце. Каталог может измениться; это не snapshot. Когда свежий обход не требуется, проще сохранить и использовать первый список.
Рядом: opendir, readdir, dir, scandir. Как читать каталог.
Directory::rewindВернуть позицию открытого каталога к началу для повторного прохода.
PHP 4+ · standard
public function rewind(): voidВернуть позицию открытого каталога к началу для повторного прохода.
| Часть вызова | Договор |
|---|---|
| Параметры | Нет с PHP 8.0; прежде допускался handle. Используется ресурс объекта. |
| Возврат | void; метод не возвращает первое имя. Чтобы получить его, отдельно вызывают read/readdir. |
| Ограничение | Не является откатом изменений файловой системы или стабильным снимком. При изменении каталога повторный проход может дать другой набор/порядок. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". dir(...) создаёт объект; ->read() вызывает его метод, ->path читает свойство. Сам Directory через new здесь не создают. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Первый проход дошёл до false
- rewind переносит курсор к началу
- Следующий read снова читает первое имя
- Изменения файлов на диске при этом не отменяются
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
try {
foreach (['Предпросмотр', 'Подборка'] as $label) {
$names = [];
while (($name = $directory->read()) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
sort($names, SORT_STRING);
echo $label, ': ', implode(', ', $names), "\n";
$directory->rewind();
}
} finally { $directory->close(); }
Ожидаемый результат
Предпросмотр: 104.txt, 105.txt
Подборка: 104.txt, 105.txt
rewind возвращает только позицию перечисления. Папка здесь не меняется, поэтому оба списка совпадают; на рабочей ФС повторный проход может увидеть изменения. Для постоянного снимка проще сохранить первый массив. Второй проход полезен лишь когда требуется новое чтение.
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
try {
$names = [];
while (($name = $directory->read()) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
sort($names, SORT_STRING);
echo 'Предпросмотр: ', implode(', ', $names), "\n";
$directory->rewind();
$documents = [];
while (($name = $directory->read()) !== false) {
if ($name === '.' || $name === '..') { continue; }
$text = file_get_contents(__DIR__ . '/documents/' . $name);
if ($text === false) { throw new RuntimeException('Документ недоступен'); }
$documents[$name] = $text;
}
ksort($documents, SORT_STRING);
foreach ($documents as $name => $text) { echo $name, ': ', $text; }
} finally { $directory->close(); }
Ожидаемый результат
Предпросмотр: 104.txt, 105.txt
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
Первый проход получает только имена, второй заново перечисляет каталог и читает содержимое. Без перемотки второй цикл уже находится в конце. Каталог может измениться; это не snapshot. Когда свежий обход не требуется, проще сохранить и использовать первый список.
Рядом: opendir, readdir, dir, scandir. Как читать каталог.
dirОткрыть каталог через объект Directory с методами read, rewind и close.
PHP 4+ · standard
dir(string $directory, ?resource $context = null): Directory|falseОткрыть каталог через объект Directory с методами read, rewind и close.
| Часть вызова | Договор |
|---|---|
| directory | Путь к каталогу; у объекта path сохраняется открытый путь. |
| context | Контекст потока либо null, nullable с PHP 8.0; возможности зависят от обёртки. |
| Возврат | Directory при успехе, false при ошибке; неудачное открытие может выдать E_WARNING. |
| Модель | Обёртка над ресурсом каталога. read возвращает по одному имени; порядок системный. Создавайте через dir(), не new Directory. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". dir(...) создаёт объект; ->read() вызывает его метод, ->path читает свойство. Сам Directory через new здесь не создают. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- dir открывает documents и возвращает Directory
- read использует handle объекта
- close закрывает только перечисление
- Файлы0,104.txt,105.txt прочитаны отдельно
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
$names = [];
try {
while (($name = $directory->read()) !== false) {
if ($name === '.' || $name === '..') { continue; }
if (is_file(__DIR__ . '/documents/' . $name)) { $names[] = $name; }
}
} finally { $directory->close(); }
sort($names, SORT_STRING);
foreach ($names as $name) {
echo $name, ': ', file_get_contents(__DIR__ . '/documents/' . $name);
}
Ожидаемый результат
0: Заказ без присвоенного номера
104.txt: Выдать две дрели
105.txt: Выдать одну пилу
dir создаёт объект Directory. ->read() вызывает метод этого объекта; ->close() закрывает его ресурс. Объект хранит тот же поток имён, что использовали процедурные функции.
<?php
declare(strict_types=1);
$directory = dir(__DIR__ . '/documents');
if ($directory === false) { throw new RuntimeException('Не открыть документы'); }
try {
$names = [];
while (($name = readdir($directory->handle)) !== false) {
if ($name !== '.' && $name !== '..') { $names[] = $name; }
}
echo $directory->read() === false ? "Метод тоже видит конец каталога.\n" : "Остались имена.\n";
sort($names, SORT_STRING);
echo 'Прочитаны: ', implode(', ', $names), "\n";
} finally { $directory->close(); }
Ожидаемый результат
Метод тоже видит конец каталога.
Прочитаны: 104.txt, 105.txt
readdir получает свойство handle; метод read использует его же. Поэтому после полного прохода функцией метод уже видит конец. Не закрывайте этот ресурс дважды разными интерфейсами.
Рядом: Directory, Directory::read, Directory::close, opendir. Как читать каталог.
DirectoryХранить открытый каталог и его позицию чтения в стандартном объекте.
PHP 4+ · standard
final class Directory { public readonly string $path; public readonly resource $handle; close(): void; read(): string|false; rewind(): void; }Хранить открытый каталог и его позицию чтения в стандартном объекте.
| Часть вызова | Договор |
|---|---|
| Создание | dir($path), не оператор new. Класс final с PHP 8.5. |
| path | Путь открытого каталога; readonly с PHP 8.1. |
| handle | Ресурс каталога; readonly с PHP 8.1, его можно передавать readdir/rewinddir/closedir. resource в synopsis описывает внутренний тип, не разрешённую декларацию свойства пользовательского класса. |
| Методы | read, rewind, close. С PHP 8.0 не принимают параметров; раньше допускали handle. |
| Не Iterator | Directory не реализует удобный foreach-перебор файлов как DirectoryIterator из SPL. Выберите интерфейс осознанно. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. Ресурс — выданный PHP доступ к открытому каталогу и его позиции чтения, не массив файлов. while повторяет тело, пока чтение даёт имя; !== false позволяет не потерять имя "0". dir(...) создаёт объект; ->read() вызывает его метод, ->path читает свойство. Сам Directory через new здесь не создают. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- path = catalog
- handle указывает на открытый поток имён
- read двигает его позицию
- close завершает работу
<?php
declare(strict_types=1);
$directory = dir('catalog');
if ($directory === false) { throw new RuntimeException('Cannot open catalog'); }
try {
echo $directory->path, "\n";
try { $directory->path = 'other'; }
catch (Error $error) { echo "readonly path\n"; }
} finally { $directory->close(); }
Ожидаемый результат
catalog
readonly path
Подмена path не должна переводить handle в другой каталог. Чтобы открыть другое место, создайте другой объект dir.
<?php
declare(strict_types=1);
$reflection = new ReflectionClass(Directory::class);
$methods = array_map(static fn(ReflectionMethod $m): string => $m->getName(), $reflection->getMethods());
sort($methods);
echo implode(', ', $methods), "\n";
var_dump($reflection->isFinal());
Ожидаемый результат
close, read, rewind
bool(true)
Метаданные проверены на PHP 8.5.11; до PHP 8.5 класс не был final. Для своего сервиса оборачивайте Directory отдельным объектом, если нужно добавить бизнес-правила.
Рядом: dir, Directory::read, Directory::rewind, Directory::close. Как читать каталог.
getcwdУзнать текущий рабочий каталог, от которого разрешаются относительные пути.
PHP 4+ · standard
getcwd(): string|falseУзнать текущий рабочий каталог, от которого разрешаются относительные пути.
| Часть вызова | Договор |
|---|---|
| Параметры | Нет. cwd означает current working directory. |
| Возврат | Строка текущего каталога либо false. На некоторых Unix отсутствие нужных прав у родительских каталогов мешает получить путь. |
| Отличие | Не гарантирует папку текущего PHP-файла. __DIR__ относится к файлу исходника, cwd может меняться через chdir. |
| ZTS | В сборке с потокобезопасностью виртуальный cwd PHP может отличаться от cwd операционной системы/внешней библиотеки FFI. Проверка PHP_ZTS объясняет сборку, а не синхронизирует пути. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- getcwd даёт текущую папку запуска
- Добавляем documents/104.txt
- Читаем «Выдать две дрели»
- В другом cwd используйте нужный явный корень
<?php
declare(strict_types=1);
$current = getcwd();
if ($current === false) { throw new RuntimeException('Не определить рабочую папку'); }
echo file_get_contents($current . '/documents/104.txt');
Ожидаемый результат
Выдать две дрели
Архив запускается из собственной папки, где находится documents. getcwd возвращает фактический абсолютный путь этой папки. При запуске из другого места он может отличаться от __DIR__; для соседних файлов исходника обычно проще __DIR__.
<?php
declare(strict_types=1);
$previous = getcwd();
if ($previous === false) { throw new RuntimeException('Нет рабочей папки'); }
echo 'В основной папке: ', file_get_contents('status.txt');
if (!chdir(__DIR__ . '/archive')) { throw new RuntimeException('Не перейти в архив'); }
try { echo 'В архиве: ', file_get_contents('status.txt'); }
finally { if (!chdir($previous)) { throw new RuntimeException('Не восстановить папку'); } }
echo 'После возврата: ', file_get_contents('status.txt');
Ожидаемый результат
В основной папке: ready
В архиве: closed
После возврата: ready
Одинаковое имя status.txt обозначает разные файлы в разных cwd. finally восстанавливает прежнюю точку отсчёта. Смена cwd — состояние выполнения, а не локальная настройка одной функции.
Рядом: chdir, is_dir, file_get_contents. Как читать каталог.
chdirСменить рабочий каталог текущего PHP-выполнения.
PHP 4+ · standard
chdir(string $directory): boolСменить рабочий каталог текущего PHP-выполнения.
| Часть вызова | Договор |
|---|---|
| directory | Новый доступный каталог, абсолютный или относительный к текущему cwd. |
| Возврат | true при успехе; false и E_WARNING при неуспехе. |
| Область влияния | Меняет последующие относительные пути. Это состояние выполнения, не локальная переменная одной функции; библиотеке лучше принимать путь явно. |
| ZTS | В ZTS PHP-функции уважают виртуальную смену каталога, но операционная система и внешние библиотеки через FFI могут её не видеть. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Было cwd проекта
- chdir("catalog")
- file_get_contents("a.txt") ищет внутри catalog
- finally возвращает предыдущий cwd
<?php
declare(strict_types=1);
$previous = getcwd();
if ($previous === false || !chdir('catalog')) { throw new RuntimeException('Cannot enter catalog'); }
try { echo file_get_contents('a.txt'); }
finally { if (!chdir($previous)) { throw new RuntimeException('Cannot restore directory'); } }
Ожидаемый результат
A
Это может пригодиться адаптеру старого инструмента, который ожидает относительные пути. Для нового кода явный полный путь обычно проще и не меняет состояние соседних операций.
<?php
declare(strict_types=1);
echo 'Текущий заказ: ', file_get_contents(__DIR__ . '/status.txt');
echo 'Архивный заказ: ', file_get_contents(__DIR__ . '/archive/status.txt');
Ожидаемый результат
Текущий заказ: ready
Архивный заказ: closed
__DIR__ привязывает пути к исходнику; явное основание обычно удобнее chdir для нового кода и не меняет поведение соседних относительных путей.
Рядом: getcwd, file_get_contents. Как читать каталог.
chrootИзменить корень файловой системы процесса в специальном Unix-окружении.
PHP 4.0.5+ · standard
chroot(string $directory): boolИзменить корень файловой системы процесса в специальном Unix-окружении.
| Часть вызова | Договор |
|---|---|
| directory | Путь нового корня. После успешного вызова cwd становится /. Значения __DIR__/__FILE__ не переписываются. |
| Доступность | GNU/BSD, только CLI/CGI/Embed SAPI; нужны root-привилегии. В Windows не реализована, в ZTS недоступна. |
| Возврат | true при успехе, false при неуспехе. Проверка function_exists не доказывает права и правильную подготовку окружения. |
| Назначение и границы | Низкоуровневая операция администратора процесса. После неё пути и доступность файлов/библиотек меняются. Это не замена проверке прав пользователя, контейнеру или самостоятельная гарантия изоляции привилегированного процесса. |
Знак $ начинает имя переменной, = сохраняет результат справа, скобки после имени вызывают функцию, а ; завершает инструкцию. if проверяет условие. === false означает именно логическое false; строка "0" ему не равна. throw new RuntimeException(...) создаёт и выбрасывает объект ошибки, чтобы не продолжать с неудачным результатом. try содержит рабочее действие; finally выполнит освобождение/восстановление даже при ошибке. catch, если показан, перехватывает указанную ошибку; один finally её не подавляет. Подробно об обработке ошибок.
- Администратор подготовил отдельное дерево
- chroot назначает его видимым /
- cwd становится /
- Старые пути и зависимости могут перестать разрешаться
<?php
declare(strict_types=1);
$available = function_exists('chroot');
if (PHP_OS_FAMILY === 'Windows' || PHP_ZTS) {
var_dump($available === false);
} else {
var_dump(is_bool($available));
}
Ожидаемый результат
bool(true)
Сам chroot здесь не вызывается. Проверка выполнена в Windows; поддерживаемый Unix, root-права и реальная изоляция этим опытом не проверены.
<?php
declare(strict_types=1);
if (PHP_SAPI !== 'cli' || PHP_ZTS || !function_exists('chroot')) {
throw new RuntimeException('Requires supported non-ZTS Unix CLI');
}
// Только фиксированное дерево, подготовленное администратором заранее.
if (!chroot('/srv/example-jail')) {
throw new RuntimeException('Cannot change process root');
}
echo getcwd(), "\n";
После успешного вызова ожидаемый cwd — /. Фрагмент проверяется только синтаксически и не запускается: в текущем Windows нет этой функции. Подготовка jail, зависимостей и сброс привилегий относятся к отдельному системному решению; не переносите такой вызов в обработчик формы.
Рядом: chdir, getcwd. Как читать каталог.
Источники и обсуждения
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


