singleton и __destruct в нескольких классах

advocat

developer
singleton и __destruct в нескольких классах

имеется простой код, для понимаю, того, что я хочу
PHP:
class singleton
{
    public static $dblayer  = null;
    public static $session  = null;
    
    public static function initClass($class)
    {
        switch ($class) {
            case 'dblayer':
                if (!self::$dblayer) {
                    self::$dblayer = new dblayer('singleton');
                }
                break;
                
            case 'session':
                if (!self::$session) {
                    self::$session = new session('singleton');
                }
                break;
                
            default:
                break;
        }
    }
}

class dblayer
{
    protected $_method  = null;
    private $_connect   = null;
    
    public function __construct($method)
    {
        $this->_method = $method;
        print __CLASS__.'->' .__FUNCTION__ . '(' .$this->_method. ')' . "\n";

        //здесь создается $_connect
    }
    
    public function __destruct()
    {
        print __CLASS__.'->' .__FUNCTION__ . '(' .$this->_method. ')' . "\n";

        //здесь умирает $_connect
    }

    public function foo($action)
    {
        //какие-то действие
        //использует $_connect
    }
}

class session
{
    protected $_method  = null;
    
    public function __construct($method)
    {
        singleton::initClass('dblayer');
        singleton::$dblayer->foo('start');
        $this->_method = $method;
        print __CLASS__.'->' .__FUNCTION__ . '(' .$this->_method. ')' . "\n";
    }
    
    public function __destruct()
    {
        singleton::$dblayer->foo('end');
        print __CLASS__.'->' .__FUNCTION__ . '(' .$this->_method. ')' . "\n";
    }
}

singleton::initClass('dblayer');
singleton::initClass('session');

$dblayer    = new dblayer('new');
$session    = new session('new');
на выходе мы получим

dblayer->__construct(singleton)
session->__construct(singleton)
dblayer->__construct(new)
session->__construct(new)
session->__destruct(new)
dblayer->__destruct(new)
dblayer->__destruct(singleton)
session->__destruct(singleton)
при простой инициализации 2х классов через new, первым __destruct сработает у того класса, который был последним инициализирован, при использовании singleton pattern все получается наоборот ...

Вопрос: как можно сделать, чтобы при использовании singleton можно было получить картину

dblayer->__construct(singleton)
session->__construct(singleton)
session->__destruct(singleton)
dblayer->__destruct(singleton)
 

maxru

МИФИст
1) А где хранятся ссылки на классы, инициализированные с помощью
PHP:
singleton::initClass('dblayer'); 
singleton::initClass('session');
? (Этот вопрос не спроста).
2) В какой последовательности удаляются переменные класса?
 

advocat

developer
Автор оригинала: maxru
1) А где хранятся ссылки на классы, инициализированные с помощью
PHP:
singleton::initClass('dblayer'); 
singleton::initClass('session');
? (Этот вопрос не спроста).
2) В какой последовательности удаляются переменные класса?
1)
singleton::$dblayer->...
singleton::$session->...
2) мне нужна именно последовательность как при new
 

asm

Пофигист
public static $dblayer = null;
public static $session = null;
заменить на стек?
 

maxru

МИФИст
asm ну зачем стек? Не поможет, поскольку КАК Я ПРЕДПОЛАГАЮ у интерпретатора PHP свой внутренний стек, а скорее ассоциативный массив. И удаляться переменные класса будут в таком порядке, в каком были инициализированы.

-~{}~ 11.07.07 12:53:

Да и какая разница в каком порядке удаляются никак не зависящие друг от друга экземпляры классов?
Ну и второе:
Создай у Singleton метод __destruct() и удаляй классы в любом порядке, хоть случайно ;)
Если нужно удалять в обратном порядке, то действительно поможет стек.
 

advocat

developer
Автор оригинала: maxru
Создай у Singleton метод __destruct() и удаляй классы в любом порядке, хоть случайно ;)
Если нужно удалять в обратном порядке, то действительно поможет стек.
singleton - это статический класс, который единожды подключается, но не иниализируется

примечание оба класса (dblayer и session) взаимосвязаны с друг другом, если быть точнее, то деструкт dblayer не должен выполняться раньше session, так как session использует dblayer
P.S. я привел простой пример без взаимодействия для простоты

Относительно стека - то я не до конца понимаю, какое именно решение ты предлагаешь
 

maxru

МИФИст
Ладно, тогда сделай в классе Singleton :
PHP:
static protected $instances = array(); 
/* ассоциативный массив "class_name" => "class_instance"*/
static public function DestroyInstances()
{
    /* Здесь в обратном порядке вызываешь ЯВНО __destruct методы экземпляров классов из self::$instances*/
}
-~{}~ 11.07.07 13:18:

Ну или unset() вместо __destruct(), раз уж это синглтон.
 

advocat

developer
maxru
не то это, что я хотел :) __destruct для чего придумали :)

ЕЩЕ ИДЕИ ?
 

maxru

МИФИст
Неизвращенных идей, я думаю, не будет, если PHP действительно хранит поля объекта так, как я предположил.
 

advocat

