Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> ViewState для PHP! 
:(
    Опции темы
Crypton
Дата 4.12.2009, 05:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 158
Регистрация: 9.10.2006
Где: США, Санкт-Петерб ург

Репутация: нет
Всего: 2



Не знаю если это относится к "Для профи" или к "Общим вопросам"


[hr]
В ASP.NET есть обалденная вещь и называется она ViewState. HTTP связь с сервером как правило не поддерживает отношений между данными так что приходится извращаться с данными в форму, куки, сессию и пр. Однако некоторые вещи порой не подходят для реализации как, например куки и сессии которые одинаковы на все приложение. Единственное что можно сделать, это внести в форму (<form>) дополнительные скрытые поля, которые при запросе обратно к скрипту дадут ему понять, что делать с данными. В ASP.NET эта функция встроена и поддерживает сохранение любых объектов помеченных атрибутом Serializable в том числе и примитивных типов как string, Guid, и т.д. Пример свойства с сохранением идентификатора статьи:
Код

   1. Guid MyId {  
   2.     get{  
   3.         return (ViewState["MyId"]!=null) ? (Guid)ViewState["MyId"] : Guid.Empty;  
   4.     }  
   5.     set{  
   6.         ViewState["MyId"] = value;  
   7.     }  
   8. }  


Однако в PHP такой функции, к сожалению, нет,  так что приходится прибегать к таким выкрутасам.  Приведем логическую конструкцию:

   1. Начало скрипта (inlude,require)
   2. Обработка GET/POST, если они есть.
   3. Вывод данных (HTML)
   4. Конец

Рассмотрим аспекты более детально. Нам необходим самый простой метод работы с данными. Можно, например, использовать ключевой массив ($array['key']) практически, как и в ASP.NET. Облегчает ситуацию еще и возможность сериализировать любой объект в php в строку. Потом всю эту строку еще и скормим в base64. Далее:

Код

   1. $myarray = array("1"=>"foo", "2"=>"bar");  
   2. $data = base64_encode(serialize($myarray));  


Получается, что все, что нам нужно будет сделать, это поставить эту строку в input type="hidden" и результат готов. При следующей загрузки страницы просто-напросто конвертируем обратно от base64 и десиарилизируем. Лучше всего сделать это в ООП подходе и создать класс.

Ах да, вы наверное, хотели спросить про безопасность. Известно, что при малейшем изменении данных, хэш SHA критически меняется. Но вот как зафиксировать попытку изменения данных в форме кулцхакера? smile. Можно хэшировать сериализованную строку (перед base64) и сохранить хэш в сессии. Однако если открыто много форм, получится много значений и в сессии. Придется при следующем запросе чистить память. Ключ к значению сессии можно хранить как раз во вью-стейте. Ничего с ним не случится а если попытаются его подменить на какой-нибудь жизненно важный объект, то произойдет облом в проверке данных. В итоге у нас и код безопасный, и передача данных безопасна. Все, что остается делать это инклюдить и юзать.

В конце-концов относим все в отдельный класс, viewstate.php:
Код

# <?php   
# if(!defined("INCLUDE")) die("Вам тут делать нечего");  
#   
# /** 
#  * Основной класс ViewState для убогой реализации штатной передачи данных 
#  * @author crypton / [email protected] 
#  * 
#  */  
# class ViewState {  
#     /** 
#      * Многоярусный массив где хранятся все данные вью стейта 
#      * @var array 
#      */  
#     public $VIEWSTATE = array();  
#     /** 
#      * Проверка подлинности вьюстейта. Лучше оставить это включенным по техники безопасности что-бы разные кулцхакеры не ковырялись 
#      * @var boolean 
#      */  
#     public $ValidateRequest = true;  
#     private $VS_KEY = NULL;  
#     /* 
#      * Генерация ключа проверки. Если хотите, можете настроить диапазон 
#      */  
#     private $RANDKEY_START = 11;  
#     private $RANDKEY_END = 22717;  
#       
#     /** 
#      * Основной конструктор вьюстейта. Эта функция автоматом вызовет loadFromPostBack() если это нужно 
#      * @return unknown_type 
#      */  
#     function __construct() {  
#         $this->loadFromPostBack();         
#     }  
#       
#     /** 
#      * Вызывайте эту функцию в начале вашего скрипта. Эта функция тупо загружает данные обратно из вьюстейта. Функция также возвращает тру или фалс 
#      * в зависимости от успеха декодирования вьюстейта (в том числе и проверки его подлинности) 
#      * @return unknown_type 
#      */  
#     public function loadFromPostBack(){  
#         if(count($_POST) == 0 || !isset($_POST['__VIEWSTATE'])) {  
#             return;  
#         }  
#         $data = base64_decode($_POST['__VIEWSTATE']);  
#         if(!$data) {  
#             trigger_error("Ошибка валидации ViewState. Входные данные повреждены. [base64_decode]", E_USER_WARNING);  
#             return false;  
#         }  
#         $vsdata = unserialize($data);  
#         /* 
#          * Формат на самом деле-то такой вот: 
#          * Array => 
#          *      [VALIDATIONKEY] => ключь валидации в сессии, генерируется каждый раз случайно 
#          *      [VIEWSTATE]     => сами данные вьюстейта как массив который вам вздумается        *  
#          */  
#         if(!$vsdata) {  
#             trigger_error("Ошибка валидации ViewState. Входные данные повреждены. [unserialize]", E_USER_WARNING);  
#             return false;  
#         }  
#         if(!isset($vsdata['VALIDATIONKEY']) || !isset($vsdata['VIEWSTATE'])) {  
#             // хм.. странновато получается  
#             trigger_error("Ошибка валидации ViewState. Входные данные повреждены. [arraykey-notexist]", E_USER_WARNING);  
#             return false;  
#         }  
#         // проверить данные вью стейта чтоб в нем не ковырялись  
#         $fromkey = $vsdata['VALIDATIONKEY'];  
#         if($this->ValidateRequest){  
#             if(!isset($_SESSION[$fromkey])) {  
#                 trigger_error("Ошибка валидации ViewState. Входные данные не прошли проверки. [session]", E_USER_WARNING);  
#                 return false;  
#             }  
#             // мы просто берем хэш полученного вьюстейта (сериализованного) и сверяем его   
#             $PrevKey = $_SESSION[$fromkey];  
#             $ThisKey = sha1($data);  
#             if($PrevKey != $ThisKey){  
#                 // упс, кто-то поковырялся, надо их мухобойкой :)  
#                 trigger_error("Ошибка валидации ViewState. Входные данные не прошли проверки. [keymismatch]", E_USER_WARNING);  
#                 return false;  
#             }  
#         }   
#             // очистить сессию от лишнего мусора  
#             unset($_SESSION[$fromkey]);  
#           
#         // вернуть все на круги своя  
#         $this->VIEWSTATE = $vsdata['VIEWSTATE'];  
#         return true;  
#     }  
#       
#     /** 
#      * Эта функция возвращает данные в base64 которые вам нужно непосредственно запихнуть в &lt;input type=hidden name=__VIEWSTATE. 
#      * @return string 
#      */  
#     public function getData(){  
#         // сгенерируем ключь валидации вью-стейта  
#         if($this->VS_KEY == NULL){  
#             $ok = false;  
#             // генерируем число если нет ключа в сессии. в лучшем случае этот цикл пройдет всего-лишь раз  
#             while(!$ok){  
#                 $this->VS_KEY = mt_rand($this->RANDKEY_START, $this->RANDKEY_END);  
#                 if(!isset($_SESSION['VS_'.$this->VS_KEY])){  
#                     $ok=true;  
#                 }  
#             }  
#         }  
#         // построить массив в нашем формате  
#         $vsdata = array(  
#                         'VALIDATIONKEY' => 'VS_'.$this->VS_KEY,  
#                         'VIEWSTATE' => $this->VIEWSTATE  
#                         );    
#         $data = serialize($vsdata);  
#         $hash = sha1($data);  
#         $_SESSION['VS_'.$this->VS_KEY] = $hash;  
#         $base64 = base64_encode($data);  
#         return $base64;  
#     }  
#       
# }  
# ?>

А в основной странице,
Код

# <?php  
# /* 
#  * Основная декларация текущего пути. Дабы избежать некоторых конфликтов ("багов", гы) в PHP когда он находит разные файлы с одним и тем-же 
#  * именем, в разных папках 
#  */  
# define("ROOT_PATH", realpath('./'));  
# /* 
#  * Декларация для других инклюдов, которые без неё будут посылать пользователя нах** 
#  */  
# define("INCLUDE",true);  
#   
# /* 
#  * С декларациями разобрались, теперь делаем самое главное. Допустим, что после скобки закрывающей этот камент у вас начинается 
#  * любой PHP файл с формой, где надо реализовать ВьюСтейт. Делаем ваши любимые инклюды и заинклюдываем наш класс по работе с вьюстейтом 
#  */  
#   
# include ROOT_PATH."/viewstate/viewstate.php";  
# session_start();  
# // инициализируем, как по правилу, вьюстейт  
# $ViewState = new ViewState();  
#   
# // мы можем загрузить данные из вьюстейта, если они конечно у нас есть. для этого можно поставить какой-нибудь магический ключь при  
# // наличии которого можно определить если вьюстейт загружен от POST или загружается чисто страница  
# $mydata = "";  
#   
# extract($ViewState->VIEWSTATE); // данная функция тупо перезапишет переменные чуть выше. короче смотрите мануал http://docs.php.net/manual/en/function.extract.php  
#   
#   
# // тут у нас основной код проги, ля-ля-ля  
# $errors = array();  
# if(count($_POST)) {  
#     $mydata = htmlentities($_POST['mydata']);  
#     echo "Полученные данные (POST): <br /><pre>";  
#     print_r($_POST);  
#     echo "</pre><br />Данные ViewState (с прошлого запроса):<br /><pre>";  
#     print_r($ViewState->VIEWSTATE);    
#     echo "</pre><br />Данные сессии:<br /><pre>";  
#     print_r($_SESSION);  
#     echo "</pre>";  
#     // загружаем данные обратно в вьюстейт ЕСЛИ нажата первая кнопка  
#     if($_POST['act'] == 'Сохранить текст')  
#         $ViewState->VIEWSTATE['mydata'] = $mydata;  
# }  
#   
# ?>  
# <html>  
# <head>  
# <title>Эксперимент с грубой эмуляцией ViewStae</title>  
# </head>  
# <body>  
#   
# <p>Введите текст, нажмите отправить, и посмотрите на исходный код  
# страницы. ViewState будет сохранять различные переменные в себе.</p>  
# <fieldset><legend>Эмуляция вьюстейта</legend>  
# <form action="<?php echo $_SERVER['PHP_SELF']?>" method="POST"><textarea  
#     name="mydata" cols="50" rows="10"></textarea> <input type="submit" name="act" value="Сохранить текст" />  
#     <input type="submit" name="act" value="Тупо отправить форму" />  
#     <input type="hidden" name="__VIEWSTATE" value="<?php echo $ViewState->getData() ?>" />  
#     </form>  
# </fieldset>  
#   
# </body>  
# </html>




[hr]

Источник http://crypton-technologies.net/experiment...wstate-dlya-php

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

Очевидные минусы ViewState:
  • Легко расшифровать. Правда можно лечить AES шифрованием на стороне сервера
  • Быстро разрастается от количества и длины данных
  • Конечно-же нельзя хранить ресурсы (mysql подключение и указатели на файлы)
  • Инклюдить ваши классы нужно ДО самого инклюда обработчика ViewState (иначе появятся ошибки типа такой объект неизвестен нам)


Это сообщение отредактировал(а) Crypton - 4.12.2009, 17:29
--------------------
«Все, что вы знаете — ложь» Теория мироздания 
PM MAIL WWW ICQ Skype   Вверх
Simpliest
Дата 4.12.2009, 08:36 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 625
Регистрация: 1.9.2009

Репутация: 1
Всего: 3



Не надо гонять viewState туда-сюда.
хеш наружу и хранилище для viewState на сервере. 

Я использовал тупо сериализацию текущего enviroment. Для моих задач этого хватало.
Хранилось все. Текущее состояние объектов, ошибки, данные ввода. При желании можно было организовать даже пошаговый просмотр всего сеанса работы пользователя.


--------------------
user posted image
PM   Вверх
solenko
Дата 4.12.2009, 10:46 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1473
Регистрация: 15.1.2006
Где: Украина

Репутация: 34
Всего: 67



Цитата(Simpliest @  4.12.2009,  07:36 Найти цитируемый пост)
Не надо гонять viewState туда-сюда.

В том, чтобы отдавать это на клиента, есть один плюс -- не нужно заморачиваться очисткой мусора. В Prado было реалиовано что-то типа ViewState с хранением в сессии. Сессиия безумно разрасталась, т.к. мы не можем контролировать закрыл пользователь страницу или нет, т.е. открыл 3, 5, n форм и потом закрыл все кроме одной, а в сессии висят все n

Добавлено через 2 минуты и 15 секунд
Цитата(Crypton @  4.12.2009,  04:43 Найти цитируемый пост)
Инклюдить ваши классы нужно ДО самого инклюда обработчика ViewState (иначе появятся ошибки типа такой объект неизвестен нам)

Пора переходить на __autoload )


