LINUX.ORG.RU

[PHP] Как проинитить данные класса из сериализированного представления объекта?

 


0

0

Здравствуйте!

Хочется мне вот чего. Есть у меня «хранилище» объектов. В этом хранилище лежат сериализованные копии объектов. Я пользуюсь фреймверком CodeIgniter, и хочу чтобы объекты контроллеров «восстанавливали» свое состояние.

Выглядеть это должно примерно так:

class Menu extends Controller
{

 private $menu_counter=0;
 private $menu_pointer=0;

 function __constructor()
 {
  ...
 
  $objectdata=$stack->get_objectdata();

  if($objectdata!=NULL) $this=unserialize($objectdata);
 }
 ...
}

Но конечно, так использовать $this нельзя. Получается ошибка:

Cannot re-assign $this in menu.php on line 28

Я, конечно, могу сделать методы, возвращающие массив свойств, и устанавливающие свойства из массива. И работать через них. Но эти методы нужно будет постоянно контролировать в каждом классе контроллера, и менять их, если появилось/удалилось/переименовалось какое-нибудь свойство. А это потенциальный источник ошибок.

Вопрос. Как проинициализировать свойства класса «изнутри» класса, имея сериализованную копию данного класса?


Ответ на: комментарий от xintrea

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

Однако, в теме про __wakeup() написали.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

тут типа фабрики надо:
class a {
public static function create() {
$objectdata=$stack->get_objectdata();
if ($objectdata!=NULL) {
return unserialize($objectdata);
}
return new a();
}
}

roller ★★★
()
Ответ на: комментарий от roller

Понимаешь, в чем дело. То что ты написал - это создание объекта _извне_, и наполнение объекта сериализованными данными у тебя тоже происходит _извне_. С этим у меня вопросов не возникает.

Я же спрашиваю, как наполнить объект сериализованными данными _изнутри_ самого объекта. Это нужно, так как я пользуюсь фреймверком CodeIgnigter. И в нем классы контроллеров должны создаваться по определенной схеме

http://code-igniter.ru/user_guide/g...ontrollers.html

А фреймверк обращается к контроллерам, напрямую преобразуя ЧПУ URL в вызов нужного метода контроллера.

Сериализованные данные становятся известными _внутри_ объекта контроллера. Потому и нужно, чтоб объект мог восстановить сам себя из сериализованной копии.


Когда я начинал проектирование, я думал, что если объект может сам себя сохранить, используя serialise($this), значит может и сам себя восстановить. И вот в самый последний момент выяснилось, что этого я сделать не могу.

xintrea
() автор топика
Ответ на: комментарий от KRoN73

Вопрос в том, что нужно написать в методе __wakeup(), чтобы объект смог сам себя восстановить из сериализованных данных.

xintrea
() автор топика
Ответ на: комментарий от xintrea

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

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

> Ну, как бы, К.О. намекает, что в этом методе нужно прописать восстановление данных, которые требуют переинициализации.

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

А меня интересует полное восстановление состояния класса.

На пхпклубе вроде додумались как можно сделать, еще не проверял.

xintrea
() автор топика
Ответ на: комментарий от KRoN73

да он не это просит он просит, чтобы при выполнении кода $myObj = new MyObject(); этот самый myObj чудесным образом понимал, что он был сереализован (видимо по выдаче myobject_data) и сказал что $this = unserialize($data)

Видимо автор хочет, что-то вроде синглетона сделать, только на случай, когда объект сериализован.

qnikst ★★★★★
()

А зачем кешировать контролер, может лучше все таки модель?

urxvt ★★★★★
()
Ответ на: комментарий от xintrea

>Статические переменные, находящиеся внутри какого-нибудь метода, ты таким образом не восстановишь.

1. Это что за такие статические переменные? Там не ошибка ли в дизайне? Может, нужен синглетон?

2. Ну а и почему бы статику не восстановить?

KRoN73 ★★★★★
()
Ответ на: комментарий от qnikst

>да он не это просит он просит, чтобы при выполнении кода $myObj = new MyObject(); этот самый myObj чудесным образом понимал, что он был сереализован

Откуда он будет сериализован, если это новый объект? :)