developer
реальная ситуация такова, что у меня синглтон содержит ссылки на 10 классов, я конечно могу создать
PHP:
public static destruct()
{
    //по очереди в нужном порядке вызывать __destruct классов, если они инициализированы
}
но я хочу избавится от вызова в конце каждого файла и при разного рода header("Location... от вызова sinleton::destruct()

я надеюсь я понятно выражаю свою мысль

-~{}~ 11.07.07 12:42:

Смотрим, что написано в мане PHP5 Manual: Конструкторы и деструкторы
PHP 5 предоставляет концепцию деструкторов, сходную с теми, что применяются в других ОО языках, таких, как Java: когда освобождается последняя ссылка на объект, перед высвобождением памяти, занимаемой этим объектом, вызывается метод __destruct(), не принимающий параметров.
По логике вещей, если инициализируются они при singleton и new одинаково, то и __destruct должен вызываться одинаково ...
т.е. последней должна быть ссылка singleton::$session, а перед ней singleton::$dblayer, и удаляться именно в таком порядке
Баг™ или Фича™...
 

atv

Новичок
Смотрим, что написано в мане PHP5 Manual: Конструкторы и деструкторы

PHP 5 предоставляет концепцию деструкторов, сходную с теми, что применяются в других ОО языках, таких, как Java: когда освобождается последняя ссылка на объект, перед высвобождением памяти, занимаемой этим объектом, вызывается метод __destruct(), не принимающий параметров.
Где там написано про последовательность удаления из памяти нескольких объектов имеющих последнюю ссылку. Эта последовательность совершенно не важна.

По логике вещей, если инициализируются они при singleton и new одинаково, то и __destruct должен вызываться одинаково ...
По логике вещей, они хранятся в разных переменных, в разных областях видимости. Освобождение памяти зависит от логики самого GC (в которой порядок инициализации не учитывается), и от организации хранения этих самых переменных. Так что это не баг и не фича, а не имеющий значения факт.

примечание оба класса (dblayer и session) взаимосвязаны с друг другом, если быть точнее, то деструкт dblayer не должен выполняться раньше session, так как session использует dblayer
Значит явно укажи эту зависимость.
 

advocat

developer
добавил в конце
PHP:
var_dump($dblayer);
var_dump($session);
var_dump(singleton::$dblayer);
var_dump(singleton::$session);
получил
Код:
object(dblayer)#3 (1) {
  ["_method:protected"]=>
  string(3) "new"
}
object(session)#4 (1) {
  ["_method:protected"]=>
  string(3) "new"
}
object(dblayer)#1 (1) {
  ["_method:protected"]=>
  string(9) "singleton"
}
object(session)#2 (1) {
  ["_method:protected"]=>
  string(9) "singleton"
}
т.е. ссылки на объекты в логически правильном порядке ... и сначало срабатывает __destruct объекта 4, потом 3, а потом почему то 1 и только потом 2

-~{}~ 11.07.07 13:19:

atv
насчет разных областей видимости - согласен

насчет последовательности ... может конечно ты и прав, но логика должна быть

а насчет "не имеющий значения факт" - если бы это не имело значения - я бы не задавал таких вопросов

-~{}~ 11.07.07 13:26:

Автор оригинала: atv
Значит явно укажи эту зависимость
указал ... если так будет проще ...
 

maxru

МИФИст
Появилась еще одна бредовая идея.
Я бы создал контейнерный класс, в котором хранились бы все классы, которые будут использоваться + динамическое подключение требуемых классов + методы выдачи ссылок на экземпляры классов.
Поскольку экземпляр класса создается, то в деструкторе можно задать последовательность удаления содержащихся в нем классов-синглтонов и классов-фабрик.
То есть вместо $myA = A::getInstance(); нужно будет вызывать
$myClassLibrary->getClass('A');
Если в $myClassLibrary под 'А' сидит фабрика, то вернется ссылка на новый экземпляр класса.
А если наследник Синглтон, как в Вашем случает, то ссылка на самого него.

З.Ы. Может невнятно объяснил, но идея-то бредовая ;)
 

atv

Новичок
Значит явно укажи эту зависимость

указал ... если так будет проще ...
Я не об этом. Что бы логика GC отработала ("освобождается последняя ссылка на объект") помести в session ссылку на dblayer, тогда dblayer не удалиться пока не будет удалён session. Это и есть явное указание зависимости. Или добавь в деструктор логику, которая ЯВНО будет учитывать зависимости.
 

advocat

developer
atv
идея конечно интересная, я уже думал об этом но одно из 2х, либо я неправильно использую, или она не работающая :(

может я что-то неправильно делаю
PHP:
//добавляю в session
private $_dblayer   = null;
//в session __construct, после singleton::$dblayer->foo('start'); добавляю
$this->_dblayer = &singleton::$dblayer;
//вроде все честно - есть в классе session ссылка на dblayer
но результат такой же, т.е. dblayer "умирает" раньше session

-~{}~ 11.07.07 13:59:

З.Ы. добавил пару комментариев, может быть так будет более понятна логика

-~{}~ 11.07.07 14:06:

понятное дело, что можно использовать
в dblayer __destruct
PHP:
if (singleton::$session) {
    singleton::$session->__destruct();
}
ну и добавить в защиту от 2го вызова session->__destruct()...

проблема только в том, что это больше хак ...

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

atv

Новичок
поменяй "$this->_dblayer = &singleton::$dblayer;" на "$this->_dblayer = singleton::$dblayer; ", т.е. убери амперсанд, и перечитай ман по ссылкам на объекты, чтобы понять в чём прикол.
 
Сверху