Создание пользовательских обвёрток файловых функций

В этой статье будет расказано как создавать пользовательские обвёртки файловых функций. Сделаю я это на примере парсера для Atom.
Так-же рекомендую обратится к первоисточнику.

   Вводные данные:

  • Обвёртка (wrapper) позволяет расширить стандартные файловые функции (fopen(), readdir()…) для работы с разнообразными протоколами и
    типами данных. Использовать их просто: вызов fopen(‘http://www.google.com’); использует обвёртку "http"
    Посмотреть список системных обвёрток можно здесь: http://php.net/manual/en/wrappers.php.

  • Atom — это основанный на XML формат, предназначенный для новостных лент, анонсов статей и так-далее.
    От RSS отличается, но служит для тех-же целей. Как-правило, даже если используют Atom всеравно пишут RSS.
    И собственно Atom-файл:

    <?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
    	<title> Feed Title </title>
    	<link href=" http://yourwebsite.com/"/>
    	<updated>2003-12-13T18:30:02Z</updated>
    	<author>
    		<name>Your Name</name>
    	</author>
    	<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
    	<entry>
    		<title>Article Title</title>
    		<link href=" http://yourwebsite.com/articlelink.html "/>
    		<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
    		<updated>2003-12-13T18:30:02Z</updated>
    		<summary>Some text.</summary>
    	</entry>
    	<entry>
    		<title>Sports</title>
    		<link href=" http://yourwebsite.com/sportslink.html "/>
    		<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344e45ab90</id>
    		<updated>2003-12-14T13:30:55Z</updated>
    		<summary>Some text.</summary>
    	</entry>
    </feed>

   Описание:
bool stream_wrapper_register(string $protocol, string $classname)
— регистрирует пользовательскую обвёртку. Возвращает true в случае успеха и false в противном случае.
Если обвёртка уже существует, то функция ее не переопределяет и возвращает false. В этом случае нужно делать так:

//Удаляем существующую обвёртку
stream_wrapper_unregister('http');

...

//Регистрируем свою
stream_wrapper_register('http', 'CustomHTTP');

//Здесь используем свою обвёртку
...

//Востанавливаем предыдущую обвёртку
stream_ wrapper_ restore('http');

bool stream_wrapper_unregister(string $protocol)
bool stream_wrapper_restore(string $protocol)
Первая функция удаляет существующую обвёртку, вторая — востанавливает удаленную раннее обвёртку.
Обе функции возвращают true в случае успеха и false в противном случае.

В класе обвёртке нужно определить методы для файловых функций.
Они должны быть определены именно так, как указано ниже. Шаг влево, шаг вправо карается багами!
bool stream_open(string $path, string $mode, int $options, string $opened_path)
   Этот метод вызывается сразу после открытия потока функцией fopen().
   $path — имя файла, переданоё в fopen().
   $mode — режим открытия файла из fopen().
   $options — содержит дополнительные флаги, переданные системой.
Опции соеденены через OR.
     STREAM_USE_PATH — если $path содержит относительный путь,
указывает на поиск ресурса с использованием include_path
     STREAM_REPORT_ERRORS — если флаг установлен, метод несет ответственность за установку ошибок
через trigger_error(), в противном случае метод не должен выставлять никаких ошибок.
   $opened_path — если ресурс открыт успешно и в $options установлен
флаг STREAM_USE_PATH, метод должен вернуть сюда полный путь к открытому ресурсу.
void stream_close(void)
   Метод вызывается при закрытии потока, используя fclose().
string stream_read(int $count)
   Метод вызывается при использывании fread() и fgets().
Метод должен вернуть не более $count байт, начиная с текущей позиции файлового указателя.
Если байти закончились — нужно вернуть FALSE. Так-же нужно сместить файловый указатель на количество успешно прочитаных байт.
Далее кратко.
bool stream_eof(void) — для feof();
int stream_tell(void) — для ftell();
bool stream_seek(int $offset, int $whence) — для fseek();
bool stream_flush(void) — для fflush();
array stream_stat(void) — для fstat();
bool unlink(string $path) — для unlink() PHP >= 5.0.0;
bool rename(string $path_from, string $path_to) — для rename() PHP >= 5.0.0;
bool mkdir(string $path, int $mode, int $options) — для mkdir() PHP >= 5.0.0;
bool rmdir(string $path, int $options) — для rmdir() PHP >= 5.0.0;
array url_stat(string $path, int $flags) — для rmdir();

bool dir_opendir(string $path, int $options) — для opendir();
   Все функции ниже работают посе dir_opendir().
string dir_readdir(void) — для readdir() ;
bool dir_rewinddir(void) — для rewinddir();
bool dir_closedir(void) — для closedir();

   Кодинг:
Класс написан на скорую руку и служит только для иллюстрации принципа написания класса обвёртки. Имена тегов заране известны, документ валиден, проверки отсутствуют, коментарии на русском.

<?php
function dump($aData){
	echo '<pre>';
	print_r($aData);
	echo '</pre>';
}

class AtomStreem{
	private $handler;
	private $raw_data = array();
	private $data = array();

	private function _GetEntryes(){
		$i = 0;// Счётчик елементов
		$entry = false;
		foreach ($this->raw_data as $idx => $row) {
			if (strpos($row, '<entry>') !== false)
				$entry = true;
//			Начало элемента
			if (strpos($row, '</entry>') !== false){
				$entry = false;
				$i++;
			}
//			Конец элемента
			if ((strpos($row, '<title>') !== false) && $entry){
//				Если в строке есть "<title>" и строка в элементе
				$row = str_replace('<title>', '', $row);
				$row = str_replace('</title>', '', $row);
				$row = trim($row);
//				Обрезаем всё лишнее
				$this->data[$i]['title'] = $row;
				unset($this->raw_data[$idx]);
//				Удаляем обработанную строку
			}
			if ((strpos($row, '<link ') !== false) && $entry){
				$row = str_replace('<link href="', '', $row);
				$row = str_replace('"/>', '', $row);
				$row = trim($row);
				$this->data[$i]['link'] = $row;
				unset($this->raw_data[$idx]);
			}
			if ((strpos($row, '<id>') !== false) && $entry){
				$row = str_replace('<id>', '', $row);
				$row = str_replace('</id>', '', $row);
				$row = trim($row);
				$this->data[$i]['id'] = $row;
				unset($this->raw_data[$idx]);
			}
			if ((strpos($row, '<updated>') !== false) && $entry){
				$row = str_replace('<updated>', '', $row);
				$row = str_replace('</updated>', '', $row);
				$row = trim($row);
				$this->data[$i]['updated'] = $row;
				unset($this->raw_data[$idx]);
			}
			if ((strpos($row, '<summary>') !== false) && $entry){
				$row = str_replace('<summary>', '', $row);
				$row = str_replace('</summary>', '', $row);
				$row = trim($row);
				$this->data[$i]['summary'] = $row;
				unset($this->raw_data[$idx]);
			}
		}
	}

	private function _GetInfo(){
//		Обрабатываем остатки данных
		foreach ($this->raw_data as $row) {
			if (strpos($row, '<title>') !== false){
				$row = str_replace('<title>', '', $row);
				$row = str_replace('</title>', '', $row);
				$row = trim($row);
				$this->data['title'] = $row;
			}
			if (strpos($row, '<link href="') !== false){
				$row = str_replace('<link href="', '', $row);
				$row = str_replace('"/>', '', $row);
				$row = trim($row);
				$this->data['link'] = $row;
			}
			if (strpos($row, '<updated>') !== false){
				$row = str_replace('<updated>', '', $row);
				$row = str_replace('</updated>', '', $row);
				$row = trim($row);
				$this->data['updated'] = $row;
			}
			if (strpos($row, '<name>') !== false){
				$row = str_replace('<name>', '', $row);
				$row = str_replace('</name>', '', $row);
				$row = trim($row);
				$this->data['name'] = $row;
			}
			if (strpos($row, '<id>') !== false){
				$row = str_replace('<id>', '', $row);
				$row = str_replace('</id>', '', $row);
				$row = trim($row);
				$this->data['id'] = $row;
			}
		}
	}

	private function _Parce(){
		$this->_GetEntryes();
		$this->_GetInfo();
	}

	/**
	 * Функция для fopen()
	 *
	 * @param string $path
	 * @param string $mode
	 * @param int $options
	 * @param string $opened_path
	 * @return bool
	 */
	public function stream_open($path, $mode, $options, $opened_path){
		$_path = parse_url($path);
		$_path = $_path['host'] . $_path['path'];
//		Предыдущее 2 строчки извлекают из строки $path сщбственно путь,
//		отбрасывая схему ("atom://" в нашем случае).
		$this->handler = fopen($_path, $mode);
		return true;
	}

	/**
	 * Функция для fgets()
	 *
	 * @param int $count
	 * @return string
	 */
	public function stream_read($count){
		while (($row = fgets($this->handler)) !== false){
//		($row = fgets($this->handler)) !== false - присваивание в условии - плохой тон.
//		Дедаем из присваивания условие
			$this->raw_data[] = $row;
		}
//		Помещаем содержимое файла в свойство $this->data.
		$this->_Parce();
//		Парсим содержимое файла
		return serialize($this->data);
//		serialize() - потому-что функция должна возвращать string
	}

	/**
	 * Функция для fclose()
	 */
	public function stream_close(){
//		Здесь освобождаем ресурсы
		fclose($this->handler);
		unset($this->handler);
		unset($this->raw_data);
		unset($this->data);
	}

	/**
	 * Функция для feof()
	 * У функции fgets(), в отличии от fread() параметр $length необязателен.
	 * На самом деле fgets() - это цикл, вызывающий fread() с длинной 1байт и feof().
	 *
	 * @return bool
	 */
	public function stream_eof(){
		if (count($this->data))
			return true;
		return false;
	}
}

stream_wrapper_register('atom', 'AtomStreem');
// Регистрируем обвёртку
$fp = fopen('atom://./atom.xml', 'rb');
dump(unserialize(fgets($fp)));
// unserialize() - смотри метод stream_read()
fclose($fp);
?>

Результат работы:

Array
(
    [0] => Array
        (
            [title] => Article Title
            [link] => http://yourwebsite.com/articlelink.html
            [id] => urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a
            [updated] => 2003-12-13T18:30:02Z
            [summary] => Some text.
        )

    [1] => Array
        (
            [title] => Sports
            [link] => http://yourwebsite.com/sportslink.html
            [id] => urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344e45ab90
            [updated] => 2003-12-14T13:30:55Z
            [summary] => Some text.
        )

    [title] => Feed Title
    [link] => http://yourwebsite.com/
    [updated] => 2003-12-13T18:30:02Z
    [name] => Your Name
    [id] => urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6
)

Вот, собственно, и всё.

Запись опубликована в рубрике PHP. Добавьте в закладки постоянную ссылку.

6 комментариев: Создание пользовательских обвёрток файловых функций

  1. Igurok говорит:

    http://php.net/manual/ru/wrappers.php — ссылка не работает.

  2. Alex говорит:

    Так почему так и не поставили капчу? Вроде на WP легко

    • admin говорит:

      потому-что поставил wp-spamfree.
      Раздражает меня капча. Думаю, многих тоже.

  3. Женя говорит:

    Без каптчи вам так они всё заспамят…

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

This blog is kept spam free by WP-SpamFree.