Может, ему просто фабрика объектов нужна?

$myObj = object_load('MyObject', <conditions>);

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

я тоже удивлён постановке вопроса.

у него есть объект стэк данных где хранится что-то в т.ч. его сериализованный объект.

после этого он создаёт свой объект new MyObj();

объект вместо того, чтобы создаваться он просит стэк (?!он получился глобальным) отдать его сериализованную версию, и потом десеарилизоваться в себя.

По моему это поведение синглетона, или да, можно через фабрику

$myObject = MyObject::getInstance();//signleton
$myObject = $stack->load('MyObject');//fabric method
что лучше я не знаю я в ООП на пхп всё перейти пытаюсь, но получается плохо.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

> у него есть объект стэк данных где хранится что-то в т.ч. его сериализованный объект.

> после этого он создаёт свой объект new MyObj();


> объект вместо того, чтобы создаваться он просит стэк (?!он получился глобальным) отдать его сериализованную версию, и потом десеарилизоваться в себя.


Да, у меня есть стек, он один на сессию, хранится в сессии.

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

xintrea
() автор топика
Ответ на: комментарий от qnikst

> $myObject = MyObject::getInstance();//signleton
> $myObject = $stack->load('MyObject');//fabric method


Это ты показал создание объекта _извне_.

Я пользуюсь готовым фреймверком CodeIgniter. Этот фреймверк сам вызывает методы класса контроллера. Поэтому контроллер должен восстанавливать свое состояние в конструкторе. То есть _изнутри_.

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

xintrea
() автор топика
Ответ на: комментарий от qnikst

>у него есть объект стэк данных где хранится что-то в т.ч. его сериализованный объект.

А ссылка на объект не покатит? :)

>что лучше я не знаю я в ООП на пхп всё перейти пытаюсь, но получается плохо


Задачи разные. Синглетон - это для глобальных объектов (чтобы в $GLOBALS[] не пихать их :)). Например, при обработке запроса это может быть клиент, в нём - браузер. По определению это единственные объекты в работе.

Фабрика объектов служит обычно для того, чтобы скрыть внутреннюю архитектуру объектов. Например, когда ты хочешь унифицированную загрузку разного рода объектов со схожим функционалом. Получается более гибко, чем пихать всё в конструктор. По сути, фабрика вызовет конструктор объекта, вызовет его функции инициализации, проверит правильность загрузки. Также, если объект тяжёлый может, например, загрузить объект из кеша, в т.ч. сериализованный. И всё это скрыто от программирования на высоком уровне. Т.е. ты просто загружаешь объект и используешь, а не в каждом месте его использования проводишь всю работу по кешированию оного.

KRoN73 ★★★★★
()
Ответ на: комментарий от xintrea

Класс контроллера должен уметь сам восстанавливать свое состояние из стека

function __wake() { ... }

Вызывается каждый раз, когда объект заканчивает десериализацию.

Например, если тебе нужно восстановить соединение с БД, можешь там вписать:

function __wakeup()
{
    $this->dbh = new driver_mysql(config('main_db'));
}

...

На самом деле, даже если бы не было этой функции, проблем бы не было никаких. Просто две функции-обёртки:

function my_serialize($object)
{
    $object->my_sleep();
    return serialize($object);
}

function my_unserialize($string)
{
    $object = unserialize($string);
    $object->my_wakeup();
    return $object;
}

class my_class
{
    $dbh = NULL;
    function __constructor() { $this->my_wakeup(); }
    function my_wakeup() { $this->dbh = new driver_mysql(); }
    function my_sleep() { $this->dbh->close(); $this->dbh = NULL; }
}

И потом где нужно:

$my_stack->push(my_serialize($object));

или

$object = $my_stack->pop();

В принципе, такой вариант даже лучше, поскольку функция __sleep() в PHP реализована неудачно. Она требует явного перечисления всех сохраняемых properties объекта :)

KRoN73 ★★★★★
()
Ответ на: комментарий от xintrea

>Поэтому контроллер должен восстанавливать свое состояние в конструкторе. То есть _изнутри_.

Конструктор объекта при десериализации не вызывается. Вызывается его метод __wakeup();

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

