PHP: хранение «объектов» внутри $ _SESSION


188

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

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

Итак, вкратце : нормально ли хранить объекты в сеансе, есть ли проблемы с этим?


Редактировать:

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

Дальнейшие ответы могли бы развить этот аспект немного подробнее!


13
Насколько «глупым» я был в 2008 году :-)
Маркус

49
но полезный вопрос для «глупых, как мы в 2014 году»: D
Момин Аль Азиз

3
Очень хорошие вопросы, которые вы задали Маркусу .. :) Я прочитал это сегодня;)
gkd

1
Вы не были глупы! Вы спросили, что я собирался спросить, и сделали мне солидно, 10 лет спустя!
Toddmo

ну, я догадался, ты только что спас меня от глупого вопроса в 2019 году
Максвелл

Ответы:


133

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

Сохраняете ли вы объекты в $ _SESSION, или восстанавливаете их целиком на основе данных, хранящихся в скрытых полях формы, или повторно запрашиваете их из БД каждый раз, вы используете состояние. HTTP не имеет состояния (более или менее; но смотрите GET vs. PUT), но почти все, что кто-либо заботится о веб-приложении, требует сохранения состояния где-то. Действовать так, будто толкать государство в укромные уголки равносильно некоторой теоретической победе, просто неправильно. Государство есть государство. Если вы используете состояние, вы теряете различные технические преимущества, получаемые при отсутствии состояния. Это не то, чтобы потерять сон, если вы заранее не знаете, что вам следует потерять сон из-за этого.

Я особенно озадачен благословением, полученным "двойным ударом" аргументов, выдвинутых Хэнком Гаем. Создает ли ОП распределенную систему электронной коммерции с балансировкой нагрузки? Мое предположение нет; и я буду далее утверждать, что сериализация его класса $ User, или что-то еще, не повредит его серверу без возможности восстановления. Мой совет: используйте методы, которые подходят для вашего приложения. С объектами в $ _SESSION все в порядке, с учетом здравого смысла. Если ваше приложение вдруг превратится во что-то конкурирующее с Amazon по трафику, вам нужно будет заново адаптировать его. Такова жизнь.


16
Хороший ответ, включающий много моих собственных мыслей, когда я читал это. Современному интернету нужно государство. В то время как некоторые приложения не нуждаются в состоянии и имеют смысл создавать без сохранения состояния, современный Интернет полагается на слишком много систем, основанных на состоянии (AKA: логины!), Чтобы просто отказаться от них! Великие боги Интернета даже годами включали эту основную концепцию в виде файлов cookie, а на базовом уровне они добавляли ее в виде локального хранилища в HTML. Возможно, имеет смысл избегать чрезмерного использования состояния в некоторых приложениях, но некоторые! = Все!
RonLugge

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

Очень мало ответов заставляют меня смеяться вслух. Этот сделал. Браво +1
toddmo

114

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


12
Спасибо! Это исправило ошибку для меня: D
Мэтт Эллен

Я предполагаю, что этой проблемы можно избежать, если у вас есть правильная __autoload()функция.
Лангел

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

35

HTTP является протоколом без сохранения состояния по причине. Сеансы сваривают состояние по HTTP. Как правило, избегайте использования состояния сеанса.

ОБНОВЛЕНИЕ: нет концепции сеанса на уровне HTTP; серверы предоставляют это, предоставляя клиенту уникальный идентификатор и сообщая ему о необходимости повторной отправки при каждом запросе. Затем сервер использует этот идентификатор в качестве ключа в большой хэш-таблице объектов Session. Всякий раз, когда сервер получает запрос, он ищет информацию о сеансе в своей хэш-таблице объектов сеанса на основе идентификатора, предоставленного клиентом вместе с запросом. Вся эта дополнительная работа - двойной удар по масштабируемости (большая причина, по которой HTTP не сохраняет состояния).

  • Whammy One: он уменьшает работу, которую может выполнять один сервер.
  • Whammy Two: это усложняет масштабирование, потому что теперь вы не можете просто направить запрос на какой-либо старый сервер - они не все имеют одинаковый сеанс. Вы можете закрепить все запросы с данным идентификатором сессии на одном сервере. Это не просто, и это единственная точка отказа (не для системы в целом, а для больших групп ваших пользователей). Или вы можете разделить хранилище сеансов между всеми серверами в кластере, но теперь у вас есть больше сложностей: подключенная к сети память, автономный сервер сеансов и т. Д.

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

