Загрузка/Выгрузка/Перезагрузка модулей ПХП

Screjet

Новичок
Загрузка/Выгрузка/Перезагрузка модулей ПХП

Привет всем!
Есть интересная проблемка:

Есть модули к боту, загружаются на старте, код примерно такой:
PHP:
class hello_mod {
  function hello_mod( &$chat ){
     //..
  }
}
Как бы сделать выгрузку модуля ( или, точнее, перезагрузку)..

Загрузить новый через
PHP:
eval()
можно, но вот старый класс из ПХП выгрузить/переобозначить нельзя.
Теоретически = это нелогично, но необходимость такая есть.

У кого какие есть идеи?
 

FreeSpace

Чукча-читатель
А зачем тебе это?
"Вы не должны этого хотеть" (с) не_помню_чей.
 

lucas

Guest
1. Избавиться от таких "необходимостей".

2. Уникально именовать классы.
 

Screjet

Новичок
1) Задача не тривиальная. С вебом ничего общего.
2) Не хотеть этого не могу.. Пример:
работает непрерываемый скрипт (например разрешена одна попытка конекта скрипта к удаленному серверу за час.). Идея заключается в том, что не прерывая работы скрипта перегружаем модули. Каким способом этим процессом управлять = неважно.
3) Модуль = это объект класса. Т.е. разобозначить объект можно, а раздекларировать класс нельзя. Передекларировать класс тоже нельзя. При попытке объявить существующий класс, интерпретатор выдаст ошибку.
4) Если каждый раз декларировать уникальный класс, то эту ситуацию можно прировнять к утечке памяти. Старые декларации классов просто не нужны и будут занимать в памяти лишнее место. (Память = не резиновое пространство)

Вопрос конкретно специалистам: может ктото уже реализовывал механизм удаления продекларированного класса из хеша Зенд-двигателя.

Если таковых нет, то придется самому влазить ручками в Зенд-машину..

А может есть смысл оформить цивильное предложение для такой ф-ции.
 

FreeSpace

Чукча-читатель
Ну наворотил...

По твоим словам модуль - это объект (экземпляр) класса.
В таком случае выгрузить модуль из памяти очень просто - unset($module).
Как создать новый экземпляр существующего модуля, думаю, объяснять не надо ;)
А вот то, что касается "каждый раз декларировать уникальный класс" - тут не совсем понятно. У тебя классов что, бесконечное количество? Или ты хочешь объявлять разные классы с одинаковой функциональностью? Именно про это я говорил, что хотеть этого не стоит.

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

Screjet

Новичок
Или ты хочешь объявлять разные классы с одинаковой функциональностью?
Одинаковые классы с разной функциональностью :)

Кроме того должен сказать, что php разрабатывался для других целей, нежели работа в режиме демона.
Ненужно опускать ПХП. Он и сейчас еще в разработке для совершенно разных целей.
 

FreeSpace

Чукча-читатель
Одинаковые классы с разной функциональностью
А зачем?
Обычно классам дают имена исходя из того, что они делают.
Например, из названия сразу понятно, что класс Mail работает с почтой, а DB - с базами данных.
У тебя-же, как я понимаю, классы с разной функциональностью носят одинаковые имена. По-моему это ошибка проектирования.
 

Raziel[SD]

untitled00
Screjet тебе может помочь метод-фабрика.
пример:

PHP:
	function &objFactory()
	{
		$classname = getNewClassName();// некоторая функция возвращающая  имя класса
        $obj =& new $classname();
        return $obj;
	}

    loadmodule();//некое подключение твоего модуля
	$myobj =& objFactory();
    $myobj->execute();
    unset($myobj);
    loadmodule2();//некое подключение другого твоего модуля
	$myobj =& objFactory();
    $myobj->execute();
 

lucas

Guest
Screjet

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

Подозреваю, что http://pecl.php.net/package/apd может помочь избавиться от этого.
 

Screjet

Новичок
Может в виде кода будет более понятно?

PHP:
<!-- файл main.php -->
<?php

define('MODULE_DIR', '');
define('MODULE_EXT', '.mod.php');
//
class my_main_class {

	var $mod_pool = array();

	function my_main_class(){
		//...
		$this->mod_loader('mod_example');
		//.. много другого полезного кода..
		// добрались до места, когда необходима перезагрузка модуля
		$this->mod_loader('mod_example');
		//(!) PHP Fatal error:  Cannot redeclare class mod_example

	}

	function mod_loader( $module_name ){
		// если модуль уже установлен, тогда удаляем его
		if ( isset($this->mod_pool[$module_name]) ){
			$this->mod_unloader($module_name);
		}
		// читаем код модуля
		require MODULE_DIR .$module_name. MODULE_EXT;
		// создаем объект модуля
		$this->mod_pool[$module_name] = new $module_name( $this );
	}

	function mod_unloader( $module_name ){
		// освобождаем ресурсы, захваченные в модуле
		$this->mod_pool[$module_name]->free();
		// уничтожаем объект класс
		unset($this->mod_pool[$module_name]);
		// а в этом месте нужно удалить декларацию класса
	}
}