> $my_stack->push(my_serialize($object));
> или

> $object = $my_stack->pop();


Ладно, в общем, разговор тут бессмыленный. Потому что в моем случае нужны команды, где $object заменен на $this.

$my_stack->push(my_serialize($this));
или
$this = $my_stack->pop();

Первая команда работает. Вторая команда не будет работать никогда.

xintrea
() автор топика
Ответ на: комментарий от xintrea

да я понял уже пока с работы ехал :)

просто контроллер эта такая штука, которая в идеале не должна обращаться к хранилищам данных будь то сессия, бд, whatever. Он должен только вызвать необходимую функцию и умереть, в принципе это вобще может быть класс только с методами класса. (Не знаю как в соделднитере, но в других фрэймоврках это в мануале написано сразу).

Но как я понял тебе нужно хранить прошлое состояние на основе чего определять действие (иначе смысла в сериализации нет). Тогда и храни в "сессии" переменные определяющие это состояние, а не весь объект. В любом случае в скорости не выиграешь (поправьте меня умные люди если это не так). А проблем будет на порядок меньше.

Там ещё проскакивало про переменные класса и их восстановление, за такой дизайн по хорошему надо чайником по лбу бить :)

qnikst ★★★★★
()
Ответ на: комментарий от KRoN73

Я понимаю, зачем нужен синглетон, а зачем фабрика. Как я понимаю в getIntanse синглетона тоже можно взять объект из кэша, в т.ч. сериализованный.

Тогда разница остаётся в том, что синглетон оборачивает 1 объект одного типа, а фабрика может генерировать много объектов (в т.ч.1) разных типов. Так ведь?

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

qnikst ★★★★★
()
Ответ на: комментарий от KRoN73

Ну, я таки сделал что хотел.

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

Так что пришлось извратится, и сделать только сохранение свойств конечного объекта (унаследованные свойства контроллера исключаю из сохранения хитрожопым способом). Главное, что смог сие сделать без "обезьянничества" - то есть нигде в коде ненужно указывать какие свойства сохранять, т.е. нет двойной работы "добавил свойство - пропиши его в сохранялку". То есть пишешь класс как обычно, дальше двиг все делает сам.

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

...

Вот щас у меня для теста крутится счетчик обращений к странице с начала сессии. Прикольно, ничего сохранять/вспоминать ненадо. Инктрементирую свойство и все :)

xintrea
() автор топика
Ответ на: комментарий от xintrea

Правда, от полной сериализации объекта пришлось отказаться так как контроллер наследуется от класса контроллера движка

Вот поэтому я и постарался в своём фреймворке повсюду отказаться от наследования на системном уровне. Оно отдано на откуп прикладной части :)

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

Так что, для этого потребовались особые извращения? Оригинальный фреймворк так не умеет? Ты имеешь в виду, типа:

$topic = object_load('forum_topic', $topic_id);
$topic->set_title("ЛОР любит PHP!", true);
и после завершения процесса соответствующее изменение окажется в БД. И в другом процессе (+memcached, естественно, по желанию) этот объект загрузится уже с новым свойством.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Ты имеешь в виду, типа:
$topic = object_load('forum_topic', $topic_id);
$topic->set_title(«ЛОР любит PHP!», true);
и после завершения процесса соответствующее изменение окажется в БД. И в другом процессе (+memcached, естественно, по желанию) этот объект загрузится уже с новым свойством.

Впринципе, да. Вот как контроллер с этим самым счетчиком выглядит:

Class Menu extends RestoreController
{

 var $menu_counter=0;

 function index()
 {
  // Главная страница администрирования с меню
  $data['content']=$this->adminmenu->get_menu();
  $data['page_counter']=$this->menu_counter++;

  $this->load->view('admin/master_template',$data);
 }
}

Обновляю URL

http://localhost/ci/index.php/admin/menu/index

и счетчик сам крутится.

xintrea
() автор топика
Ответ на: комментарий от xintrea

У меня это будет что-то навроде:

class my_menu extends base_page_db
{
    // ...
    function pre_show()
    {
        $this->set_counter($this->counter() + 1, true);

        return false;
    }
    // ...
}

KRoN73 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.