Класс для работы с сессиями

Василий М.

Новичок
Туплю и не могу до конца понять, как все грамотно сделать.
Посоветуйте, в правильном ли я направлении двигаюсь:

PHP:
$session = Krugozor_Base_Session::getInstance('new_session');
$session['key'] = 'value';
$session->destroy();
PHP:
<?php
class Krugozor_Base_Session extends Krugozor_Cover_Abstract_Array
{
    private static $instance;

    /**
     * Имя сессии.
     *
     * @var string
     */
    private $session_name;

    /**
     * ID сессии.
     *
     * @var string
     */
    private $session_id;

    /**
     * Инициализация сессии.
     *
     * @param null|string $session_name имя сессии
     * @param null|string $session_id идентификатор сессии
     * @return Krugozor_Base_Session
     */
    public static function getInstance($session_name=null, $session_id=null)
    {
        if (self::$instance === null)
        {
            self::$instance = new self($session_name, $session_id);
        }

        return self::$instance;
    }

    public function __destruct()
    {
        $this->save();
    }

    /**
     * Уничтожает сессию.
     *
     * @param void
     * @return void
     */
    public function destroy()
    {
        $this->data = $_SESSION = array();

        if (ini_get('session.use_cookies'))
        {
            $params = session_get_cookie_params();
            setcookie(session_name(), '', time() - 42000,
                $params['path'], $params['domain'],
                $params['secure'], $params['httponly']
            );
        }

        session_destroy();
    }

	/**
	 * Возвращает имя сессии.
	 *
	 * @param void
	 * @return string
	 */
    public function getName()
    {
        return $this->session_name;
    }

    /**
     * Возвращает идентификатор сессии.
     *
     * @param void
     * @return string
     */
    public function getId()
    {
        return $this->session_id;
    }

    /**
     * Стартует сессию. Устанавливает имя сесии $session_name, если оно определено
     * и стандартное имя PHPSESSID в обратном случае.
     *
     * @param null|string $session_name имя сессии
     * @param null|string $session_id идентификатор сессии
     * @return void
     */
    protected function __construct($session_name=null, $session_id=null)
    {
        $this->session_name = !empty($session_name) ? $session_name : session_name();

        session_name($this->session_name);

        // Устанавливаем $this->session_id именно таким образом, а не через $this->setId() потому,
        // что в $this->start() идёт проверка с использованием session_id().
    	if (null !== $session_id)
        {
        	$this->session_id = $session_id;
        }

        $this->start();
    }

    /**
     * Стартует сессию.
     *
     * @param void
     * @return void
     */
    protected function start()
    {
        if (!$this->isStarted())
        {
        	if ($this->session_id)
        	{
        		$this->setId($this->session_id);
            }

            session_start();

            if (!empty($_SESSION))
            {
                $this->data = $_SESSION;
            }
        }
    }

    /**
     * Устанавливает ID сессии.
     *
     * @param string $sid идентификатор сессии
     * @return void
     */
    protected function setId($session_id)
    {
        $session_id = trim($session_id);

        if (preg_match('~[^a-z0-9,\-]+~i', $session_id))
        {
            throw new InvalidArgumentException('Попытка присвоить некорректный ID сессии.');
        }

        session_id($session_id);
    }

    /**
     * Возвращает TRUE, если сессия уже стартовала, FALSE в противном случае.
     *
     * @param void
     * @return boolean
     */
    protected function isStarted()
    {
        return !(session_id() === '');
    }

    /**
     * Сохраняет данные объекта в сессии.
     *
     * @param void
     * @return void
     */
    protected function save()
    {
        $_SESSION = $this->data;
    }
}
update: изменил немного
 

Василий М.

Новичок
PHP:
$session['key1']['key2'] = 'value';
будет работать?
это частности и офф. но работать будет, да:
Код:
Krugozor_Base_Session Object
(
    [session_name:Krugozor_Base_Session:private] => PHPSESSID
    [session_id:Krugozor_Base_Session:private] => 
    [data:protected] => Array
        (
            [key1] => Krugozor_Cover_Array Object
                (
                    [data:protected] => Array
                        (
                            [key2] => value
                        )

                )

        )

)
 

MiksIr

miksir@home:~$
А не может так оказаться, что на деструкторе вашего класса сессия уже отпишется?
 

MiksIr

miksir@home:~$
Деструктор в большинстве случаев будет срабатывать когда скрипт закончит свою работу. Сохранение сесси в файл происходит в тоже время. У вас есть уверенность, что сначала будет убит ваш объект, а потом записана сессия, а не наоборот? Насколько я знаю, много лет тому назад такая проблема была.
 

Ragazzo

TDD interested
MiksIr
Дак поэтому обычно и делают session_write_close() при завершении всего приложения, нет?
 

MiksIr

miksir@home:~$
Я ваще не в теме про эти ваши сессии =)))) Не использую давно. Просто помню была проблема такая. Может ее решили уже в PHP и принудительно записывают сессию только после уничтожения всех объектов, но в документации ничего про это не нашел.
Если сделать session_write_close руками в конце приложения, то его класс точно не будет работать - сессия уже закрыта, а объект еще жив, т.е. на десктрукторе будет шиш.
 

fixxxer

К.О.
Партнер клуба
А я вообще не использую php-сессии. :)
В принципе, если бы мне раньше не нужна была поддержка работы без cookies, я бы не заморачивался. Но была нужна. А в php-сессиях в этом месте один большой кривой хак.
Зато до кучи имею возможность использовать несколько сессий одновременно
 