new my_main_class();
?><!-- конец файла main.php -->

<!-- файл mod_example.mod.php-->
<?php
// пример модуля
class mod_example {

	function mod_example( &$top ){
		$this->top = &$top;
		//.. чето очень полезное делаем
	}

	//.... очень нужный код модуля

	function free(){
		// освобождаем все захваченные ресурсы если есть
	}
}
?>
Как здесь можно применить уникальное именование класса?
И если модуль уж больно тяжелый, то "некотрый перерасход" = довольно мягко сказано.
 

Profic

just Profic (PHP5 BetaTeam)
Внимание вопрос: а зачем его по новой подключать? Ведь файл с кодом модуля не изменяется? (Если изменяется самим скриптом - то это, извините, кривое проектирование)
Есил нет, то все просто:
PHP:
    function mod_loader( $module_name ){ 
        // если модуль уже установлен, тогда удаляем его 
        if ( isset($this->mod_pool[$module_name]) ){ 
            $this->mod_unloader($module_name); 
        } 
        // читаем код модуля, если он еще не был загружен
        require_once MODULE_DIR .$module_name. MODULE_EXT; 
        // создаем объект модуля 
        $this->mod_pool[$module_name] = new $module_name( $this ); 
    }
 

Screjet

Новичок
Внимание вопрос: а зачем его по новой подключать? Ведь файл с кодом модуля не изменяется? (Если изменяется самим скриптом - то это, извините, кривое проектирование)
В этом то и смысл, что код модуля меняется програмером..
Скрипт в самом себе ничего не меняет :)

Оп! пропустил!

require_once не будет читать повторно файл. А нужно.
 

Profic

just Profic (PHP5 BetaTeam)
Screjet
тогда отдельный демон :) который может умереть и запуститься по новой и это твой долгоиграющий демон общается с ним.

Имхо, без глубокого ковыряния в ZE это единственный премлимый вариант :)
 

Screjet

Новичок
Вобщем самостоятельно добавил ф-цию unset_class() в стандартные ф-ции (php-4.3.6).

ext/standart/basic_functions.diff.c :
Код:
882c882,883
<
---
>
>       PHP_FE(unset_class, NULL)
3054a3056,3071
> PHP_FUNCTION(unset_class)
> {
>         char *class_name = NULL;
>       int class_name_length;
>
>       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &class_name, &class_name_length) == FAILURE) {
>           RETURN_FALSE;
>       }
>
>       if (zend_hash_del(CG(class_table), class_name, class_name_length+1)==FAILURE) {
>           RETURN_FALSE;
>       }
>
>       RETURN_TRUE;
>
> }
ext/standart/basic_functions.diff.h:
Код:
108a109,110
> PHP_FUNCTION(unset_class);
>
Юзать так:
PHP:
unset_class("mod_example");

(сорри за флуд.. Авось комуто понадобится)
 

tony2001

TeaM PHPClub
1) попробуй функции дать на вход массив или любую другую не-строку.
2) попробуй дать несуществующий класс.
3) попробуй дать имя существующего класса в верхнем регистре.
хех =)
 

fixxxer

К.О.
Партнер клуба
Мне это напоминает мою давнишнюю шутку с экстешненом для инкремента. :D

-~{}~ 12.07.04 17:00:

Кстати, раз уж это типа "демон" - как вариант - отфоркайся, проинклюдь что надо, сдохни чайлдом, jmp begin... ;)
 

Screjet

Новичок
Originally posted by tony2001
1) попробуй функции дать на вход массив или любую другую не-строку.
2) попробуй дать несуществующий класс.
3) попробуй дать имя существующего класса в верхнем регистре.
хех =)
Попробывал, в кору не отвалился, просто вернул FALSE :)

2fixxxer, 2Profic : тоже вариант, но дешевле добавить в зенд новую ф-цию.
(имхо)

Да и вообще ф-ция жутко специфичная.. так впрочем и задача :)
 

tony2001

TeaM PHPClub
держи, так лучше будет
Код:
PHP_FUNCTION(unregister_class)
{
	zval **class;
	zend_class_entry *ce = NULL, **pce;
	char *lowercase_name = NULL;

	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &class)==FAILURE) {
		ZEND_WRONG_PARAM_COUNT();
	}

	convert_to_string_ex(class);
	
	if (zend_lookup_class(Z_STRVAL_PP(class), Z_STRLEN_PP(class), &pce TSRMLS_CC) == SUCCESS) {
		ce = *pce;
	}

	if (!ce) {
		RETURN_FALSE;
	}

	lowercase_name = emalloc(ce->name_length+1);
	zend_str_tolower_copy(lowercase_name, ce->name, ce->name_length);
	
	if (zend_hash_del(CG(class_table), lowercase_name, ce->name_length+1) == FAILURE) {
		efree(lowercase_name);
		RETURN_FALSE;
	}
	
	efree(lowercase_name);
	RETURN_TRUE;
}
 
Сверху