--------------------
Ла-ла-ла-ла
Заметьте, нет официального подтверждения, что это не просто четыре слога.
PM MAIL WWW ICQ Skype   Вверх
Simpliest
Дата 4.12.2009, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 625
Регистрация: 1.9.2009

Репутация: 1
Всего: 3



Цитата(solenko @  4.12.2009,  09:46 Найти цитируемый пост)
В том, чтобы отдавать это на клиента, есть один плюс -- не нужно заморачиваться очисткой мусора

что поделаешь, бесплатные пирожки только у бабушки smile

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


Цитата(solenko @  4.12.2009,  09:46 Найти цитируемый пост)
Пора переходить на __autoload 

угу, и давно пора.


--------------------
user posted image
PM   Вверх
Crypton
Дата 4.12.2009, 17:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 158
Регистрация: 9.10.2006
Где: США, Санкт-Петерб ург

Репутация: нет
Всего: 2



Цитата

хеш наружу и хранилище для viewState на сервере. 


Хм, а как планируете хранить его на сервере? в Сессии или в определенном файле?

Цитата

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

А если юзверь откроет сразу много форм? Как уже говорилось, практически невозможно отследить если окно браузера (форма) была закрыта. В конце-концов вьюстейт на то и создан чтоб храниться в форме. А так можно тупо использовать сессию.