Baton

Новичок
Василий М.
Cинглтон не спасет Ваши данные если кто-то напишет одну простую строчку кода:

PHP:
$_SESSION=array()
И еще, даже если выставить для сессий максимальное время жизни, все равно сборщик мусора может прибить все данные, если на вашем веб сервере живет несколько проектов, в одном из которых стоят настройки запускающие этот самый сборщик мусора. Он сработает для всех данных на сервере.

Поэтому ИМХО очень не надежно использовать $_SESSION для таких вещей.

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

fixxxer

К.О.
Партнер клуба
>>все равно сборщик мусора может прибить все данные, если на вашем веб сервере живет несколько проектов, в одном из которых стоят настройки запускающие этот самый сборщик мусора

bullshit

session.save_path всегда надо выставлять для каждого проекта свой. Кто это не делает - сам себе буратино. Да и вообще, если несколько проектов работают из под одного unix-пользователя - session storage это наименьшая из проблем. Дебиановский пакет писали вообще полные кретины, бред с кроном надо выпиливать сразу после установки (а еще лучше не пользоваться этими говнопакетами, в них очень много клинического бреда).

>>реализуйте свой собственный механизм хранения

у меня такой есть (я не использую ext/session по иными причинам, выше озвученным). файловая реализация полностью повторяет логику стандарного php-шного хранилища, по-другому это все равно не делается
 

Baton

Новичок
fixxxer
А Вы вкурсе, как работает сборщик мусора для нестандартных session.save_path? Я вот читал что нифига он не работает.
 

fixxxer

К.О.
Партнер клуба
Работает одинаково.
"Нифига не работает" относится к дебиановским пакетам, где отключен php-шный gc и зачисткой занимается крон-скрипт вида find /path/to/sessions/ -mtime ... -delete, таким "решениям" место на помойке, а авторам пакета - в биореакторе.
 
  • Like
Реакции: NeD

Baton

Новичок
fixxxer
Пардон за глупый вопрос, но меня смущает вот эта фраза из мана:

session_save_path
У этой директивы также существует дополнительный аргумент N, определяющий глубину размещения файлов сессии относительно указанной директории. Например, указание '5;/tmp' может в конечном итоге привести к такому размещению файла сессии: /tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If . Для того, чтобы использовать аргумент N, необходимо предварительно создать все эти директории. Помочь в этом может небольшой скрипт, расположенный в ext/session. Версия для bash называется mod_files.sh, а Windows-версия - mod_files.bat. Также следует учитывать, что если N определен и больше 0, то автоматическая сборка мусора не выполняется, подробнее см. информацию в файле php.ini.
Если я укажу session_save_path = c:/www/session_data, будет ли считаться что N>0?

Я просто не втыкаю зачем могла понадобиться такая настройка как N?
 

fixxxer

К.О.
Партнер клуба
Нет. N>0 если указать, например, session_save_path = "5;c:/www/session_data". В этом случае встроенный gc неработоспособен, т.к. не умеет ходить по поддиректориям - еще одна из недоработанных фич ext/session, которой лучше не пользоваться. А если пользоваться, то, действительно, надо делать про крон-скрипту на каждый session_save_path. Этот вопрос актуален в случае, если сессий ОЧЕНЬ много - в таком случае лучше выбрать иной способ хранения сессий, например, memcache.
 

Baton

Новичок
Вот запудрили мне мозги, я думал что в случае выставления своей директории => N>0 => gc идет лесом.

Но все равно я бы не стал использовать _SESSION, а содрал бы идею хранения данных в файлах с доступом по куке у пхп, со своим gc, ttl все как положено.

Собс-но уже содрал для хранения состояния в своем минивелосипеде, отсюда мои как оказалось неполные познания относительно _SESSION.

Большое спасибо fixxxer.
 

NeD

Новичок
"Нифига не работает" относится к дебиановским пакетам, где отключен php-шный gc и зачисткой занимается крон-скрипт вида find /path/to/sessions/ -mtime ... -delete, таким "решениям" место на помойке, а авторам пакета - в биореакторе.
:eek:
12 гигов накапало за 3 месяца
 

MiksIr

miksir@home:~$
А что вы там храните 3 месяца? Если вам нужно долго хранить данные - это в базу. Вот по-этому и не люблю сессии - на их основе чего только не делают, и большая часть - совершенно не по делу, начиная от долгосрочных хранилищ и заканчивая кешированием "что бы базу не грузить".
 

fixxxer

К.О.
Партнер клуба
Мне показалось, что он не собирался хранить, просто вступил в говн^W дебиановские пакеты.

Решается проблема с дебиановскими пакетами, кстати, простейшим образом - в php.ini возвращаются нормальные настройки session_gc и убивается говноскрипт из /etc/cron.d. Надо только не забыть вернуть нормальные права, если продолжать хранить в том каталоге, куда сует дебиан - а то они считают, что получать листинг директории с файлами сессий это НЕСЕКУРНО !1111. То есть "решили" проблему отсутствия рук и головы у админов shared-хостигов, неспособных осилить open_basedir, session_save_path и disable_functions, создав еще большие проблемы.
 

Baton

Новичок
А завтра они еще чего-нибудь наворочают в сессиях о чем мы и не могли мечтать, а нам разгребать. Только свой датастор.
 
Сверху