@Vinko Обычно вы можете обойти состояние хранилища сервера, внедрив данные, которые вы отслеживаете, в ответ, который вы отправляете обратно, и попросив клиента отправить его повторно, например, отправив данные в скрытый вход. Если вам действительно нужно отслеживание состояния на стороне сервера, оно, вероятно, должно быть в вашем резервном хранилище данных.

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


1
На уровне HTTP нет понятия сеанса; серверы предоставляют это, предоставляя клиенту уникальный идентификатор и сообщая ему о необходимости повторной отправки при каждом запросе. Затем сервер использует этот идентификатор в качестве ключа в большой хэш-таблице объектов Session. Продолжение следует ...
Хэнк Гей

1
Всякий раз, когда сервер получает запрос, он ищет информацию о сеансе в своей хэш-таблице объектов сеанса на основе идентификатора, предоставленного клиентом вместе с запросом. Вся эта дополнительная работа - двойной удар по масштабируемости (большая причина, по которой HTTP не сохраняет состояния). Продолжение следует…
Хэнк Гей

1
Интересно, как бы вы реализовали сложные приложения по HTTP без какого-либо состояния сварки?
Винко Врсалович,

3
пожалуйста, отредактируйте свой ответ, чтобы включить все эти комментарии. его легче читать и лучше для вики, и в любом случае я не могу выбрать ваш ответ в качестве принятого, если в комментариях есть все важное. Спасибо!
Маркус

6
"сглаз", хотелось бы понизить это. Знай своего времени. Ссылка на память стоит 100 нано секунд или 0,0001 мс. Поэтому поиск хеш-таблицы, хранящейся в основной памяти, буквально не тратит времени. Есть ли O(1)сказать вам что - нибудь? @ whammy два: просто не случайным образом перенаправить все запросы на случайные серверы? выполните циклический перебор и продолжайте маршрутизацию на один и тот же сервер от одного и того же пользователя. Это вау, супер очевидно. Вы должны вернуться к своим книгам вместе со всеми более 30 голосами против
Toskan

19
  • Объекты, которые нельзя сериализовать (или которые содержат не сериализуемые элементы), не будут выходить из $ _SESSION, как вы ожидаете
  • Огромные сеансы обременяют сервер (сериализация и десериализация мегабайт состояния каждый раз обходятся дорого)

Кроме этого я не видел проблем.


9

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


Любой комментарий о производительности между запросом таблицы данных 5x2 при каждом запросе и кэшированием результата в сеансе и использованием этого?
musicliftsme

6

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


Итак, лучше ли перестроить объект, включая повторное выполнение всех запросов к базе данных? Потому что одна из моих мыслей сделать это было то, что мне не нужно снова запрашивать БД для того же материала.
Маркус

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

Спасибо, я на самом деле думаю, что нет. Я должен просто запросить снова.
Маркус

4

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

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


0

Я также упоминал бы об обновлении библиотек программного обеспечения - мы обновили наше программное обеспечение, и у старой версии были объекты в сеансе с именами классов программного обеспечения V1, новое программное обеспечение терпело крах, когда оно пыталось построить объекты, которые были в сеансе - как V2 Программное обеспечение больше не использовало те же классы, оно не могло их найти. Нам пришлось вставить некоторый код исправления, чтобы обнаружить объекты сеанса, удалить сеанс, если он найден, перезагрузить страницу. Изначально самой большой болью было то, что вы воссоздали эту ошибку, когда о ней впервые сообщили (слишком знакомо, «ну, это работает для меня» :), поскольку она затрагивала только людей, которые недавно использовали старые и новые системы - однако, хорошо работу, которую мы нашли перед запуском, так как все наши пользователи наверняка имели бы старые переменные сеанса в своих сеансах и могли бы потерпеть крах для всех,

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.