Цитата
Цитата

Пора переходить на __autoload 

угу, и давно пора.

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

Это сообщение отредактировал(а) Crypton - 4.12.2009, 17:36
--------------------
«Все, что вы знаете — ложь» Теория мироздания 
PM MAIL WWW ICQ Skype   Вверх
Simpliest
Дата 4.12.2009, 17:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 625
Регистрация: 1.9.2009

Репутация: 1
Всего: 3



Цитата(Crypton @  4.12.2009,  16:34 Найти цитируемый пост)
Хм, а как планируете хранить его на сервере? в Сессии или в определенном файле?

По вкусу и потребностям.
Я хранил даже в БД, но при большой нагрузке это не самый лучший вариант.
можно воспользоваться вариантом отсюда
http://forum.vingrad.ru/forum/topic-281995...d-memcahed.html
каждому хешу - свой файл.

Цитата(Crypton @  4.12.2009,  16:34 Найти цитируемый пост)
А если юзверь откроет сразу много форм? 

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


Цитата(Crypton @  4.12.2009,  16:34 Найти цитируемый пост)
они разводили руками и говорили "если работает, зачем менять?". 

Правильно говорят. 
Только донеси им мысль, что добавить новую функциональность не так просто smile


--------------------
user posted image
PM   Вверх
Crypton
Дата 4.12.2009, 18:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 158
Регистрация: 9.10.2006
Где: США, Санкт-Петерб ург

Репутация: нет
Всего: 2



Цитата

Цитата(Crypton @  4.12.2009,  16:34 Найти цитируемый пост)
Хм, а как планируете хранить его на сервере? в Сессии или в определенном файле?

По вкусу и потребностям.
Я хранил даже в БД, но при большой нагрузке это не самый лучший вариант.
можно воспользоваться вариантом отсюда
http://forum.vingrad.ru/forum/topic-281995...d-memcahed.html
каждому хешу - свой файл.



Неплохой вариант. Мне даже самому нравится. 

Цитата

Цитата(Crypton @  4.12.2009,  16:34 Найти цитируемый пост)
А если юзверь откроет сразу много форм? 

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


Может тогда написать сборщик мусора?  smile 

Цитата

Цитата(Crypton @  4.12.2009,  16:34 Найти цитируемый пост)
они разводили руками и говорили "если работает, зачем менять?". 

Правильно говорят. 
Только донеси им мысль, что добавить новую функциональность не так просто smile


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


Это сообщение отредактировал(а) Crypton - 4.12.2009, 18:02
--------------------
«Все, что вы знаете — ложь» Теория мироздания 
PM MAIL WWW ICQ Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1523 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.