Постраничная навигация через ООП для текстового файла (+ поиск), для файлов в директории и для СУБД MySQL

PHP 28.10.21 28.10.21 324
Бесплатные курсына главную сниппетов
  1. Содержание:
  2. Файловая постраничная навигация
  3. Постраничная навигация и поиск
  4. Постраничная навигация для директории
  5. Постраничная навигация для базы данных
  6. Изменение формата постраничной навигации
  7. Абстрактные классы
  8. Абстрактные методы
*Важно: везде в коде, где производится вычитание, замените на -

Рассмотрим постраничную навигацию. Объемный список неудобно отображать на странице целиком, т. к. это требует значительных ресурсов.

Гораздо нагляднее выводить список, например, по 10 элементов, предоставляя ссылки на оставшиеся страницы. В большинстве случаев такая задача решается без привлечения объектно-ориентированного подхода и тем более без наследуемой иерархии и полиморфизма.
Однако в Web-приложении источником списка элементов, к которому следует применить постраничную навигацию, могут выступать база данных, файл со строками, директория с файлами изображений и т. п. Каждый раз создавать отдельную функцию для реализации постраничной навигации не всегда удобно, т. к. придется дублировать значительную часть кода в нескольких функциях.

В данном случае удобнее реализовать постраничную навигацию в методе базового класса pager, а методы, работающие с конкретными источниками, переопределить в производных классах:
- pager_mysql — постраничная навигация для СУБД MySQL;
- pager_file — постраничная навигация для текстового файла;
- pager_dir — постраничная навигация для файлов в директории.
Иерархия классов постраничной навигации представлена на рис. 4.5.

pager_oop
Рис. 4.5. Иерархия классов постраничной навигации

Создадим базовый класс pager, который при помощи перегруженного метода __toString() будет выводить ссылки постраничной навигации в следующем формате: [1-10]… [281-290] [291-300] [301-310] [311-320] [321-330] … [511-517]
Номера отображенных на экране элементов [301—310] выделяются жирным шрифтом. Крайняя левая и крайняя правая ссылки позволяют перейти в начало и конец списка, а вокруг текущей страницы выводятся ссылки на соседние страницы.

Класс pager() “не знает”, каким образом производные классы будут узнавать общее количество позиций в списке, сколько позиций будет отображаться на одной странице, сколько ссылок находится слева и справа от текущей страницы. Поэтому помимо метода __ToString() класс будет содержать четыре защищенных (protected) метода, которые возвращают:
- get_total() — общее количество позиций в списке;
- get_pnumber() — количество позиций на странице;
- get_page_link() — количество позиций слева и справа от текущей страницы;
- get_parameters() — строку, которую необходимо передать по ссылкам на другую страницу (например, при постраничном выводе результатов поиска по ссылкам придется передавать результаты поиска).
В листинге 4.28 представлена одна из возможных реализаций класса pager.

Следует обратить внимание, что перечисленные выше методы не несут никакой функциональности: они необходимы лишь для того, чтобы обращение к ним из метода __toString() не приводило к ошибке. Сами методы должныбыть реализованы в производных классах.

Конструктор класса pager объявлен защищенным, т. е. из внешнего кода не будет никакой возможности объявить объект класса pager: такая предосторожность необходима, поскольку сам по себе класс pager не является работоспособным, он лишь задает функциональность производных классов.

Листинг 4.28. Реализация класса pager

