Rewinddir: примеры (PHP)

Использование rewinddir для управления чтением каталогов в PHP
Раздел: Работа с файловой системой
rewinddir(resource $dir_handle): void
Описание функции rewinddir

Функция rewinddir в PHP сбрасывает указатель внутреннего итератора каталога на начало списка записей. Это означает, что последующий вызов readdir() начнет чтение заново с первой записи в директории.

Функция используется при работе с дескриптором каталога, открытого с помощью opendir(), когда требуется повторно пройти по списку файлов и поддиректорий без необходимости повторного открытия каталога.

Аргументы функции

Функция принимает один необязательный аргумент:

  • $dir_handle (ресурс) — Дескриптор (resource) каталога, ранее открытый с помощью opendir(). Если аргумент не указан, функция попытается использовать последний дескриптор каталога, использованный в opendir(). Однако такое поведение считается устаревшим и ненадежным. Начиная с PHP 8.0.0, этот параметр является обязательным.

Простые примеры использования
Пример 1: Базовое использование

Перемотка дескриптора каталога для повторного чтения.

<?php
$dir = opendir('.');
if ($dir) {
    echo "Первое чтение:\n";
    while (($file = readdir($dir)) !== false) {
        echo $file . "\n";
    }

    rewinddir($dir); // Сброс указателя

    echo "\nПовторное чтение:\n";
    while (($file = readdir($dir)) !== false) {
        echo $file . "\n";
    }
    closedir($dir);
}
?>
Первое чтение:
.
..
index.php
style.css

Повторное чтение:
.
..
index.php
style.css
Пример 2: Обязательный аргумент в PHP 8+
<?php
$handle = opendir('/tmp');
rewinddir($handle); // В PHP 8 передача аргумента обязательна
// ... дальнейшие операции с readdir($handle)
closedir($handle);
?>
Похожие функции в PHP
  • opendir() — Открывает дескриптор каталога. Это необходимая операция перед использованием rewinddir().
  • readdir() — Читает следующую запись из дескриптора каталога. rewinddir() сбрасывает внутренний указатель этой функции.
  • scandir() — Сканирует указанную директорию и возвращает массив всех файлов и поддиректорий. Эта функция сама управляет указателем и возвращает полный список, поэтому rewinddir() не требуется. Предпочтительнее для получения полного списка сразу в массив.
  • Функции итератора DirectoryIterator — Объектно-ориентированный интерфейс (например, DirectoryIterator::rewind()), который обычно удобнее и безопаснее для итерации по каталогам.

Функцию rewinddir() предпочтительно использовать в низкоуровневом потоковом чтении больших директорий, где нецелесообразно хранить весь список в памяти (как это делает scandir()), и требуется несколько проходов по данным.

Типичные ошибки
1. Передача неверного типа аргумента
<?php
rewinddir('not_a_resource'); // Не ресурс
?>
Warning: rewinddir() expects parameter 1 to be resource, string given
2. Использование закрытого дескриптора
<?php
$dir = opendir('.');
closedir($dir);
rewinddir($dir); // Дескриптор уже закрыт
?>
Warning: rewinddir(): supplied resource is not a valid Directory resource
3. Пропуск обязательного аргумента в PHP 8.0+
<?php // PHP 8.0 и выше
$dir = opendir('.');
rewinddir(); // Без аргументов
?>
Fatal error: Uncaught ArgumentCountError: rewinddir() expects exactly 1 argument, 0 given
4. Попытка перемотки без предварительного открытия
<?php
rewinddir(); // Нет последнего использованного дескриптора
?>
Warning: rewinddir(): %s is not a valid Directory resource
Изменения в версиях PHP
  • PHP 8.0.0: Параметр $dir_handle стал обязательным. Ранее он был необязательным, и функция использовала последний открытый каталог.
  • До PHP 8.0.0, если параметр не был передан, функция использовала последний ресурс каталога, открытый opendir(). Это поведение было удалено для улучшения ясности и предотвращения ошибок.
