Задача:
Использовать класс, в который можно добавлять новые методы воздействия, чтобы в последующем можно было ими пользоваться. При этом отделить эти методы по разным файлам.
Представим космический корабль, в котором используются:
a) методы перемещения по пространству, связанные с двигателем
б) методы зарядки энергии, связанные с его солнечными батареями
в) итд
Есть варианты:
1) Создание в объекте переменных и инициализация их через __construct, как новые классы.
Но, при создании каждого нового объекта — мы получаем снижение производительности и постоянную модификацию класса (что может осложнять работу нескольких программистов).
2) Работа через функции __call, __get, __set.
В 3-10 раз более медленная работа этих функций. Особенно при вызове call_user_func_array с параметрами.
Я предлагаю свой вариант.
Быстрый, оптимизированный, но при этом есть небольшая сложность в отладке.
В этом хабратопике я затрону темы:
1. Overloading
2. Ускорение загрузки модулей. Объединение модулей.
3. Оптимизация
Мы можем объединить все модули в один файл, при этом одинаковые классы сливая в один.
Для этого я написал модуль, который разберу чуть попозже.
Для работы с модулем, используется (похожая на Singleton pattern) — функция mods. Она инициализирует модуль и выглядит так:
Для работы объединяющего модуля, используется два кеш файла.
Один из них необходим для того, чтобы хранить все подключаемые классы (в serialize виде). Из них, с помощью функции __autoload, выбираются нужные, что необходимы для работы.
Второй файл — все необходимые модули, слитые воедино в php-файле. Он как раз подгружается, вместо всех модулей.
Инициализация этих двух файлов происходит в начале, при инициализации модуля:
За счёт объединения всех файлов в один, загрузка файлов идёт быстрее. (плюс, советую установить себе eAccelerator, для того, чтобы файлы загружались мгновенно)
Но, необходимо ещё добавить модули, которые подгружаются в систему (это работает, как простой include):
Хочу обратить внимание на define PHP_DEBUG_MODE. Штудировать все модули на проверку изменений каждый раз — нет нужды. По этому, когда внтури модули не будут меняться, можно эту проверку отключить (приравняв PHP_DEBUG_MODE к 0), оставляя лишь подключение готового cache-php-модуля (include_modules), содержащего все необходимые классы.
2) Слияние функций внутри классов
Пользуйтесь на здоровье.
Использовать класс, в который можно добавлять новые методы воздействия, чтобы в последующем можно было ими пользоваться. При этом отделить эти методы по разным файлам.
Представим космический корабль, в котором используются:
a) методы перемещения по пространству, связанные с двигателем
б) методы зарядки энергии, связанные с его солнечными батареями
в) итд
Есть варианты:
1) Создание в объекте переменных и инициализация их через __construct, как новые классы.
Но, при создании каждого нового объекта — мы получаем снижение производительности и постоянную модификацию класса (что может осложнять работу нескольких программистов).
2) Работа через функции __call, __get, __set.
В 3-10 раз более медленная работа этих функций. Особенно при вызове call_user_func_array с параметрами.
Я предлагаю свой вариант.
Быстрый, оптимизированный, но при этом есть небольшая сложность в отладке.
В этом хабратопике я затрону темы:
1. Overloading
2. Ускорение загрузки модулей. Объединение модулей.
3. Оптимизация
Альтернативный подход
Мы можем объединить все модули в один файл, при этом одинаковые классы сливая в один.
Для этого я написал модуль, который разберу чуть попозже.
Для работы с модулем, используется (похожая на Singleton pattern) — функция mods. Она инициализирует модуль и выглядит так:
/**
* Синглетон паттерн для modules
*
* @return <modules> - класс
*/
function mods(){
static $mod;
if (!isset($mod))
$mod=new modules;
return $mod;
}
Для работы объединяющего модуля, используется два кеш файла.
Один из них необходим для того, чтобы хранить все подключаемые классы (в serialize виде). Из них, с помощью функции __autoload, выбираются нужные, что необходимы для работы.
Второй файл — все необходимые модули, слитые воедино в php-файле. Он как раз подгружается, вместо всех модулей.
Инициализация этих двух файлов происходит в начале, при инициализации модуля:
mods()->cache('cache.data')
->phpcache('cache.php');
Файлы можно задавать разные. Я, например на разных страницах сайта (на которых используются разные модули), использую разные phpcache файлы. За счёт объединения всех файлов в один, загрузка файлов идёт быстрее. (плюс, советую установить себе eAccelerator, для того, чтобы файлы загружались мгновенно)
Но, необходимо ещё добавить модули, которые подгружаются в систему (это работает, как простой include):
mods()->include_php('modules/mod1.php');
И в конце сконвертировать модули в один файл, подгружая его:mods()->include_modules();
Пример работы с модулями
//Режим проверки всех подключаемых файлов (когда идёт редактирование модулей)
define ('PHP_DEBUG_MODE',1);
//Инициализация файлов кеша для всех модулей и объединенного php-кода
mods()->cache('cache.file')
->phpcache('cache.php');
//Добавляем модули
if (PHP_DEBUG_MODE==1){
$files = glob($moddir.'/*.inc');
foreach ($files as $file){
mods()->include_php($file);
}
//Загружаем все модули
mods()->include_modules();
Хочу обратить внимание на define PHP_DEBUG_MODE. Штудировать все модули на проверку изменений каждый раз — нет нужды. По этому, когда внтури модули не будут меняться, можно эту проверку отключить (приравняв PHP_DEBUG_MODE к 0), оставляя лишь подключение готового cache-php-модуля (include_modules), содержащего все необходимые классы.
Что получится
1) Слияние классов.//file_A.php
class A{
public $start;
function start(){
$this->start=1;
}
}
//file_B.php
class A{
private $stop;
function stop(){
$this->stop=1;
}
}
Результат://result_cache.php
class A{
public $start;
private $stop;
function start(){
$this->start=1;
}
function stop(){
$this->stop=1;
}
}
2) Слияние функций внутри классов
//file_A.php
class A{
public $start;
function __construct(){
$this->start=1;
}
}
//file_B.php
class A{
public $stop;
function __construct(){
$this->stop=1;
}
}
Результат://result_cache.php
class A{
public $stop;
public $start;
function __construct(){
$this->stop=1;
$this->start=1;
}
}
Реализация
/**
* Автоподгрузка неизвестного класса
*
* @param <string> $class - имя неизвестного класса
*/
function __autoload($class){
mods()->putclass($class);
}
/**
* Синглетон паттерн для modules
*
* @return <modules> - класс
*/
function mods(){
static $mod;
if (!isset($mod))
$mod=new modules;
return $mod;
}
class modules{
private $dataclass=array();
private $datacache_date=0;
private $datacache_name=null;
private $classesfile=null;
private $classesfiledate=0;
private $classesincluded=false;
/**
* Устанавливает бинарный файл для кеша
*
* @param <string> $file - путь+имя файла
*/
public function cache($file){
$this->datacache_name=$file;
if (file_exists($file)){
$this->datacache_date=filemtime($file);
}
return $this;
}
/**
* Устанавливает php-файл для кеша
*
* @param <string> $file - путь+имя файла
*/
public function phpcache($file){
$this->classesfile=$file;
if (file_exists($file))
$this->classesfiledate=filemtime($file);
}
/**
* Добавляет модуль в базу
*
* @param <string> $file - название файла модуля
*/
public function include_php($file){
if (!file_exists($file)){
$this->fatalerror('no include file \'<b>'.$file.'</b>\'');
}
if ($this->datacache_date<filemtime($file)){
$this->loadcachedata();
$this->removefile($file);
$this->parsephp(file_get_contents($file),$file);
file_put_contents($this->datacache_name,serialize($this->dataclass));
if ($this->classesfiledate!=0){
unlink($this->classesfile);
$this->classesfiledate=0;
}
}elseif ($this->classesfiledate!=0 && $this->classesfiledate<filemtime($file)){
unlink($this->classesfile);
$this->classesfiledate=0;
}
return $this;
}
/**
* Загружает все обозначенные модули в систему
*/
public function include_modules(){
if (!$this->classesincluded){
if (!file_exists($this->classesfile)){
$this->loadcachedata();
file_put_contents($this->classesfile,"<?\n".$this->printclass('')."\n?>");
}
include_once($this->classesfile);
$this->classesincluded=true;
}
}
/**
* Удаляет модуль из базы
*
* @param <string> $file - файл модуля
*/
private function removefile($file){
foreach($this->dataclass as $key=>&$class){
foreach ($class[0] as $name=>$vars)
if ($name==$file)
unset($class[0][$name]);
foreach ($class[1] as $fk=>&$function){
foreach ($function as $name=>$vars)
if ($name==$file)
unset($function[$name]);
if ($function==array())
unset($class[1][$fk]);
}
if ($class[0]==array() && $class[1]==array())
unset($this->dataclass[$key]);
}
}
/**
* Печатает PHP-классы
*
* @param <string> $class - название класса
* @return <string> - результат
*/
private function printclass($class=''){
if ($class==''){
$string=implode("\n",$this->dataclass[''][0]);
foreach($this->dataclass[''][1] as $fname=>$function)
$string.="\nfunction ".$fname."{\n".implode("\n",$function)."\n}";
return $string;
}
foreach ($this->dataclass as $classname=>$data){
$name=preg_split('/\s/', $classname);
if ($name[0]==$class){
$string='class '.$classname.'{'.implode("\n",$data[0]);
foreach($data[1] as $fname=>$function)
$string.="\nfunction ".$fname."{\n".implode("\n",$function)."\n}";
return $string.'}';
}
}
return false;
}
/**
* Загружает базу из cache-файла
*/
private function loadcachedata(){
if ($this->dataclass!=array())
return;
if (file_exists($this->datacache_name))
$this->dataclass=unserialize(file_get_contents($this->datacache_name));
}
private function classfiles($class){
foreach ($this->dataclass as $classname=>$data){
$name=preg_split('/\s/', $classname);
if ($name[0]==$class){
$keys=array();
foreach ($data[1] as $dat)
$keys=array_merge(array_keys($data[0]),$keys);
return implode(", ",array_unique(array_merge(array_keys($data[0]),$keys)));
}
}
}
/**
* Подгружает необходимые модули.
* Сохраняет их в php-cache файле.
*
* @param <string> $class - имя класса
*/
public function putclass($class){
if (!$this->classesincluded){
$this->includemodules();
if (class_exists($class,false))
return;
}
$this->loadcachedata();
if (($evclass=$this->printclass($class))!==false){
file_put_contents($this->classesfile,"<?\n".$evclass."\n?>",FILE_APPEND);
chmod($this->classesfile,0777);
if (@eval($evclass)===false){
$this->fatalerror('Error in class <b>'.$class.'</b> in file <b> '.$this->classfiles($class).'</b>');
}
}
}
/**
* Добавить класс в базу
*
* @param <string> $classname - имя класса
* @param <array> $class - Составляющие класса
* @param <string> $filename - имя файла, откуда этот класс
*/
private function mergeclass($classname,$class,$filename){
if (!isset($this->dataclass[$classname][0][$filename])){
$this->dataclass[$classname][0][$filename]='';
}
$this->dataclass[$classname][0][$filename].=$class[0];
if (!isset($this->dataclass[$classname][1]))
$this->dataclass[$classname][1]=array();
foreach ($class[1] as $fname=>$function){
if (!isset($this->dataclass[$classname][1][$fname][$filename]))
$this->dataclass[$classname][1][$fname][$filename]='';
$this->dataclass[$classname][1][$fname][$filename].=$function;
}
}
/**
* Парсит php файл, выдирая из него классы и функции
*
* @param <string> $php - внутренность файла
* @param <string> $filename - название файла
*/
private function parsephp($php,$filename){
if (($pos=strpos($php,'<?'))!==false){
$pos+=2;
if (strtolower(substr($php,$pos,3))=='php')
$pos+=3;
}
if (!preg_match_all('/((?:[^\'"\/$]|([\'"])(?:\\\\[\s\S]|[\s\S])*\2|\/[^*]|\/\*[\s\S]*\*\/|\$[^;\'"(){}\s]++)*)([{};]|\?>|$|\s(function)\s|\s(class)\s)/DSU',$php,$scobe,PREG_SET_ORDER|PREG_OFFSET_CAPTURE,$pos))
return;
for ($i=0,$j=count($scobe);$i<$j;$i++){
if (isset($scobe[$i][5]) && $scobe[$i][5][0]=='class' && $i<$j-1 && $scobe[$i+1][3][0]=='{'){
//is class
$newclass=array('',array());
for ($x=$i+2;$x<$j;$x++){
$test=&$scobe[$x][3][0];
if (isset($scobe[$x][4][0]) && $scobe[$x][4][0]=='function' && $x<$j-1 && $scobe[$x+1][3][0]=='{'){
$name=$scobe[$x+1][1][0];
$function='';
for ($x+=2,$sco=1;$x<$j;$x++){
$test=&$scobe[$x][3][0];
if ($test=='{')
++$sco;
elseif ($test=='}')
if (--$sco==0)
break;
$function.=$scobe[$x][0][0];
}
if ($x==$j)
$this->fatalerror('error parsing function'.$name.' in class <b>'.$scobe[$i+1][1][0].'</b>, not found }');
$newclass[1][$name]=$function;
}elseif ($test==';')
$newclass[0].=$scobe[$x][0][0];
elseif ($test=='}')
break;
else $this->fatalerror('error parsing '.$test.' in class <b>'.$scobe[$i+1][1][0].'</b>');
}
$this->mergeclass($scobe[$i+1][1][0],$newclass,$filename);
$i=$x;
}else{
if ($scobe[$i][3][0]=='?>')
$this->mergeclass('',array($scobe[$i][1][0],array()),$filename);
else
$this->mergeclass('',array($scobe[$i][0][0],array()),$filename);
}
}
}
/**
* Сообщает об ошибке
*
* @param <string> $error - расшифровка ошибки
*/
private function fatalerror($error){
trigger_error($error."<br/>\n", E_USER_ERROR);
die();
}
}
* This source code was highlighted with Source Code Highlighter.
Пользуйтесь на здоровье.