<?php
class pager
{
protected function __construct()
{
}
protected function get_total()
{
// Общее количество записей
}
protected function get_pnumber()
{
// Количество позиций на странице
}
protected function get_page_link()
{
// Количество ссылок слева и справа
// от текущей страницы
}
protected function get_parameters()
{
// Дополнительные параметры, которые
// необходимо передать по ссылке
}

// Ссылки на другие страницы
public function __toString()
{
// Строка для возвращаемого результата
$return_page = "";
// Через GET-параметр page передается номер
// текущей страницы      
$page = $_GET['page'];
if(empty($page)) $page = 1;

// Вычисляем число страниц в системе
$number = (int)($this->get_total()/$this->get_pnumber());
if((float)($this->get_total()/$this->get_pnumber()) — $number != 0)
{
$number++;
}
// Проверяем, есть ли ссылки слева
if($page — $this->get_page_link() > 1)
{
$return_page .= "<a href=$_SERVER[PHP_SELF]".
"?page=1{$this->get_parameters()}>
[1-{$this->get_pnumber()}]
</a>&nbsp;&nbsp;...&nbsp;&nbsp;";
// Есть
for($i = $page — $this->get_page_link(); $i<$page; $i++)
{
$return_page .= "&nbsp;<a href=$_SERVER[PHP_SELF]".
"?page=$i{$this->get_parameters()}>
[".(($i — 1)*$this->get_pnumber() + 1).
"-".$i*$this->get_pnumber()."]
</a>&nbsp;";
}
}
else
{
// Нет
for($i = 1; $i<$page; $i++)
{
$return_page .= "&nbsp;<a href=$_SERVER[PHP_SELF]".
"?page=$i{$this->get_parameters()}>
[".(($i — 1)*$this->get_pnumber() + 1).
"-".$i*$this->get_pnumber()."]
</a>&nbsp;";
}
}
// Проверяем, есть ли ссылки справа
if($page + $this->get_page_link() < $number)
{
// Есть
for($i = $page; $i<=$page + $this->get_page_link(); $i++) 
{
if($page == $i)
$return_page .= "&nbsp;[".
(($i — 1) * $this->get_pnumber() + 1).
"-".$i*$this->get_pnumber()."]&nbsp;";
else
$return_page .= "&nbsp;<a href=$_SERVER[PHP_SELF]".
"?page=$i{$this->get_parameters()}>
[".(($i — 1)*$this->get_pnumber() + 1).
"-".$i*$this->get_pnumber()."]
</a>&nbsp;";
}
$return_page .= "&nbsp;...&nbsp;&nbsp;".
"<a href=$_SERVER[PHP_SELF]".
"?page=$number{$this->get_parameters()}>
[".(($number — 1)*$this->get_pnumber() + 1).
"-{$this->get_total()}]
</a>&nbsp;";
}
else
{
// Нет
for($i = $page; $i<=$number; $i++)
{
if($number == $i)
{
if($page == $i)
$return_page .= "&nbsp;[".
(($i — 1)*$this->get_pnumber() + 1).
"-{$this->get_total()}]&nbsp;";
else
$return_page .= "&nbsp;<a href=$_SERVER[PHP_SELF]".
"?page=$i{$this->get_parameters()}>
[".(($i — 1)*$this->get_pnumber() + 1).
"-{$this->get_total()}]
</a>&nbsp;";
}
else
{
if($page == $i)
$return_page .= "&nbsp;[".
(($i — 1)*$this->get_pnumber() + 1).
"-".$i*$this->get_pnumber()."]&nbsp;"; 
else
$return_page .= "&nbsp;<a href=$_SERVER[PHP_SELF]".
"?page=$i{$this->get_parameters()}>
[".(($i — 1)*$this->get_pnumber() + 1).
"-".$i*$this->get_pnumber()."]
</a>&nbsp;";
}
}
}
return $return_page;
}
}
?>

Заранее не известно, для какой страницы будет выводиться навигация, поэтому в ссылках используется элемент суперглобального массива $_SERVER['PHP_SELF'], который возвращает номер текущей страницы. Нумерация страниц начинается с 1; номер текущей страницы передается через GET-параметр page и доступен в суперглобальном массиве $_GET['page'].

Суперглобальные массивы ($_GET, $_POST, $_SESSION, $_COOKIE, $_GLOBAL, $_REQUEST) доступны в любой части скрипта, поэтому не обязательно предусматривать передачу их элементов объекту.

Файловая постраничная навигация

Реализуем постраничную навигацию при помощи производного класса pager_file, который будет работать с источником и перегрузит методы get_total(), get_pnumber(), get_page_link() и get_parameters() базового класса pager.
Рассмотрим простейший случай, когда класс pager_file читает строки из текстового файла и выводит 10 строк на одной странице, предоставляя ссылки на другие страницы (листинг 4.29).

Листинг 4.29. Класс pager_file

<?php 
  // Подключаем базовый класс 
  require_once("class.pager.php"); 
 
  class pager_file extends pager 
  {
    // Имя файла 
    protected $filename; 
    // Количество позиций на странице 
    private $pnumber; 
    // Количество ссылок слева и справа 
    // от текущей страницы 
    private $page_link; 
    // Параметры 
    private $parameters; 
    // Конструктор 
    public function __construct($filename, 
                                $pnumber = 10, 
                                $page_link = 3, 
                                $parameters = "") 
    { 
      $this->filename   = $filename; 
      $this->pnumber    = $pnumber; 
      $this->page_link  = $page_link; 
      $this->parameters = $parameters; 
    } 
    public function get_total() 
    { 
      $countline = 0; 
      // Открываем файл 
      $fd = fopen($this->filename, "r"); 
      if($fd) 
      { 
        // Подсчитываем количество записей 
        // в файле 
        while(!feof($fd)) 
        { 
          fgets($fd, 10000); 
          $countline++; 
        } 
        // Закрываем файл 
        fclose($fd); 
      } 
      return $countline; 
    } 
    public function get_pnumber() 
    { 
      // Количество позиций на странице 
      return $this->pnumber; 
    }
    public function get_page_link() 
    { 
      // Количество ссылок слева и справа 
      // от текущей страницы 
      return $this->page_link; 
    } 
    public function get_parameters() 
    { 
      // Дополнительные параметры, которые 
      // необходимо передать по ссылке 
      return $this->parameters; 
    } 
    // Возвращает массив строк файла 
    // по номеру страницы $index 
    public function get_page() 
    { 
      // Текущая страница 
      $page = $_GET['page']; 
      if(empty($page)) $page = 1; 
      // Количество записей в файле 
      $total = $this->get_total(); 
      // Вычисляем число страниц в системе 
      $number = (int)($total/$this->get_pnumber()); 
      if((float)($total/$this->get_pnumber()) — $number != 0) $number++; 
      // Проверяем, попадает ли запрашиваемый номер 
      // страницы в интервал от 1 до get_total() 
      if($page <= 0 || $page > $number) return 0; 
      // Извлекаем позиции текущей страницы 
      $arr = array(); 
      $fd = fopen($this->filename, "r"); 
      if(!$fd) return 0; 
      // Номер, начиная с которого следует 
      // выбирать строки файла 
      $first = ($page — 1)*$this->get_pnumber(); 
      for($i = 0; $i < $total; $i++) 
      { 
        $str = fgets($fd, 10000); 
        // Пока не достигнут номер $first, 
        // досрочно заканчиваем итерацию 
        if($i < $first) continue; 
        // Если достигнут конец выборки, 
        // досрочно покидаем цикл 
        if($i > $first + $this->get_pnumber() — 1) break;
        // Помещаем строки файла в массив, 
        // который будет возвращен методом 
        $arr[] = $str; 
      } 
      fclose($fd); 
 
      return $arr; 
    } 
  } 
?> 

Конструктор класса page_file принимает четыре параметра:
- $filename — имя текстового файла, выступающего источником строк;
- $pnumber — количество позиций на странице, необязательный параметр; если не указывается при создании объекта, принимает значение 10;
- $page_link — количество ссылок слева и справа от текущей страницы, необязательный параметр; если не указывается при создании объекта, принимает значение 3;
- $parameters — параметры для передачи по ссылке на другие страницы, необязательный параметр; если не указывается при создании объекта, принимает значение 3.

Данные четыре параметра передаются соответствующим закрытым членам класса. Реализация методов get_pnumber(), get_page_link() и get_parameters() сводится к возвращению значений соответствующих закрытых членов. Метод get_total() открывает файл с именем $filename, переданным в конструкторе, и подсчитывает количество строк в файле при каждом обращении.

С точки зрения производительности было бы разумно завести закрытую переменную $total и присваивать ей значение только один раз в конструкторе, т. к. операция сканирования файла достаточно трудоемка. Однако это не всегда удобно, поскольку количество записей в файле может изменяться на всем протяжении существования объекта.

Подсчет строк в файле осуществляется путем чтения строк функцией fgets() в цикле while(). До тех пор, пока конец файла не достигнут, функция feof() возвращает значение false, и цикл продолжает работу. Функция fgets() читает из файла количество символов, указанное во втором параметре; чтение символов заканчивается, если функция встречает символ перевода строки.
Обычно строки в текстовых файлах гораздо короче 10 000 символов, поэтому чтение всегда выполняется корректно.

Для работы с текстовыми файлами можно использовать функцию file(), которая возвращает содержимое текстового файла в виде массива, каждый элемент которого соответствует отдельной строке файла. Однако для файлов большого объема функция file() не всегда подходит.
Дело в том, что содержимое файла приходится полностью загружать в память скрипта, которая зачастую ограничена 8 или 16 Мбайт, а это приводит к аварийному завершению работы функции file(). При использовании функции fgets() в цикле while() для хранения содержимого файла скрипт в каждую секунду времени использует не больше 10 Кбайт (переменная $str).

Помимо указанных выше методов, объект снабжен методом get_page(), который возвращает массив строк файла, соответствующих текущей позиции.
Функция ориентируется на GET-параметр page, который используется для указания номера страницы в постраничной навигации. Если этот параметр не установлен, считается, что текущей является первая страница. Перед использованием параметра проверятся, не выходит ли его значение за пределы допустимого интервала; если выходит, параметр принимает значение 0.
Далее вычисляется номер строки $first, начиная с которой следует выбирать стро- ки из файла и помещать их в массив $arr, возвращаемый методом get_page() в качестве результата.
Пока счетчик цикла for() не достиг величины $first, итерация цикла может быть прекращена досрочно при помощи ключевого слова continue.
Если счетчик цикла превысил величину, равную $first плюс количество позиций на странице, цикл прекращает работу при помощи ключевого слова break.

Следует отметить, что ни класс pager, ни класс pager_file не используют ни одного оператора echo; результат возвращается при помощи ключевых слов return. Очень важно помещать в классы как можно меньше информации о дизайне и месте вывода информации на странице.
Именно такой подход позволяет повторно использовать классы в других проектах. Местоположение вывода — это задача либо клиентского кода, либо специального класса вывода; абстрактный тип данных должен решать только свойственные ему задачи.

Теперь, когда готов первый производный класс постраничной навигации, можно воспользоваться им для представления файла большого объема — стандартного словаря операционной системы Linux, который можно найти в /usr/share/dict/linux.words.

В листинге 4.30 приводится пример построения постраничной навигации по словарю linux.words.

Листинг 4.30. Постраничная навигация по файлу

<?php 
  require_once("class.pager_file.php"); 
 
  // Объявляем объект постраничной навигации 
  $obj = new pager_file("linux.words"); 
 
  // Выводим содержимое текущей страницы 
  $arr = $obj->get_page(); 
  for($i = 0; $i < count($arr); $i++) 
  { 
    echo "{$arr[$i]}<br>"; 
  } 
 
  // Выводим ссылки на другие страницы 
  echo $obj; 
?> 

Результат работы скрипта из листинга 4.30 представлен на рис. 4.6.

pager_oop1
Рис. 4.6. Постраничная навигация

Постраничная навигация и поиск

Классы не следует воспринимать как нечто статическое и завершенное.
Основное преимущество объектно-ориентированного подхода заключается в том, что уже существующие классы легко расширяются.

Например, пользователю может понадобиться возможность вывести в окно браузера все слова из словаря, начинающиеся с введенных им символов, а результаты поиска разбить на страницы. Разработанный ранее класс pager_file не подходит для решения этой задачи, т. к. не учитывает дополнительное ограничение.
Однако не стоит спешить его модифицировать, поскольку это может отразиться на коде, уже использующем этот класс. Более правильным будет унаследовать от класса pager_file новый класс pager_file_search, модифицировав его отдельные методы (рис. 4.7).

pager_oop2
Рис. 4.7. Расширение функциональности существующей системы путем наследования

В листинге 4.31 представлена возможная реализация класса pager_file_search, позволяющая устанавливать фильтры на позиции из словаря.

Листинг 4.31. Класс pager_file_search

<?php 
  // Подключаем базовый класс 
  require_once("class.pager_file.php"); 
 
  class pager_file_search extends pager_file 
  { 
    // Начало слова 
    private $search; 
    // Конструктор 
    public function __construct($search, 
                                $filename, 
                                $pnumber = 10, 
                                $page_link = 3)
    { 
      parent::__construct($filename, 
                          $pnumber, 
                          $page_link, 
                          "&search=".urlencode($search)); 
      $this->search   = $search; 
    } 
    public function get_total() 
    { 
      $countline = 0; 
      // Открываем файл 
      $fd = fopen($this->filename, "r"); 
      if($fd) 
      { 
        // Подсчитываем количество записей 
        // в файле 
        while(!feof($fd)) 
        { 
          $str = fgets($fd, 10000); 
          if(preg_match("|^".preg_quote($this->search)."|i", $str)) 
          { 
            $countline++; 
          } 
        } 
        // Закрываем файл 
        fclose($fd); 
      } 
      return $countline; 
    } 
    // Возвращает массив строк файла 
    // по номеру страницы $index 
    public function get_page() 
    { 
      // Текущая страница 
      $page = $_GET['page']; 
      if(empty($page)) $page = 1; 
      // Количество записей в файле 
      $total = $this->get_total(); 
      // Вычисляем число страниц в системе 
      $number = (int)($total/$this->get_pnumber()); 
      if((float)($total/$this->get_pnumber()) — $number != 0) $number++; 
      // Проверяем, попадает ли запрашиваемый номер 
      // страницы в интервал от 1 до get_total() Ãëàâà 4 
      if($page <= 0 || $page > $number) return 0; 
      // Извлекаем позиции текущей страницы 
      $arr = array(); 
      $fd = fopen($this->filename, "r"); 
      if(!$fd) return 0; 
      // Номер, начиная с которого следует 
      // выбирать строки файла 
      $first = ($page — 1)*$this->get_pnumber(); 
      while(!feof($fd)) 
      { 
        $str = fgets($fd, 10000); 
        if(preg_match("|^".preg_quote($this->search)."|i", $str)) 
        { 
          $countline++; 
          // Пока не достигнут номер $first, 
          // досрочно заканчиваем итерацию 
          if($countline < $first) continue; 
          // Если достигнут конец выборки, 
          // досрочно покидаем цикл 
          if($countline > $first + $this->get_pnumber() — 1) break; 
          // Помещаем строки файла в массив, 
          // который будет возвращен методом 
          $arr[] = $str; 
        } 
      } 
      // Закрываем файл 
      fclose($fd); 
 
      return $arr; 
    } 
  } 
?> 

Как видно из листинга 4.31, перегрузке подвергся конструктор, который первым параметром $search теперь принимает искомое словосочетание, вторым параметром $filename — имя файла-источника и лишь затем необязательные параметры, среди которых отсутствует $parametrs.
Последний параметр формируется явно при вызове конструктора базового класса — именно с его помощью передается искомое словосочетание $search.

Помимо конструктора перегрузке подверглись методы get_total() и get_page(), которые при помощи регулярного выражения, выстраиваемого на основе члена $search, проверяют каждую строку файла на соответствие заданному условию.

В листинге 4.32 приводится пример использования объекта класса pager_file_search, в котором задается вывод всех слов из словаря, начинающихся с последовательности "ab" по пять позиций на странице.

Листинг 4.32. Использование объекта класса pager_file_search

<?php 
  require_once("class.pager_file_search.php"); 
 
  // Объявляем объект постраничной навигации 
  $obj = new pager_file_search("ab", "linux.words", 5); 
 
  // Выводим содержимое текущей страницы 
  $arr = $obj->get_page(); 
  for($i = 0; $i < count($arr); $i++) 
  { 
    echo "{$arr[$i]}<br>"; 
  } 
 
  // Выводим ссылки на другие страницы 
  echo $obj; 
?> 

Теперь не представляет сложности создать небольшое Web-приложение по поиску слов в Linux-словаре (листинг 4.33).

При поиске для передачи параметров из HTML-формы обработчику лучше пользоваться методом GET, чем POST. Во-первых, это позволяет ссылаться на результаты поиска, во-вторых, для организации постраничной навигации можно легко передавать все параметры формы через URL, не помещая их в сессию (т. к. массив для хранения данных сессий $_SESSION является суперглобальным и не сбрасывается автоматически, это зачастую приводит к созданию сложных систем, в которых легко допустить ошибку).

Листинг 4.33. Поиск по файлу linux.words

<form method=get> 
<input type=text name=search 
       value=<?= htmlspecialchars($_GET['search'], ENT_QUOTES); ?>> 
<input type=submit value=Искать> 
</form> 
<?php 
  require_once("class.pager_file_search.php"); 
  // Обработчик HTML-формы 
  if(!empty($_GET)) 
  { 
    // Объявляем объект постраничной навигации 
    $obj = new pager_file_search($_GET['search'], "linux.words", 5); 
 
    // Выводим содержимое текущей страницы 
    $arr = $obj->get_page(); 
    for($i = 0; $i < count($arr); $i++) 
    { 
      echo "{$arr[$i]}<br>"; 
    } 
 
    // Выводим ссылки на другие страницы 
    echo $obj; 
 } 
?> 

Результат работы скрипта из листинга 4.33 представлен на рис. 4.8.


Рис. 4.8. Поиск по словарю linux.words

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

В качестве источника позиций, нуждающихся в постраничной навигации, может выступать директория с файлами. Сами файлы могут иметь разную природу — это может быть фотография, путь к которой следует передать атрибуту src тега img, или текстовый файл с сообщением для гостевой книги.

Поэтому класс постраничной навигации pager_dir должен, по аналогии с pager_file, возвращать массив путей к файлам, а выводить их в окно браузера.
Это позволит, не сужая области применения класса, использовать его для организации постраничной навигации по любой произвольной директории.

В листинге 4.34 представлен класс pager_dir, который перегружает методы get_total(), get_pnumber(), get_page_link() и get_parameters() базового класса pager.

Конструктор класса принимает в качестве первого параметра имя директории с файлами $dirname, количество позиций на текущей страни- це $pnumber, количество ссылок слева и справа от текущей страницы $page_link и строку с GET-параметрами $parameters, которую следует передавать по ссылкам.

От класса pager_dir можно, в свою очередь, унаследовать класс pager_dir_sort, позволяющий задавать критерий сортировки файлов, или класс pager_dir_recurse, который осуществляет рекурсивный спуск и учитывает файлы вложенных поддиректорий.

Листинг 4.34. Класс pager_dir

<?php 
  // Подключаем базовый класс 
  require_once("class.pager.php"); 
 
  class pager_dir extends pager 
  { 
    // Имя директории 
    protected $dirname; 
    // Количество позиций на странице 
    private $pnumber; 
    // Количество ссылок слева и справа 
    // от текущей страницы 
    private $page_link; 
    // Параметры 
    private $parameters; 
    // Конструктор 
    public function __construct($dirname, 
                                $pnumber = 10, 
                                $page_link = 3, 
                                $parameters = "")
    { 
      // Удаляем последний символ /, если он имеется 
      $this->dirname    = trim($dirname, "/"); 
      $this->pnumber    = $pnumber; 
      $this->page_link  = $page_link; 
      $this->parameters = $parameters; 
    } 
    public function get_total() 
    { 
      $countline = 0; 
      // Открываем директорию 
      if(($dir = opendir($this->dirname)) !== false) 
      { 
        while(($file = readdir($dir)) !== false) 
        { 
          // Если текущая позиция является файлом, 
          // учитываем ее 
          if(is_file($this->dirname."/".$file)) $countline++; 
        } 
        // Закрываем директорию 
        closedir($dir); 
      } 
      return $countline; 
    } 
    public function get_pnumber() 
    { 
      // Количество позиций на странице 
      return $this->pnumber; 
    } 
    public function get_page_link() 
    { 
      // Количество ссылок слева и справа 
      // от текущей страницы 
      return $this->page_link; 
    } 
    public function get_parameters() 
    { 
      // Дополнительные параметры, которые 
      // необходимо передать по ссылке 
      return $this->parameters; 
    } 
    // Возвращает массив строк файла 
    // по номеру страницы $index 
    public function get_page()
    { 
      // Текущая страница 
      $page = $_GET['page']; 
      if(empty($page)) $page = 1; 
      // Количество записей в файле 
      $total = $this->get_total(); 
      // Вычисляем количество страниц в системе 
      $number = (int)($total/$this->get_pnumber()); 
      if((float)($total/$this->get_pnumber()) — $number != 0) $number++; 
      // Проверяем, попадает ли запрашиваемый номер 
      // страницы в интервал от 1 до get_total() 
      if($page <= 0 || $page > $number) return 0; 
      // Извлекаем позиции текущей страницы 
      $arr = array(); 
      // Номер, начиная с которого следует 
      // выбирать строки файла 
      $first = ($page — 1)*$this->get_pnumber(); 
      // Открываем директорию 
      if(($dir = opendir($this->dirname))  === false) return 0; 
      $i = -1; 
      while(($file = readdir($dir)) !== false) 
      { 
        // Если текущая позиция является файлом 
        if(is_file($this->dirname."/".$file)) 
        { 
          // Увеличиваем счетчик 
          $i++; 
          // Пока не достигнут номер $first, 
          // досрочно заканчиваем итерацию 
          if($i < $first) continue; 
          // Если достигнут конец выборки, 
          // досрочно покидаем цикл 
          if($i > $first + $this->get_pnumber() — 1) break; 
          // Помещаем пути к файлам в массив, 
          // который будет возвращен методом 
          $arr[] = $this->dirname."/".$file; 
        } 
      } 
      // Закрываем директорию 
      closedir($dir); 
 
      return $arr; 
    } 
  } 
?> 

Класс pager_dir отличается от класса pager_file только реализацией метода get_total(), предназначенного для возвращения количества файлов в директории, и метода get_page(), возвращающего массив путей к файлу для текущей страницы.

В основе обоих методов лежит использование функций для работы с директориями.
Порядок работы с директорией точно такой же, как и с файлом: директория открывается при помощи функции opendir(), функция принимает в качестве единственного параметра имя директории и возвращает дескриптор $dir, который затем используется в качестве первого параметра для всех остальных функций, работающих с директорией.
В цикле while() осуществляется последовательное чтение элементов директории при помощи функции readdir().
Функция возвращает лишь имя файла, поэтому при обращении к файлу необходимо формировать путь, добавляя перед его названием имя директории $this->dirname.

Пользователь класса может задавать имя директории как со слэшем на конце — "photo/", так и без него — "photo". Чтобы не создавать специальной обработки подобной ситуации, в конструкторе класса pager_dir косая черта в конце строки удаляется при помощи функции trim().
По умолчанию данная функция удаляет пробелы в начале и в конце строки, однако при помощи второго параметра можно указать удаляемый символ, отличный от пробела.

Следует отметить, что результат присвоения переменной $file значения функции readdir() сравнивается со значением false.
Обычно в качестве аргумента цикла while() используется выражение $file = readdir($dir), однако в данном случае это недопустимо, т. к. имя файла может начинаться с 0, что в контексте оператора while() будет рассматриваться как false и приводить к остановке цикла.

В директории могут находиться как файлы, так и поддиректории; обязательно учитываются как минимум две директории: текущая "." и родительская "..".
Поэтому при подсчете количества или при формировании массива файлов для текущей страницы важно подсчитывать именно файлы, избегая директорий. Для этой цели служит функция is_file(), которая возвращает true, если переданный ей в качестве аргумента путь ведет к файлу, и false — в противном случае.

В листинге 4.35 приводится пример использования класса pager_dir для вывода фотографий из папки photo по три штуки на странице.
Конструктор класса принимает в качестве первого параметра имя директории, в качестве второго, задающего количество позиций на странице, по умолчанию используется число 3.

Листинг 4.35. Использование класса pager_dir

<?php 
  require_once("class.pager_dir.php"); 
 
  // Объявляем объект постраничной навигации 
  $obj = new pager_dir("photo", 3); 
 
   // Выводим содержимое текущей страницы 
  $arr = $obj->get_page(); 
  for($i = 0; $i < count($arr); $i++) 
  { 
    echo "<img src={$arr[$i]}>&nbsp;&nbsp;&nbsp;"; 
  } 
  echo "<br>"; 
 
  // Выводим ссылки на другие страницы 
  echo $obj; 
?> 

Результат работы скрипта из листинга 4.35 может выглядеть так, как это представлено на рис. 4.9.

pager_oop4
Рис. 4.9. Постраничная навигация для файлов в директории

Постраничная навигация для базы данных

Помимо файлов и директорий, постраничная навигация часто применяется для вывода позиций из базы данных.
Реализуем производный класс pager_mysql, который расширит класс pager для работы с СУБД MySQL.

Очень часто для доступа к СУБД MySQL организуют специальный класс. Это не оправданно, как и в случае с классом доступа к файлу: если приложение работает только с одним типом СУБД, незачем подменять стандартный прозрачный интерфейс доступа собственным объектно-ориентированным интерфейсом, который не будет знаком никому, кроме самого разработчика.
Однако когда приложение проектируется для работы сразу с несколькими базами данных, управляемыми разными СУБД, полезно создать класс, который будет адаптировать запросы для доступа к разным типам СУБД.
Для доступа к СУБД MySQL следует воспользоваться новой библиотекой php_mysqli, которая предоставляет объектно-ориентированный интерфейс.
К сожалению, данная библиотека пока не получила столь же широкого распространения, как и библиотека php_mysql.

В листинге 4.36 представлена одна из возможных реализаций класса pager_mysql.
В задачу класса не входит установка соединения с базой данных — этим будет заниматься внешний код.
Конструктор класса принимает в качестве параметров имя таблицы $tablename, WHERE-условие $where и критерий сортировки $order.

Конструктор класса не пытается предотвратить SQL- инъекции, т. к. предполагается, что объекту передаются уже сформированные фрагменты SQL-запроса, и внешний программист позаботился о предотвращении данного вида атаки.

SQL-запросы зачастую формируются динамически, т. е. включают в себя одну или несколько переменных. Если значение таких переменных может быть умышленно искажено с целью изменения логики SQL-запроса, имеет место SQL-инъекция.
Представленный в листинге 4.36 класс pager_mysql работает лишь с одной таблицей, именно поэтому в нем не предусмотрены параметры для группировки при помощи ключевого слова GROUP BY и условий ON и HAVING. Для реализации многотабличных запросов потребуется унаследовать от pager новый класс pager_mysql_multi.

Листинг 4.36. Реализация класса pager_mysql

<?php 
  // Подключаем базовый класс 
  require_once("class.pager.php"); 
  class pager_mysql extends pager 
  { 
    // Имя таблицы 
    protected $tablename; 
    // WHERE-условие 
    protected $where; 
    // Критерий сортировки ORDER 
    protected $order; 
    // Количество позиций на странице 
    private $pnumber; 
    // Количество ссылок слева и справа 
    // от текущей страницы 
    private $page_link; 
    // Параметры 
    private $parameters; 
    // Конструктор 
    public function __construct($tablename, 
                                $where = "", 
                                $order = "", 
                                $pnumber = 10, 
                                $page_link = 3, 
                                $parameters = "") 
    { 
      $this->tablename  = $tablename; 
      $this->where      = $where; 
      $this->order      = $order; 
      $this->pnumber    = $pnumber; 
      $this->page_link  = $page_link; 
      $this->parameters = $parameters; 
    } 
    public function get_total() 
    { 
      // Формируем запрос на получение 
      // общего количества записей в таблице 
      $query = "SELECT COUNT(*) FROM {$this->tablename} 
                {$this->where} 
                {$this->order}"; 
      $tot = mysql_query($query); 
      if(!$tot) exit("Ошибка подсчета количества 
                      позиций<br>{mysql_error()}<br>$query"); 
      return mysql_result($tot, 0); 
    } 
   public function get_pnumber() 
    { 
      // Количество позиций на странице 
      return $this->pnumber; 
    } 
    public function get_page_link() 
    { 
      // Количество ссылок слева и справа 
      // от текущей страницы 
      return $this->page_link; 
    } 
    public function get_parameters() 
    { 
      // Дополнительные параметры, которые 
      // необходимо передать по ссылке 
      return $this->parameters; 
    } 
    // Возвращает массив строк файла 
    // по номеру страницы $index 
    public function get_page() 
    { 
      // Текущая страница 
      $page = $_GET['page']; 
      if(empty($page)) $page = 1; 
      // Количество записей в файле 
      $total = $this->get_total(); 
      // Вычисляем количество страниц в системе 
      $number = (int)($total/$this->get_pnumber()); 
      if((float)($total/$this->get_pnumber()) — $number != 0) $number++; 
      // Проверяем, попадает ли запрашиваемый номер 
      // страницы в интервал от 1 до get_total() 
      if($page <= 0 || $page > $number) return 0; 
      // Извлекаем позиции текущей страницы 
      $arr = array(); 
      // Номер, начиная с которого следует 
      // выбирать строки файла 
      $first = ($page — 1)*$this->get_pnumber(); 
      // Извлекаем позиции для текущей страницы 
      $query = "SELECT * FROM {$this->tablename} 
                {$this->where} 
                {$this->order} 
                LIMIT $first, {$this->get_pnumber()}"; 
      $tbl = mysql_query($query);
      if(!$tbl) exit("Ошибка обращения к таблице 
                      позиций<br>{mysql_error()}<br>$query"); 
      // Если имеется хотя бы один элемент, 
      // заполняем массив $arr 
      if(mysql_num_rows($tbl)) 
      { 
        while($arr[] = mysql_fetch_array($tbl)); 
      } 
      // Удаляем последний нулевой элемент 
      // массива $arr 
      unset($arr[count($arr) — 1]); 
 
      return $arr; 
    } 
  } 
?> 

Точно так же, как и в предыдущих классах, наиболее серьезной модификации подвергаются метод get_total(), возвращающий количество записей в таблице $tablename, и метод get_page(), который возвращает двумерный массив: каждый элемент массива представляет собой массив полей одной из записи.

Для подсчета количества записей таблицы в методе get_total() используется функция COUNT(*):

SELECT COUNT(*) FROM tbl

В методе get_page(), возвращающем записи для текущей страницы, для получения ограниченного объема записей используется конструкция LIMIT: так, запрос с конструкцией LIMIT 0, 10 возвращает первые 10 элементов таблицы, LIMIT 10, 10 возвращает следующие 10 элементов, LIMIT 20, 10 — следующие и т. д.

SELECT COUNT(*) FROM tbl LIMIT 0, 10 

Для демонстрации возможностей класса pager_mysql создадим таблицу position, предназначенную для хранения заголовков разделов сайта (листинг 4.37).

Листинг 4.37. SQL-дамп таблицы position

CREATE TABLE position ( 
  id_position INT(11) NOT NULL AUTO_INCREMENT, 
  name TEXT NOT NULL,
  PRIMARY KEY (id_position) 
); 
INSERT INTO position VALUES (1, 'C++'); 
INSERT INTO position VALUES (2, 'Pascal'); 
INSERT INTO position VALUES (3, 'Perl'); 
INSERT INTO position VALUES (4, 'PHP'); 
INSERT INTO position VALUES (5, 'C#'); 
INSERT INTO position VALUES (6, 'Visual Basic'); 
INSERT INTO position VALUES (7, 'BASH'); 
INSERT INTO position VALUES (8, 'Python'); 
INSERT INTO position VALUES (9, 'SQL'); 
INSERT INTO position VALUES (10, 'Fortran'); 
INSERT INTO position VALUES (11, 'JavaScript'); 
INSERT INTO position VALUES (12, 'HTML'); 
INSERT INTO position VALUES (13, 'UML'); 
INSERT INTO position VALUES (14, 'Java'); 

Таблица position состоит из двух полей: - id_position — первичный ключ таблицы, снабженный атрибутом AUTO_INCREMENT; - name — название раздела. Перед началом работы класса pager_mysql необходимо установить соединение с базой данных под управлением СУБД MySQL. Так как задача установки соединения с базой данных возникает достаточно часто, код, осуществляющий соединение, выделяют в отдельный файл (листинг 4.38).

Листинг 4.38. Установка соединения с базой данных под управлением СУБД MySQL (файл config.php)

<?php 
  // Адрес сервера MySQL 
  $dblocation = "localhost"; 
  // Имя базы данных на хостинге или локальной машине 
  $dbname = "oop"; 
  // Имя пользователя базы данных 
  $dbuser = "root"; 
  // и его пароль 
  $dbpasswd = ""; 
 
  // Устанавливаем соединение с базой данных 
  $dbcnx = @mysql_connect($dblocation, $dbuser, $dbpasswd);
  if (!$dbcnx) { 
   exit( "<P>В настоящий момент сервер базы данных не доступен, 
             поэтому корректное отображение страницы невозможно.</P>" ); 
  } 
  // Выбираем базу данных 
  if (! @mysql_select_db($dbname, $dbcnx) ) { 
    exit( "<P>В настоящий момент база данных не доступна, 
              поэтому корректное отображение страницы невозможно.</P>" ); 
  } 
 
  // Устанавливаем кодировку соединения; следует выбрать кодировку, 
  // в которой данные будут отправляться серверу MySQL 
  @mysql_query("SET NAMES 'cp1251'"); 
?> 

Теперь, когда весь вспомогательный код готов, выведем содержимое таблицы position, отсортировав ее по полю name (листинг 4.39).

Листинг 4.39. Постраничный вывод содержимого таблицы position

<?php 
  // Устанавливаем соединение с базой данных 
  require_once("config.php"); 
  // Подключаем класс постраничной навигации 
  require_once("class.pager_mysql.php"); 
 
  // Объявляем объект постраничной навигации 
  $obj = new pager_mysql("position", 
                         "", 
                         "ORDER BY name"); 
 
  // Выводим содержимое текущей страницы 
  $arr = $obj->get_page(); 
  for($i = 0; $i < count($arr); $i++) 
  { 
    echo "<a href=position.php?id={$arr[$i][id_postion]}>". 
         "{$arr[$i][name]}</a><br>"; 
  } 
 
  // Выводим ссылки на другие страницы 
  echo $obj; 
?>

Пожалуй, основным неудобством при использовании класса pager_mysql является раздельное формирование SQL-запроса. Большинство программистов будут вынуждены затратить значительное время для того, чтобы выяснить, что запись

$obj = new pager_mysql("position", 
                       "", 
                       "ORDER BY name"); 

эквивалентна SQL-запросу

SELECT * FROM position ORDER BY name 

Такова плата за возможность повторного использования кода, которая снижает гибкость всей системы.
Результат работы скрипта из листинга 4.39 про- демонстрирован на рис. 4.10.

pager_oop5
Рис. 4.10. Постраничный вывод информации из таблицы position базы данных под управлением MySQL

Изменение формата постраничной навигации

Пусть требуется изменить формат вывода ссылок постраничной навигации или предоставить альтернативную форму постраничной навигации, например, вместо номеров позиции выводить номера страниц: << ... < ... 3 4 5 6 7 8 9 ... > ... >> Символ << является ссылкой на первую страницу, символ >> — на послед- нюю, а ссылки < и > перемещают пользователя с текущей страницы на одну позицию влево или вправо соответственно.

Для осуществления такого способа постраничной навигации создадим в базовом классе pager новый метод print_page() (листинг 4.40).

В листинге 4.40 с целью экономии места базовый класс pager приводится не полностью.

Листинг 4.40. Альтернативный способ реализации постраничной навигации

<?php 
  class pager 
  { 
 
    ... 
 
    // Альтернативный способ постраничной навигации 
    public function __toString() 
    { 
      // Строка для возвращаемого результата 
      $return_page = ""; 
 
      // Через GET-параметр page передается номер 
      // текущей страницы 
      $page = $_GET['page']; 
      if(empty($page)) $page = 1; 
 
      // Вычисляем число страниц в системе 
      $number = (int)($this->get_total()/$this->get_pnumber()); 
      if((float)($this->get_total()/$this->get_pnumber()) — $number != 0) 
      { 
        $number++; 
      } 
 
      // Ссылка на первую страницу 
      $return_page .= "<a href='$_SERVER[PHP_SELF]". 
                      "?page=1{$this->get_parameters()}'>". 
                      "&lt;&lt;</a> ... "; 
      // Выводим ссылку "Назад", если это не первая страница 
      if($page != 1) $return_page .= " <a href='$_SERVER[PHP_SELF]". 
                      "?page=".($page — 1)."{$this->get_parameters()}'>". 
                      "&lt;</a> ... "; 
       // Выводим предыдущие элементы 
      if($page > $this->get_page_link() + 1) 
      { 
        for($i = $page — $this->get_page_link(); $i < $page; $i++) 
        { 
          $return_page .= "<a href='$_SERVER[PHP_SELF]?page=$i'>$i</a> "; 
        } 
      } 
      else 
      { 
        for($i = 1; $i < $page; $i++) 
        { 
          $return_page .= "<a href='$_SERVER[PHP_SELF]?page=$i'>$i</a> "; 
        } 
      } 
      // Выводим текущий элемент 
      $return_page .= "$i "; 
      // Выводим следующие элементы 
      if($page + $this->get_page_link() < $number) 
      { 
        for($i = $page + 1; $i <= $page + $this->get_page_link(); $i++) 
        { 
          $return_page .= "<a href='$_SERVER[PHP_SELF]?page=$i'>$i</a> "; 
        } 
      } 
      else 
      { 
        for($i = $page + 1; $i <= $number; $i++) 
        { 
          $return_page .= "<a href='$_SERVER[PHP_SELF]?page=$i'>$i</a> "; 
        } 
      } 
 
      // Выводим ссылку "вперед", если это не последняя страница 
      if($page != $number) $return_page .= " ... <a href='". 
                           "$_SERVER[PHP_SELF]?page=". 
                           ($page + 1)."{$this->get_parameters()}'>". 
                           "&gt;</a>"; 
      // Ссылка на последнюю страницу 
      $return_page .= " ... <a href='$_SERVER[PHP_SELF]". 
                      "?page=$number{$this->get_parameters()}'>". 
                      "&gt;&gt;</a>";
      return $return_page; 
    } 
  } 
?>

Важно отметить, что новый метод автоматически начинает работать во всех производных классах pager_file, pager_file_search, pager_dir, pager_mysql, и для объектов каждого класса автоматически выбираются правильные методы get_total(), get_pnumber(), get_page_link() и get_parameters().
Метод print_page() можно вызывать вместо содержимого метода __toString() или строку echo $obj в листингах 4.30, 4.32, 4.33, 4.35 и 4.39 заменить на строку echo $obj->print_page()

На рис. 4.11 представлен внешний вид постраничной навигации при вызове метода print_page() для объекта класса pager_dir.

pager_oop6
Рис. 4.11. Альтернативный вариант постраничной навигации

Абстрактные классы

На примере постраничной навигации было продемонст- рировано построение типичной иерархии классов с применением полиморфизма. Однако объект класса pager не имеет смысла и не может быть использован по назначению, и для того, чтобы предотвратить объявление объекта класса pager, пришлось снабдить его конструктор спецификатором protected, запрещающим объявление объекта из внешнего кода.

Однако объектно-ориентированная модель PHP предоставляет специальные инструменты для классов, появление объектов которых нежелательно: класс можно объявить абстрактным.
Для этого объявление класса предваряют ключевым словом abstract (листинг 4.41).

Листинг 4.41. Объявление абстрактного класса

<?php 
  abstract class pager 
  { 
    ... 
  } 
?>

Теперь попытка объявить экземпляр класса pager (листинг 4.42) будет приводить к возникновению ошибки: Fatal error: Cannot instantiate abstract class pager (Невозможно объявить объект абстрактного класса).

Листинг 4.42. Попытка объявления объекта абстрактного класса

<?php 
  require_once("class.pager.php"); 
 
  // $obj = new pager(); // Ошибка 
?>

pager_oop7
Рис. 4.12. Схема транспортной сети

Абстрактными следует объявлять классы, которые не существуют в реальности и объекты которых не понадобятся.
Например, в схеме транспортной сети очевидно, что объекты "транспорт", "автомобиль", "воздушный транспорт", "водный транспорт" и "железнодорожный транспорт" не понадобятся, и их можно сделать абстрактными.
В то же время конкретные классы ("лодка", "баржа", "теплоход", "катер") могут понадобиться для оценки грузо- и пассажиропотока, расчета налогообложения и т. п. (рис. 4.12).

Абстрактные методы

Если во время работы над сайтом появляется новый источник данных, к которому нужно будет применить постраничную навигацию, достаточно унаследовать от базового класса pager новый производный класс.

Однако продемонстрированный подход обладает некоторым изъяном: программист может забыть реализовать один из методов, который используется базовым классом. Напомним, что в базовом классе pager находятся только заглушки методов get_total(), get_pnumber(), get_page_link() и get_parameters().

Заглушками в программировании называют методы, которые не выполняют никакой работы.

Для решения этой задачи в объектн-ориентированной модели PHP предусмотрены абстрактные методы, объявление которых предваряется ключевым словом abstract.
Для абстрактных методов задают их описание и список параметров без реализации.

Каждый класс, который наследуется от базового класса, содержащего абстрактные методы, обязан их реализовать.
Если хотя бы один метод не реализован — работа PHP-скрипта будет остановлена, а интерпретатор сообщит о необходимости реализации абстрактного метода в производном классе.

При помощи абстрактных методов исключается воз- можность неумышленного нарушения полиморфизма для производных классов.

В листинге 4.43 методы get_total(), get_pnumber(), get_page_link() и get_parameters() класса pager объявляются абстрактными.

Листинг 4.43. Объявление абстрактных методов

<?php 
  abstract class pager 
  { 
    abstract function get_total(); 
    abstract function get_pnumber(); 
    abstract function get_page_link(); 
    abstract function get_parameters(); 
 
    ... 
 
  } 
?>

Наличие в классе абстрактного метода требует, чтобы класс также был объявлен абстрактным.

Невозможно объявить класс не абстрактным, если он содержит абстрактные методы.

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

В листинге 4.44 приводится пример класса pager_abstract, который является абстрактным производным классом, унаследованным от класса pager.

Листинг 4.44. Абстрактный класс может не реализовывать абстрактные методы

<?php 
  // Подключаем базовый класс 
  require_once("class.pager.php"); 
 
  abstract class pager_abstract extends pager 
  { 
    // Имя директории 
    protected $dirname; 
    // Количество позиций на странице 
    protected $pnumber; 
    // Количество ссылок слева и справа 
    // от текущей страницы 
    protected $page_link; 
    // Параметры 
    protected $parameters; 
    // Конструктор 
    public function __construct($dirname, 
                                $pnumber = 10, 
                                $page_link = 3, 
                                $parameters = "") 
    { 
      // Удаляем последний символ /, если он имеется 
      $this->dirname    = trim($dirname, "/"); 
      $this->pnumber    = $pnumber; 
      $this->page_link  = $page_link; 
      $this->parameters = $parameters; 
    } 
    public function get_pnumber() 
    { 
      // Количество позиций на странице 
      return $this->pnumber; 
    } 
    public function get_page_link() 
    { 
      // Количество ссылок слева и справа 
      // от текущей страницы 
      return $this->page_link; 
    } 
    public function get_parameters() 
    { 
      // Дополнительные параметры, которые 
      // необходимо передать по ссылке 
      return $this->parameters; 
    } 
  } 
?> 

Если класс, унаследованный от класса pager_abstract, не является абстрактным, он должен реализовать все абстрактные методы всех абстрактных классов, которые предшествуют ему в иерархии классов.

Например, абстрактный класс pager_abstract реализует три абстрактных метода класса pager — get_pnumber(), get_page_link() и get_parameters(), поэтому производным классам достаточно реализовать лишь метод get_total() (листинг 4.45).

Листинг 4.45. Использование класса pager_abstract

<?php 
  // Подключаем базовый класс 
  require_once("class.pager_abstract.php"); 
 
  class pager_dir extends pager_abstract 
  { 
    // Конструктор 
    public function __construct($dirname, 
                                $pnumber = 10, 
                                $page_link = 3, 
                                $parameters = "") 
    { 
      // Удаляем последний символ /, если он имеется 
      $this->dirname    = trim($dirname, "/"); 
      $this->pnumber    = $pnumber; 
      $this->page_link  = $page_link; 
      $this->parameters = $parameters; 
    } 
    public function get_total() 
    { 
      $countline = 0; 
      // Открываем директорию 
      if(($dir = opendir($this->dirname)) !== false) 
      { 
        while(($file = readdir($dir)) !== false) 
        { 
          // Если текущая позиция является файлом, 
          // учитываем ее 
          if(is_file($this->dirname."/".$file)) $countline++; 
        } 
        // Закрываем директорию 
        closedir($dir); 
      } 
      return $countline; 
    } 
    // Возвращает массив строк файла 
    // по номеру страницы $index 
    public function get_page() 
    { 
      // Текущая страница 
      $page = $_GET['page']; 
      if(empty($page)) $page = 1; 
      // Количество записей в файле 
      $total = $this->get_total(); 
      // Вычисляем количество страниц в системе 
      $number = (int)($total/$this->get_pnumber()); 
      if((float)($total/$this->get_pnumber()) — $number != 0) $number++; 
      // Проверяем, попадает ли запрашиваемый номер 
      // страницы в интервал от 1 до get_total() 
      if($page <= 0 || $page > $number) return 0;
      // Извлекаем позиции текущей страницы 
      $arr = array(); 
      // Номер, начиная с которого следует 
      // выбирать строки файла 
      $first = ($page — 1)*$this->get_pnumber(); 
      // Открываем директорию 
      if(($dir = opendir($this->dirname)) === false) return 0; 
      $i = -1; 
      while(($file = readdir($dir)) !== false) 
      { 
        // Если текущая позиция является файлом 
        if(is_file($this->dirname."/".$file)) 
        { 
          // Увеличиваем счетчик 
          $i++; 
          // Пока не достигнут номер $first, 
          // досрочно заканчиваем итерацию 
          if($i < $first) continue; 
          // Если достигнут конец выборки, 
          // досрочно покидаем цикл 
          if($i > $first + $this->get_pnumber() — 1) break; 
          // Помещаем пути к файлам в массив, 
          // который будет возвращен методом 
          $arr[] = $this->dirname."/".$file; 
        } 
      } 
      // Закрываем директорию 
      closedir($dir); 
 
      return $arr; 
    } 
  } 
?>

 на главную сниппетов

Курсы