Расширенные примеры
Пример 1: Поиск определенных файлов с несколькими проходами

Поиск всех файлов .txt, затем подсчет их общего количества.

Пример php
<?php
$dirHandle = opendir('.');
$txtFiles = [];
if ($dirHandle) {
    // Первый проход: сбор .txt файлов
    while (($entry = readdir($dirHandle)) !== false) {
        if (str_ends_with($entry, '.txt')) {
            $txtFiles[] = $entry;
        }
    }
    echo "Найдены файлы: " . implode(', ', $txtFiles) . "\n";

    rewinddir($dirHandle);
    $count = 0;
    // Второй проход: просто подсчет всех записей
    while (($entry = readdir($dirHandle)) !== false) {
        $count++;
    }
    echo "Всего записей в директории: $count\n";
    closedir($dirHandle);
}
?>
Найдены файлы: readme.txt, notes.txt
Всего записей в директории: 8
Пример 2: Имитация функции glob с помощью readdir и rewinddir
Пример php
<?php
function customGlob($pattern, $path = '.') {
    $dirHandle = opendir($path);
    $matches = [];
    if (!$dirHandle) return $matches;

    // Преобразуем шаблон glob в регулярное выражение
    $regex = '/^' . str_replace(['.', '*'], ['\.', '.*'], $pattern) . '$/';

    while (($file = readdir($dirHandle)) !== false) {
        if (preg_match($regex, $file)) {
            $matches[] = $file;
        }
    }
    closedir($dirHandle);
    return $matches;
}

print_r(customGlob('*.php'));
?>
Array
(
    [0] => index.php
    [1] => admin.php
)
Пример 3: Работа с вложенными операциями (осторожно)

Использование rewinddir внутри вложенного цикла чтения может привести к неожиданным результатам.

Пример php
<?php
$dir = opendir('.');
while (($file = readdir($dir)) !== false) {
    if ($file == '..') continue;
    echo "Внешний цикл: $file\n";
    // Внутренний проход по тому же каталогу без открытия нового дескриптора
    rewinddir($dir);
    while (($innerFile = readdir($dir)) !== false) {
        if ($innerFile == '..') continue;
        echo "  Внутренний: $innerFile\n";
        break; // Важно иметь break, чтобы избежать бесконечного цикла
    }
}
closedir($dir);
?>
Внешний цикл: .
  Внутренний: .
Внешний цикл: ..
  Внутренний: .
Внешний цикл: index.php
  Внутренний: .
...
Аналоги в других языках
Python

В Python для повторного чтения содержимого каталога обычно заново используют os.listdir() или вызывают seek(0) на объекте файлового дескриптора, возвращенного os.opendir().

import os
dir_handle = os.opendir('.')
# Первый проход
for entry in dir_handle:
    print(entry.name)
# Возврат к началу с помощью seek
os.lseek(dir_handle.fileno(), 0, os.SEEK_SET)
# Или просто заново открыть итератор
dir_handle = os.opendir('.')
JavaScript (Node.js)

В Node.js используется асинхронный API. Повторное чтение директории обычно выполняется путем нового вызова fs.readdir() или fs.opendirSync() с последующим созданием нового итератора.

const fs = require('fs');
async function readDirTwice(path) {
  const dir = await fs.promises.opendir(path);
  // Первый проход
  for await (const dirent of dir) {
    console.log(dirent.name);
  }
  // Нет прямого аналога rewinddir, нужно открыть заново
  const dir2 = await fs.promises.opendir(path);
  for await (const dirent of dir2) { /* ... */ }
}

Rewinddir в MySQL

В SQL нет прямой аналогии для работы с файловой системой сервера. Для списка файлов используются запросы к таблицам, например, INFORMATION_SCHEMA.FILES, и повторное выполнение запроса дает тот же эффект.

PHP rewinddir function comments

En
Rewinddir Rewind directory handle