Подход к реализации динамически подключаемых библиотек (классов) на PHP5
Приветствую всех читающих, ищущих, спотыкающихся и стремящихся рости над собой. Сегодня я бы хотел поразмышлять на тему разработки системы подкючения пакетов функций в рамках платформы PHP. Что же я имею ввиду.
Под пакетами функций, я подразумеваю некоторый набор методов объеденённых относительно семантической зависимости устанавливаемой между ними. Понятие пакета очень хорошо описано в рамках технологии Java, и позволяет создавать более упорядоченные наборы методов, разделяя их относительно их задач и контекста..
Мы же под пакетом будем подразумевать некоторый набор методов и их прототипов (интерфейс класса), зависимых друг от друга по целевому предназанчению.
Структура пакета будет следующей:
packages / {имя_пакета} class.{имя_пакета}.php interface.{имя_пакета}.php errors.{имя_пакета}.php
То есть пакет функций будет разделятся на:
- Прототипы методов, описанные в рамках интерфейса, описываемого основным классом пакета (идентификатор интерфейса должен соответствовать идентификатору класса, с добавление символа "I", в качестве последнего символа справа в тексте идентификатора)
- Непосредственная реализация прототипов методов, объявлённых в интерфейсе пакета, в основном классе пакета (идентификаторру класса должно соответствовать названию пакета)
- Информация относительно исключений, которые могут быть возбуждены во время активации того либо иного метода входящего в реализацию основного класса.
Так же, последуюя принципам стандартизации, я считаю необходимым ввести некоторую обязательную структуру для каждого класса, а то есть те его методы и свойства, которые должны в нём присутствовать в любом случае, такой подход очень полезен при модульной структуре информационной системы, и при условии того, что система расширяется не только непосредственными разработчиками, но и третьими лицами. При этом это вводит некую упорядоченность и "чистоту" пакетов функций подключаемых к системе.
Среди обязательных компонентов подключаемого модуля я предпочитаю выделять следующие обязательные составные:
Св-ва:
- Версия данного пакета
- Информационный массив об разработчиках пакета
Методы:
- Основной метод класса, реализующий основную логику класса
- Функция для получения версии данного пакета (стандарт ООП)
- Фукнция для получения информационного массива об авторах(е) пакета
Продолжать данный список можно в зависимости от требований к информационной системе и реализуемых в её контексте задач. Заниматься его совершенствованием и корректировкой предстоит вам.
Это была небольшой обзор структурной части системы, теперь же давайте поговорим об подключении и создании экземпляров основных классов непосредственно.
Ниже приведён код для подключения заданных пользователем пакетов, и добавления ссылок на них в ассоциативный массив, в котором будут хранится экземпляры классов. Подход реализован с использованием процедурного программирования, так как в данном случае это будет более универсальное и менее проблематичное с точки зрения реализации решение.
<?php $path_to_packages='core/kernel'; //Путь к директории, содержащей библиотеки $instances=array(); //Массив ссылок на объекты классов $package_members=array('errors','interface','class'); //Составные пакетов данных $packages_to_include=array(); //Подключаемые пакеты (данные добавляются по-методу //функции registerPackage($indefier) function registerPackage($indefier){ global $path_to_packages,$packages_to_include; if(file_exists($path_to_packages.'/'.$indefier)){ $packages_to_include[]=$indefier; }else{ return false; } return true; } //Функция реализующая непосредственное подключение библиотеки к программе function includePackage($indefier){ global $instances,$path_to_packages,$package_members; if(trim($indefier)!=''){ //Подключить все компоненты пакета foreach($package_members as $k=>$v){ $member=$path_to_packages.'/'.$indefier.'/'.$v.'.'.$indefier.'.php'; if(!file_exists($member)){ return false; }else{ if(!include($member)) return false; } } //Добавить экзмепляр класса в коллекцию $instances[] if(class_exists($indefier) && !isset($instances[$indefier])){ //Проверка вхождения обязательных компонент в пакет $err=0; foreach($main_pieces as $k=>$v){ //Входит ли данный метод в список методов класса //(проверку вхождения полей добавите сами ;) ). if(!in_array($v,get_class_methods(get_class($indefier)))) $err=1; break; } } if(!$err) $instances[$indefier]=new $indefier(); else return(false); } }else{ return false; } return true; } //Функция для подключения всех зарегистрированных пакетов function loadLibs(){ global $packages_to_include; foreach($packages_to_include as $k=>$v){ if(!includePackage($v)){ return false; } } return true; } ?>
Ну вот и всё, как видите всё довольно просто. Хотя в этой реализации есть однин весомый недочёт, и не упомянуть о котором было бы надеждой, что данную статью читает неопытный читатель, либо же просто показать собственное незнание.
Но при написании не подразумевался не тот ни другой случай, а скорее расчитывалось на собственную работу читателя, которая очень положительна в любом случае. Я не буду приводить описания данной проблемы, и не буду приводить её решения, я лишь скажу, что нарушается основной подход к реализации возвращения функцией данных, а то есть одной из главных проблем стиля программирования.
Итак, чтобы показать всё это "чудо" на практике приведу пример небольшого класса, который реализует общение с удалённым сервером посредствам сокетов:
Название пакета: csc (Cross Server Communicator)
Файл: errors.csc.php
<?php define('CONNECTION_ERROR',21); define('CONNECTION_SUCCESSFUL',3); define('CONNECTION_EXISTS',205); define('REQUEST_SUCCESSFUL',2); define('REQUEST_FAILED',51); define('INCORRECT_METHOD',31); define('POINTER_NE',7); define('CONNECTION_NOT_ESTABILISHED',86); define('WRONG_DATA',11); ?>
Файл: interface.csc.php
<?php interface cscI{ private function setPort($port); private function correctMethod($method); public function openConnection($host); public function sendQuery($method,$uri); public function isError($code); public function readAnswer($cut_headers=false); public function closeConnection(); public function logon(); } ?>
Файл: class.csc.php
<? class csc implement cscI { private $_package=' Cross Server Communication Library'; private $_version=0.1; private $_author=array('company'=>'Transfer of New Technologyes', 'author'=>'K.Karpneko'); private $_space=" "; private $_crlf="\r\n"; private $_host=''; private $_port=80; private $_protocol='HTTP/1.1'; private $_timeout=30; private $_err_str=''; private $_err_no=0; private $_answer=''; private $_errors_codes=array(21,205,51,86,31,11,7); private $_status=200; private $_server_info=''; private $_request=''; var $_conn_id=null; public function setPort($port){ //Реализация } public function openConnection($host){ if(!$this->_conn_id){ $this->_host=$host; $this->_conn_id=fsockopen( (eregi('http://',$this->_host)? str_replace('http://','',$this->_host):$this->_host), $this->_port,$this->_err_no,$this->_err_str, $this->_timeout); if(!$this->_conn_id){ return CONNECTION_ERROR; }else{ return CONNECTION_SUCCESSFUL; } }else{ return CONNECTION_EXISTS; } } private function correctMethod($method){ switch($method){ case 'POST': case 'HEAD': case 'GET': case 'PUT': case 'TRACE': return true; default: return false; } } public function sendQuery($method,$uri){ if($this->_conn_id){ if(trim($method)!='' && trim($uri)!=''){ $uri=substr($uri,strpos('?',$uri),strlen($uri)); $uri=explode('&',$uri); foreach($uri as $k=>$v){ $v=explode('=',$v); $v=$v[0].'='.rawurlencode($v[1]); $uri[$k]=$v; } $uri=implode('&',$uri); $this->_request=''; if($this->correctMethod($method)){ $this->_request.=$method.$this->_space; $this->_request.=$uri.$this->_space; $this->_request.=$this->_protocol.$this->_crlf; $this->_request.='Host: '.$this->_host.$this->_crlf; $this->_request.='Connection: Close'.$this->_crlf.$this->_crlf; #die($this->_request); $this->_answer=fwrite($this->_conn_id,$this->_request); if($this->_answer){ return REQUEST_SUCCESSFUL; }else{ return REQUEST_FAILED; } }else{ return INCORRECT_METHOD; } }else{ return WRONG_DATA; } }else{ return CONNECTION_NOT_ESTABILISHED; } } public function isError($code){ return(in_array($code,$this->_errors_codes)?true:false); } public function readAnswer($cut_headers=false){ $this->_result=''; if($this->_conn_id){ if($this->_answer){ while(!feof($this->_conn_id)){ $this->_result.=fread($this->_conn_id,4096); } if($cut_headers){ $this->_result=substr($this->_result,0,1024); } }else{ return POINTER_NE; } }else{ return CONNECTION_NOT_ESTABILISHED; } return $this->_result; } public function logon(){ //METHOD NOT IMPLEMENTED } public function closeConnection(){ return(isset($this->_conn_id)?fclose($this->_conn_id):CONNECTION_NOT_ESTABILISHED); } } ?>
Далее подразумевается, что функции для динамического подключения были помещены в документ connector.php:
<?php include 'connector.php'; registerPackage('csc'); if(!loadPackages()) die('Critical system error !'); print $instances['csc']; //В данном случае должен быть возвращён идентификатор ресурса ?>
При этом следует заметить, что при реализации механизма создания экземпляра основного класса подключаемых документов, ссылка на класс создаётся лишь раз, а после вызов функции создания просто игнорируется. В целом, я думаю, что на сегодня вполне достаточно, и предлагаю подумать получше об упомянутом в тексте статьи недочёте )
Целую и обнимаю всех кто это читает, успехов вам !
Ваш К.Карпенко, компания ИНПП "Трансфер Новых Технологий - 43"
Оставить комментарий
Комментарии
Тогда загрузка класса (пакета, библиотеки, как угодно назовите) превратится в:
$classTest = new classTest();
?>