hook и ООП

12 3
mendel
На сайте с 06.03.2008
Offline
183
2208

хукать функции просто - где вызвали хук, там он и работает...

с ооп сложнее - что хукать не понятно - конкретный объект или весь класс?

и если и то и другое, то кому давать приоритет? сначала действует хук класса, а потом уже если есть хук для конкретного объекта?

Шутку любишь над Фомой, так люби и над собой. (с) народ. Бесплатные списки читабельных(!) свободных доменов (http://burzhu.net/showthread.php?t=2976) (5L.com) Сайты, All inclusive. 5* (/ru/forum/962215)
Антон Лавеев
На сайте с 31.10.2005
Offline
425
#1

Кстати, а зачем эти хуки вообще? Ни разу не мог найти применению этой технологии. Для чего переопределять функции, а тем более классы?

☠️☠️☠️
Dreammaker
На сайте с 20.04.2006
Offline
570
#2

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

mendel
На сайте с 06.03.2008
Offline
183
#3

Суть вот в чем:

Я написал ядро ЦМС.

Dreammaker, написал под нее модуль... ну скажем блог.

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

Что делать? Лезть в чужой код, а потом отслеживать каждую новую версию?

хуки дают возможность Dreammaker-у в каждой функции/методе сделать хук (обычно в начале и в конце функции) и тогда Tarry спокойно сможет повесить хук на функцию добавления комментария и спокойненько записав нужную информацию отдать дальше управление основной функции (отдается автоматом).

Или к примеру еще:

я написал ядро.

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

Конечно можно влезть в код ядра и сделать "девелоперскую" версию/сборку, но проще хукнуть функции $istio->lang() и $istio->say() если конечно я не забыл прописать для них хуки :)

т.е. хуки это те же события.

Но вопрос не в том.

Вопрос - в методе класса есть скажем return $istio->hook($return);

дальше в $istio->hook() мы определяем что нас вызвали из такого-то класса и такого-то объекта...

смотрим в таблице зарегистрированных хуков есть ли у нас хук для этого события?

вот тут и вопрос - хукать конкретный экземпляр или класс?

или и то и другое?

но если хукать и класс и экземпляр, то как им приоритет раздать, если есть оба?

Dreammaker
На сайте с 20.04.2006
Offline
570
#4

mendel, я так понимаю - такие идеи это тяжёлое наследство от использования CodeIgniter?

Посмотрите как реализованы события в Yii. Имхо, это более удобно и гибко.

Реализовав в своей модели свой метод afterSave() я могу перекрыть уже существующий, а если мне нужно чтобы выполнился ещё и код и из родительского класса, я в нужно месте пишу parent::afterSave();

по крайней мере если мне память не изменяет :)

p.s. Если вы вообще пишете на CI свою CMS, то тут я удаляюсь. Я пытаюсь забыть CI как страшный сон, там многое сделано не для людей.

mendel
На сайте с 06.03.2008
Offline
183
#5

пуф...

Почему наследие CodeIgniter я могу предположить, хоть ни разу в жизни не видел этот фреймворк.

В чем-то мой фреймворк похож на CodeIgniter - мой тоже будут критиковать за простоту :)

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

Черт, я всегда считал что я очень хорошо умею объяснять.

Ну почему еще ни разу не было, чтобы на мой вопрос ответили когда я спрашиваю по архитектуре совет? Неужели я такой тупой?

Dreammaker
На сайте с 20.04.2006
Offline
570
#6

mendel,

смотрите

index.php


<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}

$class = new $_GET['module'];
$class->afterSave();

BaseModule.php


<?php
class BaseModule
{
public function afterSave()
{
echo 'это событие afterSave базового модуля';
}
}

?>

News.php


<?php
class News extends BaseModule
{
public function __construct()
{
echo 'мы запустили модуль News';
}

public function afterSave()
{
echo 'это событие afterSave модуля News<br />';
parent::afterSave();
}
}
?>

Вызываем:

http://oursite.ru/index.php?module=News

Где-то так. пример понятно, что никакой, но суть, как мне кажется он раскрывает.

p.s. Разработчик модуля волен делать с событием, что ему угодно, механизм же хуков несколько ограничивает процесс творчества.

p.p.s. Где-то я ступил, сейчас посмотрю :D

p.p.p.s. Не было вызова $class->afterSave(); в index.php - поправил. Пример можно пробовать, работает так как задумывалось.

mendel
На сайте с 06.03.2008
Offline
183
#7

$class = new $_GET['module'];

конечно жесть, но думаю это вы для простоты примера :)

Но я не о том.

То что пхп хоть extends поддерживает я знаю :)

Я о другом.

Вот есть у меня:

.htaccess

RewriteEngine on

Options +FollowSymlinks
RewriteBase /
RewriteRule ^.*$ core/istio.php

в core/istio.php:


...поскипано...
// Инициируем ядро
GLOBAL $istio; $istio = istio::init();
// Если есть файл описания проекта то выполним его
if(file_exists('./../project.php')) require_once('./../project.php');
// Выполним все файлы из папки модулей
foreach(scandir('./../mod/') as $f) if(file_exists('./../mod/'.$f) AND !is_dir('./../mod/'.$f) AND strstr($f,'.php')) require_once('./../mod/'.$f);
// Обработаем запрошенный адрес или сообщим что страница не найдена
$istio->go_map() OR $istio->notfound();
// ***********************************************************************
// ** Основные функции ядра, хранение системных переменных и т.п. **
// ***********************************************************************
class istio
{
...поскипано...
private $_config = array(); // глобальный файл конфигурации
....поскипано.....
public function config($name)
{
if (isset($this->_config[$name])) return $this->_config[$name];
else return FALSE;
}
....поскипано....
// Приватный конструктор может быть вызван только изнутри (singleton)
private function __construct()
{
...поскипано....
// Если есть конфиг то считаем его
if(file_exists('./../config.cfg'))
{
$data = file('./../config.cfg');
foreach ($data as $d)
{
$d = trim($d);//уберем пробелы в начале
if(strstr($d,'=') AND $d{0} <> '#')
{
list($name,$value) = explode('=',$d,2);
if(strstr($name,'[')) { list($arr,$key) = explode('[',$name,2); list($key,) = explode(']',$key,2);
$arr = trim($arr);
$key = trim($key);
if($key <> '') $this->_config[$arr][$key] = trim($value);
else $this->_config[$arr][] = trim($value);
}
else $this->_config[trim($name)] = trim($value);
}
}
}
...поскипано......
}

Вот как к примеру здесь пристроить чтение конфига не из файла а из базы?

или


// ***********************************************************************
// ** Модуль работы с шаблонами страниц и вывода всего в браузер. **
// ***********************************************************************
class _view
{
...поскипано....
private $var_array=array(); // Массив переменных для подстановки
...поскипано....
public function say($name,$data=NULL)
{
// Если данные есть и имя переменной не body то добавим ее в массив
if(!($data===NULL) AND $name<>'body' AND $name<>'BODY') $this->var_array[$name]=$this->escsay($data);
// Если переменная есть, то вернем ее. Если нет, то вернем NULL
if (isset($this->var_array[$name])) return $this->var_array[$name];
else return NULL;
}

public function lang($name)
{
$filename=$this->TEMPL_ROOT. $name . '.lng';
$data=array();
if(file_exists($filename)) $data=file($filename);
foreach ($data as $d)
{
$d=rtrim($d,"\n\r");
$d=ltrim($d);
if($d{0}<>'#')
{
list($name,$value) = explode('=',$d,2);
if(strstr($name,'[')) { list($arr,$key) = explode('[',$name,2); list($key,) = explode(']',$key,2);
$arr = trim($arr);
$key = trim($key);
if($key <> '') $this->var_array[$arr][$key] = $value;
else $this->var_array[$arr][] = $value;
}
else $this->var_array[trim($name)] = $value;
}
}
}

класс _view тоже синглтон и его экземпляр создается из конструктора istio и все его методы вызываются методами того же класса.

Как мне при этом методами ооп-пхп сделать учет того каким образом та или иная переменная оказалась в $var_array - через lang или через say?

Ну это так, к слову...

Ну а если разобрать Ваш код, то как в вашем коде допустим... ну что бы такое придумать.... ну допустим послесохранения новости я хочу теперь ее в лог сохранить.

Как мне это сделать? Причем естественно я хочу сохранить все пути неизменными, потому как из-за моего модуля никто не будет переписывать весь проект и все ссылки везде... да и ПС вряд-ли обрадуется что будет два одинаковых контента по http://oursite.ru/index.php?module=News и http://oursite.ru/index.php?module=News2log

Dreammaker
На сайте с 20.04.2006
Offline
570
#8
mendel:
конечно жесть, но думаю это вы для простоты примера

естественно.

mendel:
Вот как к примеру здесь пристроить чтение конфига не из файла а из базы?

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

mendel:
Как мне при этом методами ооп-пхп сделать учет того каким образом та или иная переменная оказалась в $var_array - через lang или через say?

Не совсем понятно зачем это нужно, но если так уж потребовалось, то сохраняйте данные в массив через отдельный метод.

mendel:
потому как из-за моего модуля никто не будет переписывать весь проект и все ссылки везде...

Тоже не понял зачем что-то переписывать. Ваш модуль дёргается как класс через __autoload() (путём вызова через роутинг или же вызова, например как в Yii, через механизм виджетов) и в нём уже меняете что хотите.

$class->afterSave(); - это я не совсем корректно показал, ибо afterSave() скорее к модели относится и имеет хороший смысл, если используется ORM.

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

Вы спрашивали:

mendel:
И как можно переопределить класс когда ты не можешь контролировать создание экземпляров этого класса я тоже не понял.

Вам не нужно контролировать создание экземпляра класса, он сам создастся когда ему нужно через __autoload(). Пример это и показывал.

А уже запись в лог вы может прописать в вашем модуле или просто контроллере, где хотите.

mendel
На сайте с 06.03.2008
Offline
183
#9

Вы предлагаете изменять ЧУЖОЙ код, а хуки как раз и предназначены для разделения кода расширений от прикладного кода, и иногда от кода ядра.

Проект одного разработчика, ну двух... ну максимум трех еще как-то может жить на такой парадигме, но если мы хотим более менее открытую архитектуру, то менять ЧУЖОЙ код нельзя.

автолоад это конечно хорошо, но если уже есть класс "ньюз" и я хочу внести в него небольшие изменения, то как мне это сделать?

из _ГЕТ мы получили название класса который надо вызывать. Ядро ЕГО и вызовет. Всё. Точка. Для вызова нашего нового класса который наследует наш "ньюз"... ну пусть будет "ньюз2лог" нам надо менять ссылку и в ней указывать другой метод. А это есть перделка половины проекта. Если же архитектура будет другая и не через ссылку передается модуль, то все равно нужно лезть в ЧУЖОЙ код чтобы изменить вызов "ньюз" на "ньюз2лог"

Dreammaker
На сайте с 20.04.2006
Offline
570
#10
mendel:
ну пусть будет "ньюз2лог" нам надо менять ссылку и в ней указывать другой метод.

Зачем? Мы просто сделаем новое правило роутинга, которое будет перенаправлять вызов news на news2log.

Я боюсь, как раз в вашей системе всё слишком жёстко прописано и она недостаточно гибкая чтобы её могли пользоваться больше 2-3 программистов :)

p.s. Вызов просто $_GET['module'] - это пример и не нужно к нему привязываться. Система может быть намного сложнее.

p.p.s. Это я ещё упустил, что зачем нам создать класс news2log, если мы дописываем функционал к существующему модулю?

12 3

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий