标签归档:SESSION

Laravel Session详解

Laravel中Session的实现并没有使用PHP本身的Session扩展。而是自己实现了一套Session实现。好处是灵活,不好是不兼容使用PHP原生Session实现的应用。

Session是一个服务,当然是由SessionServiceProvider.php引入框架的:

<?php
namespace Illuminate\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->registerSessionManager();

        $this->registerSessionDriver();

        $this->app->singleton('Illuminate\Session\Middleware\StartSession');
    }

    protected function registerSessionManager()
    {
        $this->app->singleton('session', function ($app) {
            return new SessionManager($app);
        });
    }

    protected function registerSessionDriver()
    {
        $this->app->singleton('session.store', function ($app) {
            $manager = $app['session'];
            return $manager->driver();
        });
    }
}

注册了一个session,对应SessionManager实例,session.store是$manager->driver()返回的实例,它是Session存在的默认配置。关于Manager的实现都是老套路。最后关键的是绑定了一个Illuminate\Session\Middleware\StartSession。

在框架启动流程中:

namespace App\Http;
 
use Illuminate\Foundation\Http\Kernel as HttpKernel;
 
class Kernel extends HttpKernel {
    protected $middleware = [
        'Illuminate\Session\Middleware\StartSession',
    ];
 
}

这里的$middleware就是定义路由前需要调用的中间件,所以Session的逻辑就在这里被勾进来的。大概看一下这个中间件的hanlder方法:

    public function handle($request, Closure $next)
    {
        $this->sessionHandled = true;

        // If a session driver has been configured, we will need to start the session here
        // so that the data is ready for an application. Note that the Laravel sessions
        // do not make use of PHP "native" sessions in any way since they are crappy.
        if ($this->sessionConfigured()) {
            $session = $this->startSession($request);

            $request->setSession($session);
        }

        $response = $next($request);

        // Again, if the session has been configured we will need to close out the session
        // so that the attributes may be persisted to some storage medium. We will also
        // add the session identifier cookie to the application response headers now.
        if ($this->sessionConfigured()) {
            $this->storeCurrentUrl($request, $session);

            $this->collectGarbage($session);

            $this->addCookieToResponse($response, $session);
        }

        return $response;
    }

首先启动Session,然后调用$request->setSession($session),这样$request->session()就可用了。接下来存储当前URL,垃圾回收,添加会话Cookie到响应。从这里来看,每次响应都应该有一个setCookie的响应头。

简单来说,每次都会从客户端cookie中取回Session ID,由于这个Session ID由Cookie传输,其加解密工作由Cookie完成,如果接收不到这个ID就启动一个新的ID。然后使用这个ID,通过Handler类提供的方法,把存在的数据load回来(没有就是空),产生会话token(如果没有的话),标记会话启动。

不同的Driver实际上就是对应应用了不同Hander的Store对象(以上的描述的过程就是在Store中完成的),Store应用Hanlder方法取回和保存数据,所以如果要实现把Session数据保存到其它介质,只要实现Hanlder接口即可。另外,保存的Session数据是可以做加密保存的,这个取决于conf/session.php中的配置。

对Session的操作的方法,都是由Store对象提供的,可以通过$request->session()获取到SessionManager,也可以使用全局的方法session(),或者app(“session”)都是可以的,不过需要之一,这里返回的都是一个SessionManager实例,然而可以通过它间接调用默认Driver(Store对象)的方法,app(‘session.store’)才是真正对应Store对象。

用法:

$value = $request->session()->get('key', 'default');

$value = $request->session()->get('key', function() {
    return 'default';
});

$data = $request->session()->all();

#使用session()
Route::get('home', function () {
    // 从session中获取数据...
    $value = session('key');

    // 存储数据到session...
    session(['key' => 'value']);
});

if ($request->session()->has('users')) {
    //
}

$request->session()->put('key', 'value');

$request->session()->push('user.teams', 'developers');

$value = $request->session()->pull('key', 'default');

$request->session()->forget('key');
$request->session()->flush();

$request->session()->regenerate();

$request->session()->flash('status', 'Task was successful!');

$request->session()->reflash();
$request->session()->keep(['username', 'email']);

另外一个比较坑的地方,估计就是会话数据的存储了,从源代码来看,Session数据视乎是在发送前些前才一次性把会话数据同步到存储介质,那么就是说如果中间异常退出,那么之前保存的数据就无法持久化。

PHP SESSION 垃圾回收机制

PHP SESSION 垃圾回收机制涉及三个参数(在php.ini中设置):

session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1800

会话保存在服务器端,它的有效时间由session.gc_maxlifetime决定,比如会话A超过了1800秒不操作,那么这个会话就已经过期(属于垃圾),过程的会话不是一旦过期就会被清理的,它需要等待垃圾回收器执行。这里涉及到一个垃圾回收器触发的问题。PHP执行脚本完毕后就可以认为对应的进程退出,它不是长驻内存的,所以垃圾回收器的触发必定是在会话启动时去触发,但是如果每次会话都启动垃圾回收清扫一遍垃圾,显然很不现实。这就是session.gc_probability和session.gc_divisor的作用。

简单来说,session.gc_probability和session.gc_divisor决定了在每次启动会话时,垃圾回收器可能被触发的概率。计算公式是:session.gc_probability/session.gc_divisor,对以上参数的配置,这个概率是百分之一。换个通俗的说法就是每次会话开始(session_start())时,垃圾回收器有百分之一的概率被触发,一旦触发,那些过期的会话(文件等)就会被清理。

从原理上,垃圾回收器触发后,扫描一遍会话文件(假设会话使用文件存储),对每个扫描的文件,判断它是否过期,如果过期就删除。所以,如果会话很多,而且垃圾回收器被触发的概率又很大,那么产生的IO就会上升,甚至影响到系统性能。最简单的办法是调大session.gc_divisor值,这样垃圾回收器被触发的概率就会变小,如果把会话保存在共享内存中,则可以适当调大回收器被触发的概率,因为内存的读写速度比硬盘自然快很多。

另外,session.cookie_lifetime的设置实际跟SESSION的在服务端的保存没有关系。在SESSION和客户端使用cookie进行交互时,它的设置影响到这个cookie的生存时间,如果设置为0,说明浏览器关闭时,这个cookie被删除,但是这个cookie关系的SESSION是否被清理和它没有关系。

在session.cookie_lifetime设置为0的情况下,如果浏览器不退出,你可能碰到登录了很久,但是都不超时的情况。这个情况可能是,一,访问量少;二,垃圾回收器触发概率太小;使得垃圾回收器没有被触发过。如果要严格进行超时控制,单纯依靠PHP的SESSION机制是不行的,我们可以在登录时,把登录的时间记录一下,第二次操作这个会话时,判断一下是否超时,超时就直接清空会话,定位到登录页。

永久链接:http://blog.ifeeline.com/1596.html

PHP框架Phalcon 之 闪存消息 Session

闪存消息(Flashing Messages)
Flash messages are used to notify the user about the state of actions he/she made or simply show information to the users. These kind of messages can be generated using this component.

适配器(Adapters)
This component makes use of adapters to define the behavior of the messages after being passed to the Flasher:

Adapter Description API
Direct Directly outputs the messages passed to the flasher Phalcon\Flash\Direct
Session Temporarily stores the messages in session, then messages can be printed in the next request Phalcon\Flash\Session

使用(Usage)
Usually the Flash Messaging service is requested from the services container, if you’re using Phalcon\DI\FactoryDefault then Phalcon\Flash\Direct is automatically registered as “flash” service:

<?php

//Set up the flash service
$di->set('flash', function() {
    return new \Phalcon\Flash\Direct();
});

This way, you can use it in controllers or views by injecting the service in the required scope:

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function saveAction()
    {
        $this->flash->success("The post was correctly saved!");
    }

There are four built-in message types supported:
有4个内置消息类型:

<?php

$this->flash->error("too bad! the form had errors");
$this->flash->success("yes!, everything went very smoothly");
$this->flash->notice("this a very important information");
$this->flash->warning("best check yo self, you're not looking too good.");

You can add messages with your own types:
注意,可以通过调用message()方法建立一种新消息类型:

<?php

$this->flash->message("debug", "this is debug message, you don't say");

输出信息(Printing Messages)
Messages sent to the flash service are automatically formatted with html:

<div class="errorMessage">too bad! the form had errors</div>
<div class="successMessage">yes!, everything went very smoothly</div>
<div class="noticeMessage">this a very important information</div>
<div class="warningMessage">best check yo self, you're not looking too good.</div>

As you can see, CSS classes are added automatically to the DIVs. These classes allow you to define the graphical presentation of the messages in the browser. The CSS classes can be overridden, for example, if you’re using Twitter bootstrap, classes can be configured as:

<?php

//Register the flash service with custom CSS classes
$di->set('flash', function(){
    $flash = new \Phalcon\Flash\Direct(array(
        'error' => 'alert alert-error',
        'success' => 'alert alert-success',
        'notice' => 'alert alert-info',
    ));
    return $flash;
});

(可以做有限定制)
Then the messages would be printed as follows:

<div class="alert alert-error">too bad! the form had errors</div>
<div class="alert alert-success">yes!, everything went very smoothly</div>
<div class="alert alert-info">this a very important information</div>

绝对刷送与会话(Implicit Flush vs. Session)
Depending on the adapter used to send the messages, it could be producing output directly, or be temporarily storing the messages in session to be shown later. When should you use each? That usually depends on the type of redirection you do after sending the messages. For example, if you make a “forward” is not necessary to store the messages in session, but if you do a HTTP redirect then, they need to be stored in session:
(这段说应用场景)

<?php

class ContactController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function saveAction()
    {

        //store the post

        //Using direct flash
        $this->flash->success("Your information was stored correctly!");

        //Forward to the index action
        return $this->dispatcher->forward(array("action" => "index"));
    }

}

Or using a HTTP redirection:

<?php

class ContactController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function saveAction()
    {

        //store the post

        //Using session flash
        $this->flashSession->success("Your information was stored correctly!");

        //Make a full HTTP redirection
        return $this->response->redirect("contact/index");
    }

}

In this case you need to manually print the messages in the corresponding view:
这样你需要手动在相关视图中打印消息:

<!-- app/views/contact/index.phtml -->

<p><?php $this->flashSession->output() ?></p>

The attribute ‘flashSession’ is how the flash was previously set into the dependency injection container. You need to start the session first to successfully use the flashSession messenger.
属性flashSession跟之前在DI中设置flash一样。在使用flashSession消息器之前你要首先开启会话。

一般会话在用到时自动开启:

//在DI中设置session,让其在使用到session时启动并返回实例
$di['session'] = function () {
    $session = new SessionAdapter();
    $session->start();

    return $session;
};

以下是会话存储内容:
————————————————
测试程序:

    public function indexAction()
    {
        //Session do not start, so $_SESSION do not exists
        //print_r($_SESSION);

        $this->persistent->name = "index controller.";

        $user       = new \Phalcon\Session\Bag('user');
        $user->setDI($this->getDI());
        $user->name = "Kimbra Johnson";
        $user->age  = 22;

        //Session have start.
        print_r($_SESSION);
        print_r($this->session);

        echo $this->session->getId();
        $this->view->disable();
    }
//输出
Array
(
    [user] => Array
        (
            [name] => Kimbra Johnson
            [age] => 22
        )

    [Vf\Frontend\Controllers\IndexController] => Array
        (
            [name] => index controller.
        )

    [ifeelineVf\Frontend\Controllers\IndexController] => Array
        (
            [name] => index controller.
        )

    [ifeelineuser] => Array
        (
            [name] => Kimbra Johnson
            [age] => 22
        )

)
Phalcon\Session\Adapter\Files Object
(
    [_uniqueId:protected] => ifeeline
    [_started:protected] => 1
    [_options:protected] => Array
        (
            [uniqueId] => ifeeline
        )

)
ducm4na6838kdfvmu1f5jdhls0

从输出来看,所谓的分组,不过是$_SESSION中设置一个变量名称执行一个数组,Bag就是这样的一个变量。组件中的persistent就是DI资源的persistent,它是一个Bag对象,如果在组件中设置了persistent,那么对应到$_SESSION时使用组件的完整名作为下标,所以这个组的数据就显得只有这个组件才能使用。另外如果设置了uniqueId,它就在原有基础上复制一遍然后添加前缀。

找不到修改会话ID对应名称修改的封装(这个名称实际无关痛痒,默认使用php.ini的默认设置),也没有修改传送会话ID的cookie的封装,基本上,Phalcon中提供的Session封装,相对单薄了一些。

PHP的Session可以参考:http://blog.ifeeline.com/300.html

Phalcon 1.3.x中提供了一个叫Phalcon\Session\Adapter\Libmemcached的适配器(类似Phalcon\Session\Adapter\Memcache),可以通过它把会话数据保存到Memcached中:

//官方代码给出的例子
	 * $session = new Phalcon\Session\Adapter\Libmemcached(array(
	 *     'servers' => array(
	 *         array('host' => 'localhost', 'port' => 11211, 'weight' => 1),
	 *     ),
	 *     'client' => array(
	 *         Memcached::OPT_HASH => Memcached::HASH_MD5,
	 *         Memcached::OPT_PREFIX_KEY => 'prefix.',
	 *     ),
	 *    'lifetime' => 3600,
	 *    'prefix' => 'my_'
	 * ));
	 *
	 * $session->start();
	 *
	 * $session->set('var', 'some-value');
	 *
	 * echo $session->get('var');

另外,我们知道如要把会话数据放到其它介质,可以通过在php.ini中配置session.save_handler = files|mm|sqlite|user指令(user表示使用自定义函数),还可以通过执行session_set_save_handler()函数来修改会话的保存和获取的逻辑。它要实现一系列函数,也是会话适配器的要实现的方法,可以查看https://github.com/phalcon/incubator/blob/master/Library/Phalcon/Session/Adapter/Database.php的实现方法:

    public function __construct($options = null)
    {
        //......

        parent::__construct($options);

        session_set_save_handler(
            array($this, 'open'),
            array($this, 'close'),
            array($this, 'read'),
            array($this, 'write'),
            array($this, 'destroy'),
            array($this, 'gc')
        );
    }

———————————————-

使用 Session 存储数据(Storing data in Session)
The Phalcon\Session provides object-oriented wrappers to access session data.

Reasons to use this component instead of raw-sessions:
使用这个组件替换原生的session的原因:
You can easily isolate session data across applications on the same domain
Intercept where session data is set/get in your application
Change the session adapter according to the application need 随时改变会话适配器

启动会话(Starting the Session)
Some applications are session-intensive, almost any action that performs requires access to session data. There are others who access session data casually. Thanks to the service container, we can ensure that the session is accessed only when it’s clearly needed:
用到时才启用:

<?php

//Start the session the first time when some component request the session service
$di->setShared('session', function() {
    $session = new Phalcon\Session\Adapter\Files();
    $session->start();
    return $session;
});

Session 的存储与读取(Storing/Retrieving data in Session)
From a controller, a view or any other component that extends Phalcon\DI\Injectable you can access the session service and store items and retrieve them in the following way:

<?php

class UserController extends Phalcon\Mvc\Controller
{

    public function indexAction()
    {
        //Set a session variable
        $this->session->set("user-name", "Michael");
    }

    public function welcomeAction()
    {

        //Check if the variable is defined
        if ($this->session->has("user-name")) {

            //Retrieve its value
            $name = $this->session->get("user-name");
        }
    }

}

Sessions 的删除和销毁(Removing/Destroying Sessions)
It’s also possible remove specific variables or destroy the whole session:

<?php

class UserController extends Phalcon\Mvc\Controller
{

    public function removeAction()
    {
        //Remove a session variable
        $this->session->remove("user-name");
    }

    public function logoutAction()
    {
        //Destroy the whole session
        $this->session->destroy();
    }

}

隔离不同应用的会话数据(Isolating Session Data between Applications)
Sometimes a user can use the same application twice, on the same server, in the same session. Surely, if we use variables in session, we want that every application have separate session data (even though the same code and same variable names). To solve this, you can add a prefix for every session variable created in a certain application:
为特定的应用对每个创建的会话变量添加一个前缀:

<?php

//Isolating the session data
$di->set('session', function(){

    //All variables created will prefixed with "my-app-1"
    $session = new Phalcon\Session\Adapter\Files(
        array(
            'uniqueId' => 'my-app-1'
        )
    );

    $session->start();

    return $session;
});

会话袋(Session Bags)
Phalcon\Session\Bag is a component that helps separating session data into “namespaces”. Working by this way you can easily create groups of session variables into the application. By only setting the variables in the “bag”, it’s automatically stored in session:
为会话变量分组,只要把变量放入袋子中,它自动在会话中存储:

<?php

$user       = new Phalcon\Session\Bag('user');
$user->setDI($di);
$user->name = "Kimbra Johnson";
$user->age  = 22;

组件的持久数据(Persistent Data in Components)
Controller, components and classes that extends Phalcon\DI\Injectable may inject a Phalcon\Session\Bag. This class isolates variables for every class. Thanks to this you can persist data between requests in every class in an independent way.
Phalcon\Session\Bag对应的DI资源名称是persistent。它用来为每个类隔离变量。

<?php

class UserController extends Phalcon\Mvc\Controller
{

    public function indexAction()
    {
        // Create a persistent variable "name"
        $this->persistent->name = "Laura";
    }

    public function welcomeAction()
    {
        if (isset($this->persistent->name))
        {
            echo "Welcome, ", $this->persistent->name;
        }
    }

}

In a component:

<?php

class Security extends Phalcon\Mvc\User\Component
{

    public function auth()
    {
        // Create a persistent variable "name"
        $this->persistent->name = "Laura";
    }

    public function getAuthName()
    {
        return $this->persistent->name;
    }

}

The data added to the session ($this->session) are available throughout the application, while persistent ($this->persistent) can only be accessed in the scope of the current class.
$this->session中设置的数据是在整个应用中都有可用的,而$this->persistent只能在当前类的作用域内被访问到。

自定义适配器(Implementing your own adapters)
The Phalcon\Session\AdapterInterface interface must be implemented in order to create your own session adapters or extend the existing ones.

There are more adapters available for this components in the Phalcon Incubator

(以上关于SESSION的内容是远远不够的)

PHP框架Phalcon 之 URL 闪存消息 会话

生成 URL 和 路径(Generating URLs and Paths)
Phalcon\Mvc\Url is the component responsible of generate urls in a Phalcon application. It’s capable of produce independent urls based on routes.
在Phalcon应用中Phalcon\Mvc\Url是产生url的组件的代表。它能够产生基于路由的独立url。

设置站点基地址(Setting a base URI)
Depending of which directory of your document root your application is installed, it may have a base uri or not.
依赖应用安装的文档根目录,可能有基本uri。

For example, if your document root is /var/www/htdocs and your application is installed in /var/www/htdocs/invo then your baseUri will be /invo/. If you are using a VirtualHost or your application is installed on the document root, then your baseUri is /. Execute the following code to know the base uri detected by Phalcon:()

<?php

$url = new Phalcon\Mvc\Url();
echo $url->getBaseUri();

By default, Phalcon automatically may detect your baseUri, but if you want to increase the performance of your application is recommended setting up it manually:
默认,Phalcon自动地检测baseUri,如果你想提升应用的性能建议手动设置:

<?php

$url = new Phalcon\Mvc\Url();

//Setting a relative base URI
$url->setBaseUri('/invo/');

//Setting a full domain as base URI
$url->setBaseUri('//my.domain.com/');

//Setting a full domain as base URI
$url->setBaseUri('http://my.domain.com/my-app/');

Usually, this component must be registered in the Dependency Injector container, so you can set up it there:
通常,组件必须在DI容器中注册:

<?php

$di->set('url', function(){
    $url = new Phalcon\Mvc\Url();
    $url->setBaseUri('/invo/');
    return $url;
});

生成 URI(Generating URIs)
If you are using the Router with its default behavior. Your application is able to match routes based on the following pattern: /:controller/:action/:params. Accordingly it is easy to create routes that satisfy that pattern (or any other pattern defined in the router) passing a string to the method “get”:

<?php echo $url->get("products/save") ?>

Note that isn’t necessary to prepend the base uri. If you have named routes you can easily change it creating it dynamically. For Example if you have the following route:
注意不需要添加基础uri。如果你有命名路由,你可以很容易改变并动态创建。比如你有如下路由:

<?php

$route->add('/blog/{$year}/{month}/{title}', array(
    'controller' => 'posts',
    'action' => 'show'
))->setName('show-post');

A URL can be generated in the following way:

<?php

//This produces: /blog/2012/01/some-blog-post
$url->get(array(
    'for' => 'show-post',
    'year' => 2012,
    'month' => '01',
    'title' => 'some-blog-post'
));

没有伪静态状态下的生成 URL(Producing URLs without Mod-Rewrite)
You can use this component also to create urls without mod-rewrite:

<?php

$url = new Phalcon\Mvc\Url();

//Pass the URI in $_GET["_url"]
$url->setBaseUri('/invo/index.php?_url=/');

//This produce: /invo/index.php?_url=/products/save
echo $url->get("products/save");

You can also use $_SERVER[“REQUEST_URI”]:

<?php

$url = new Phalcon\Mvc\Url();

//Pass the URI in $_GET["_url"]
$url->setBaseUri('/invo/index.php?_url=/');

//Pass the URI using $_SERVER["REQUEST_URI"]
$url->setBaseUri('/invo/index.php/');

In this case, it’s necessary to manually handle the required URI in the Router:

<?php

$router = new Phalcon\Mvc\Router();

// ... define routes

$uri = str_replace($_SERVER["SCRIPT_NAME"], '', $_SERVER["REQUEST_URI"]);
$router->handle($uri);

The produced routes would look like:

<?php

//This produce: /invo/index.php/products/save
echo $url->get("products/save");

Volt 中生成 URL(Volt Producing URLs from Volt)
静态 URI 与 动态 URI(Static vs. Dynamic Uris)
This component allow you to set up a different base uri for static resources in the application:
这个组件允许你针对应用中的静态资源设置一个不同的基uri:

<?php

$url = new Phalcon\Mvc\Url();

//Dynamic URIs are
$url->setBaseUri('/');

//Static resources go through a CDN
$url->setStaticBaseUri('http://static.mywebsite.com/');

Phalcon\Tag will request both dynamical and static URIs using this component.
Phalcon\Tag将通过使用这个组件请求动态和静态URI。

自定义 URL 生成器(Implementing your own Url Generator)
The Phalcon\Mvc\UrlInterface interface must be implemented to create your own URL generator replacing the one provided by Phalcon.

闪存消息(Flashing Messages)
Flash messages are used to notify the user about the state of actions he/she made or simply show information to the users. These kind of messages can be generated using this component.

适配器(Adapters)
This component makes use of adapters to define the behavior of the messages after being passed to the Flasher:

Adapter Description API
Direct Directly outputs the messages passed to the flasher Phalcon\Flash\Direct
Session Temporarily stores the messages in session, then messages can be printed in the next request Phalcon\Flash\Session

使用(Usage)
Usually the Flash Messaging service is requested from the services container, if you’re using Phalcon\DI\FactoryDefault then Phalcon\Flash\Direct is automatically registered as “flash” service:

<?php

//Set up the flash service
$di->set('flash', function() {
    return new \Phalcon\Flash\Direct();
});

This way, you can use it in controllers or views by injecting the service in the required scope:

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function saveAction()
    {
        $this->flash->success("The post was correctly saved!");
    }

There are four built-in message types supported:
有4个内置消息类型:

<?php

$this->flash->error("too bad! the form had errors");
$this->flash->success("yes!, everything went very smoothly");
$this->flash->notice("this a very important information");
$this->flash->warning("best check yo self, you're not looking too good.");

You can add messages with your own types:
注意,可以通过调用message()方法建立一种新消息类型:

<?php

$this->flash->message("debug", "this is debug message, you don't say");

输出信息(Printing Messages)
Messages sent to the flash service are automatically formatted with html:

<div class="errorMessage">too bad! the form had errors</div>
<div class="successMessage">yes!, everything went very smoothly</div>
<div class="noticeMessage">this a very important information</div>
<div class="warningMessage">best check yo self, you're not looking too good.</div>

As you can see, CSS classes are added automatically to the DIVs. These classes allow you to define the graphical presentation of the messages in the browser. The CSS classes can be overridden, for example, if you’re using Twitter bootstrap, classes can be configured as:

<?php

//Register the flash service with custom CSS classes
$di->set('flash', function(){
    $flash = new \Phalcon\Flash\Direct(array(
        'error' => 'alert alert-error',
        'success' => 'alert alert-success',
        'notice' => 'alert alert-info',
    ));
    return $flash;
});

(可以做有限定制)
Then the messages would be printed as follows:

<div class="alert alert-error">too bad! the form had errors</div>
<div class="alert alert-success">yes!, everything went very smoothly</div>
<div class="alert alert-info">this a very important information</div>

绝对刷送与会话(Implicit Flush vs. Session)
Depending on the adapter used to send the messages, it could be producing output directly, or be temporarily storing the messages in session to be shown later. When should you use each? That usually depends on the type of redirection you do after sending the messages. For example, if you make a “forward” is not necessary to store the messages in session, but if you do a HTTP redirect then, they need to be stored in session:
(这段说应用场景)

<?php

class ContactController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function saveAction()
    {

        //store the post

        //Using direct flash
        $this->flash->success("Your information was stored correctly!");

        //Forward to the index action
        return $this->dispatcher->forward(array("action" => "index"));
    }

}

Or using a HTTP redirection:

<?php

class ContactController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function saveAction()
    {

        //store the post

        //Using session flash
        $this->flashSession->success("Your information was stored correctly!");

        //Make a full HTTP redirection
        return $this->response->redirect("contact/index");
    }

}

In this case you need to manually print the messages in the corresponding view:
这样你需要手动在相关视图中打印消息:

<!-- app/views/contact/index.phtml -->

<p><?php $this->flashSession->output() ?></p>

The attribute ‘flashSession’ is how the flash was previously set into the dependency injection container. You need to start the session first to successfully use the flashSession messenger.
属性flashSession跟之前在DI中设置flash一样。在使用flashSession消息器之前你要首先开启会话。

一般会话在用到时自动开启:

//在DI中设置session,让其在使用到session时启动并返回实例
$di['session'] = function () {
    $session = new SessionAdapter();
    $session->start();

    return $session;
};

————————————————————————————————
测试程序:

    public function indexAction()
    {
        //Session do not start, so $_SESSION do not exists
        //print_r($_SESSION);

        $this->persistent->name = "index controller.";

        $user       = new \Phalcon\Session\Bag('user');
        $user->setDI($this->getDI());
        $user->name = "Kimbra Johnson";
        $user->age  = 22;

        //Session have start.
        print_r($_SESSION);
        print_r($this->session);

        echo $this->session->getId();
        $this->view->disable();
    }
//输出
Array
(
    [user] => Array
        (
            [name] => Kimbra Johnson
            [age] => 22
        )

    [Vf\Frontend\Controllers\IndexController] => Array
        (
            [name] => index controller.
        )

    [ifeelineVf\Frontend\Controllers\IndexController] => Array
        (
            [name] => index controller.
        )

    [ifeelineuser] => Array
        (
            [name] => Kimbra Johnson
            [age] => 22
        )

)
Phalcon\Session\Adapter\Files Object
(
    [_uniqueId:protected] => ifeeline
    [_started:protected] => 1
    [_options:protected] => Array
        (
            [uniqueId] => ifeeline
        )

)
ducm4na6838kdfvmu1f5jdhls0

从输出来看,所谓的分组,不过是$_SESSION中设置一个变量名称执行一个数组,Bag就是这样的一个变量。组件中的persistent就是DI资源的persistent,它是一个Bag对象,如果在组件中设置了persistent,那么对应到$_SESSION时使用组件的完整名作为下标,所以这个组的数据就显得只有这个组件才能使用。另外如果设置了uniqueId,它就在原有基础上复制一遍然后添加前缀。

找不到修改会话ID对应名称修改的封装(这个名称实际无关痛痒,默认使用php.ini的默认设置),也没有修改传送会话ID的cookie的封装,基本上,Phalcon中提供的Session封装,相对单薄了一些。
————————————————————————————————

使用 Session 存储数据(Storing data in Session)
The Phalcon\Session provides object-oriented wrappers to access session data.

Reasons to use this component instead of raw-sessions:
使用这个组件替换原生的session的原因:
You can easily isolate session data across applications on the same domain
Intercept where session data is set/get in your application
Change the session adapter according to the application need 随时改变会话适配器

启动会话(Starting the Session)
Some applications are session-intensive, almost any action that performs requires access to session data. There are others who access session data casually. Thanks to the service container, we can ensure that the session is accessed only when it’s clearly needed:
用到时才启用:

<?php

//Start the session the first time when some component request the session service
$di->setShared('session', function() {
    $session = new Phalcon\Session\Adapter\Files();
    $session->start();
    return $session;
});

Session 的存储与读取(Storing/Retrieving data in Session)
From a controller, a view or any other component that extends Phalcon\DI\Injectable you can access the session service and store items and retrieve them in the following way:

<?php

class UserController extends Phalcon\Mvc\Controller
{

    public function indexAction()
    {
        //Set a session variable
        $this->session->set("user-name", "Michael");
    }

    public function welcomeAction()
    {

        //Check if the variable is defined
        if ($this->session->has("user-name")) {

            //Retrieve its value
            $name = $this->session->get("user-name");
        }
    }

}

Sessions 的删除和销毁(Removing/Destroying Sessions)
It’s also possible remove specific variables or destroy the whole session:

<?php

class UserController extends Phalcon\Mvc\Controller
{

    public function removeAction()
    {
        //Remove a session variable
        $this->session->remove("user-name");
    }

    public function logoutAction()
    {
        //Destroy the whole session
        $this->session->destroy();
    }

}

隔离不同应用的会话数据(Isolating Session Data between Applications)
Sometimes a user can use the same application twice, on the same server, in the same session. Surely, if we use variables in session, we want that every application have separate session data (even though the same code and same variable names). To solve this, you can add a prefix for every session variable created in a certain application:
为特定的应用对每个创建的会话变量添加一个前缀:

<?php

//Isolating the session data
$di->set('session', function(){

    //All variables created will prefixed with "my-app-1"
    $session = new Phalcon\Session\Adapter\Files(
        array(
            'uniqueId' => 'my-app-1'
        )
    );

    $session->start();

    return $session;
});

会话袋(Session Bags)
Phalcon\Session\Bag is a component that helps separating session data into “namespaces”. Working by this way you can easily create groups of session variables into the application. By only setting the variables in the “bag”, it’s automatically stored in session:
为会话变量分组,只要把变量放入袋子中,它自动在会话中存储:

<?php

$user       = new Phalcon\Session\Bag('user');
$user->setDI($di);
$user->name = "Kimbra Johnson";
$user->age  = 22;

组件的持久数据(Persistent Data in Components)
Controller, components and classes that extends Phalcon\DI\Injectable may inject a Phalcon\Session\Bag. This class isolates variables for every class. Thanks to this you can persist data between requests in every class in an independent way.
Phalcon\Session\Bag对应的DI资源名称是persistent。它用来为每个类隔离变量。

<?php

class UserController extends Phalcon\Mvc\Controller
{

    public function indexAction()
    {
        // Create a persistent variable "name"
        $this->persistent->name = "Laura";
    }

    public function welcomeAction()
    {
        if (isset($this->persistent->name))
        {
            echo "Welcome, ", $this->persistent->name;
        }
    }

}

In a component:

<?php

class Security extends Phalcon\Mvc\User\Component
{

    public function auth()
    {
        // Create a persistent variable "name"
        $this->persistent->name = "Laura";
    }

    public function getAuthName()
    {
        return $this->persistent->name;
    }

}

The data added to the session ($this->session) are available throughout the application, while persistent ($this->persistent) can only be accessed in the scope of the current class.
$this->session中设置的数据是在整个应用中都有可用的,而$this->persistent只能在当前类的作用域内被访问到。

自定义适配器(Implementing your own adapters)
The Phalcon\Session\AdapterInterface interface must be implemented in order to create your own session adapters or extend the existing ones.

There are more adapters available for this components in the Phalcon Incubator

(以上关于SESSION的内容是远远不够的)

Magento Session与Session共享(Memcached)

前台控制器从Mage_Core_Controller_Front_Action继承,后台控制器从Mage_Adminhtml_Controller_Action继承,而它们的父类都是Mage_Core_Controller_Varien_Action,这里面的preDispatch方法是每个控制器在调用它的方法前都被执行的。里面有初始化会话的代码:

$session = Mage::getSingleton('core/session', array('name' => $this->_sessionNamespace))->start();

这里的对应所在的具体对象不同,可能为fontend和adminhtml。实际上Mage_Adminhtml_Controller_Action覆盖了preDispatch方法,它实现了自己的逻辑。
Magento会话类层次

搜索:“extends Mage_Core_Model_Session_Abstract”发现有22个匹配。

Mage_Core_Model_Session的构造函数:

    public function __construct($data=array())
    {
        $name = isset($data['name']) ? $data['name'] : null;
        $this->init('core', $name);
    }

把传递进来的$name传递到init函数初始化,这个函数从它的父类Mage_Core_Model_Session_Abstract继承过来:

//Mage_Core_Model_Session_Abstract
	// $namespace->core  $sessionName->frontend
    public function init($namespace, $sessionName=null)
    {
        parent::init($namespace, $sessionName);
        $this->addHost(true);
        return $this;
    }

而这个方法中又调用它的父类的Mage_Core_Model_Session_Abstract_Varien方法:

    public function init($namespace, $sessionName=null)
{
		//启动会话,$sessionName -> frontend
        if (!isset($_SESSION)) {
            $this->start($sessionName);
        }
		//
        if (!isset($_SESSION[$namespace])) {
            $_SESSION[$namespace] = array();
        }

        $this->_data = &$_SESSION[$namespace];

        $this->validate();
        $this->revalidateCookie();

        return $this;
    }

这个方法如果没有设置$_SESSION(默认Magento把自动启动会话关闭),就可以调用start方法开启会话。实际上,全局这里只调用这一次(区分前后台),因为:

    public function start($sessionName=null)
    {
        if (isset($_SESSION)) {
            return $this;
        }
		//…
	}

start方法中,只要会话一开始,就直接返回。不过为了确保会话正常开始,在实例化Mage_Core_Model_Session时还是调用它的start方法。

接下来start中的这段代码:

        switch($this->getSessionSaveMethod()) {
            case 'db':
                ini_set('session.save_handler', 'user');
                $sessionResource = Mage::getResourceSingleton('core/session');
                /* @var $sessionResource Mage_Core_Model_Mysql4_Session */
                $sessionResource->setSaveHandler();
                break;
            case 'memcache':
                ini_set('session.save_handler', 'memcache');
                session_save_path($this->getSessionSavePath());
                break;
            case 'memcached':
                ini_set('session.save_handler', 'memcached');
                session_save_path($this->getSessionSavePath());
                break;
            case 'eaccelerator':
                ini_set('session.save_handler', 'eaccelerator');
                break;
            default:
                session_module_name($this->getSessionSaveMethod());
                if (is_writable($this->getSessionSavePath())) {
                    session_save_path($this->getSessionSavePath());
                }
                break;
        }

决定了存储会话数据的介质。在Magento安装的时候可以选择的是文件和数据库。第一种情况存储到数据库,那么就会使用到会话的资源模型(因为和数据库交互),如果是保存到文件中,那么路径可写是必须检查的。

我们这里看到很多的Session类实现,实际上,只是说明模块需要保留数据到会话而已,每个类继承过来的init方法的第一个参数是一个所谓的名空间,实际上是$_SESSION数组的一个下标,比如core对应$_SESSION[‘core’]=array(), customer对应$_SESSION[‘customer’]=array()。

实际上这里的封装也不复杂。关于SESSION的内容可以参考http://blog.ifeeline.com/300.html。

这里探讨一下存储到共享内存的方法。处了保存到数据库和文件,Magento支持把SESSION数据通过memeche和memcached和eaccelerator保存到共享内存中。这三个都是PHP的扩展库。需要安装才能用。

在Magento的app/etc/local.xml中添加如此代码:

<global>
<!— 保存到文件 -->
<session_save><![CDATA[files]]></session_save>

		<!—通过memcached扩展把数据保存到memcached服务端 -->
        <session_save><![CDATA[memcached]]></session_save>
        <session_save_path><![CDATA[127.0.0.1:11212]]></session_save_path>
</global>

注意这里的memcached不是memcache(没有d)。

可以参考:http://www.php.net/manual/zh/memcached.sessions.php。这里给出了一个最佳实践,SESSION使用的缓存最好不要和其它的缓存共享。

针对memcached的配置,还有一个可以配置的参数是session_save_limiter,如果不理解这个最好不要设置,默认是no-cache,用来控制页面的缓存,参考http://www.php.net/manual/zh/function.session-cache-limiter.php。

这样我们在服务器上开启memcached服务:

/usr/local/bin/memcached -d -m 32 -u root -l 127.0.0.1 -p 11212

Magento共享内存数据查看

可见,数据已经保存到memcached共享内存中。在共享内存中保存SESSION有一个问题,当访问量比较大,可能用完共享空间,共享空间用完会产生什么情况呢,剔除最老的SESSION还是拒绝存入都会产生问题,另外如果多个应用都保存到了内存空间,Key的前缀都是memc.sess.key,后面的串看起来总不能保存唯一(应该有产生碰撞的可能)。这个问题的解决是使用快慢存储,比如在保存到Memcached中的同时也保存到数据库中,写入时监控共享内存利用率,如果超过伐值,从共享内存中删除这个会话,这时可能释放了共享内存,只保存到了数据库,而取数据时,先到共享内存取,如果没有命中则到数据库取,从而解决了以上的问题。

一般,为了有效共享内存,会话存在时间不应该设置过长甚至设置成永不过期(时间设置得很大),常见的时间应该是低于30分钟(1800秒,Magento默认是3600),SESSION的过期时间就是根据当前时间加上这个生存时间设置的,每次把会话内存读出来,都会自动写回去,这个主要是为了更新时间戳,写入共享内存的也是如此,共享内存的每块数据都有时间戳,超时就会自动从内存剔除。

以下看看生存时间的代码:

    public function getLifeTime()
    {
        if (is_null($this->_lifeTime)) {
            $configNode = Mage::app()->getStore()->isAdmin() ?
                    'admin/security/session_cookie_lifetime' : 'web/cookie/cookie_lifetime';
            $this->_lifeTime = (int) Mage::getStoreConfig($configNode);

            if ($this->_lifeTime < 60) {
                $this->_lifeTime = ini_get('session.gc_maxlifetime');
            }

            if ($this->_lifeTime < 60) {
                $this->_lifeTime = 3600; //one hour
            }

            if ($this->_lifeTime > self::SEESION_MAX_COOKIE_LIFETIME) {
                $this->_lifeTime = self::SEESION_MAX_COOKIE_LIFETIME; // 100 years
            }
        }
        return $this->_lifeTime;
    }

前台的从web/cookie/cookie_lifetime取出值,看起来是设置cookie的时间,实际上,保存会话ID的这个cookie生存时间,应该是和会话的生存时间一样长的。不过,在其它应用中,可能不这样设置,保存会话ID的cookie生存时间可能会设置为0,这个表示这个cookie在浏览器关闭后马上过期(就是浏览器关闭的同时删除这个cookie),可见,虽然服务器端的会话内容还没有过期,但是这个数据已经作废了。重新打开浏览器访问,服务器只能给它一个新的会话(因为你没有把SID传过来,所以不知道你之前状态),这样服务器端就会产生很多作废数据。如果保存会话ID的这个cookie跟会话生存时间一样长,就不会因为浏览器关闭重新打开时重新开启一个会话,不过用户把浏览器关闭了很大情况可能时不想再浏览了,另外关闭浏览器cookie不过期还有一个问题时,如果另一个人在你的电脑上用同一浏览器打开了相同的站点,你的信息可能被暴露,所以我们通常会被教育,要记得登出。

永久链接:http://blog.ifeeline.com/732.html
原创文章,转载务必保留出处。

Zen-cart中SESSION的配置与封装

关于SESSION的配置,对应后台Configuration -> Sessions:
Zen-cart会话配置

相关配置对应的常量定义如下:
Zen-cart会话配置对应的常量定义

除了Cookie Domain对应SESSION_USE_FQDN外,其它都是见名知意的。如下:

SESSION_WRITE_DIRECTORY          当使用文件来存储会话内容时,它用来控制存储的目录,不过再150以后的版本只能使用数据库来存储了,这个参数无用了
SESSION_USE_FQDN                 这个是设置会话cookie的域名,它可以控制添加的cookie是否是FQDN,其实如果域名是访问网址是www.ifeeline.com,这个变量设置为Ture时就是www.ifeeline.com,是False是就是ifeeline.com
SESSION_FORCE_COOKIE_USE         是否强制使用cookie来传递会话ID
SESSION_CHECK_SSL_SESSION_ID     是否检查SSL会话ID
SESSION_CHECK_USER_AGENT         是否检查用户浏览器前后是否一致
SESSION_CHECK_IP_ADDRESS         是否检查用户IP前后是否一致
SESSION_BLOCK_SPIDERS            是否阻止机器人会话(如果强制使用cookie,则此设置没有使用)
SESSION_RECREATE                 是否更换会话ID(比如登录后更换ID)
SESSION_IP_TO_HOST_ADDRESS       是否把客户端的IP转换成名字(将发起DNS查询,建议不要开启)
SESSION_USE_ROOT_COOKIE_PATH     是否使用跟作为会话cookie的路径参数(默认为False,将根据程序实际情况自己决定)
SESSION_ADD_PERIOD_PREFIX        是否添加域名前缀(点号)到会话cookie的域名设置参数

关于SESSION_USE_FQDN设置,主要初始化文件在includes/init_includes/init_tlds.php中:

// 主要代码,zen_get_top_level_domain()函数受到SESSION_USE_FQDN设置影响
$http_domain = zen_get_top_level_domain(HTTP_SERVER);
$https_domain = zen_get_top_level_domain(HTTPS_SERVER);
$cookieDomain = $current_domain = (($request_type == 'NONSSL') ? $http_domain : $https_domain);

测试zen_get_top_level_domain()函数,假如当前网址是www.ifeeline.com,当SESSION_USE_FQDN是True时,输出是www.ifeeline.com,当SESSION_USE_FQDN是False时,输入ifeeline.com。这个设置主要影响到会话cookie的域设置,如果SESSION_USE_FQDN为Flase时,带www的网址www将被去掉:
会话域设置

关于SESSION_RECREATE设置,主要在登录和退出登录时用到。

其它的设置主要在includes/init_includes/init_sessions.php中,以下为代码逻辑(删除了部分代码,添加了详细注释)

require(DIR_WS_FUNCTIONS . 'sessions.php');
// 设置会话名称
zen_session_name('zenid');
// 对应后台Session Directory 设置保存路径,不过只是使用文件保存会话内容时 不过1.5.0以后只采用数据库保存
zen_session_save_path(SESSION_WRITE_DIRECTORY);

// 准备设置会话cookie的参数 前台所有页面都是通过访问index.php展示的,所有$_SERVER['SCRIPT_NAME']永远都是/index.php, 如果网站是http://sample.com/zcc,那么就是/zcc/index.php
$path = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
// 对应后台的 Use root path for cookie path,默认是False的,比如设置了访问网站是http://sample.com/zcc,那么将使用/zcc作为会话cookie的路径
if (defined('SESSION_USE_ROOT_COOKIE_PATH') && SESSION_USE_ROOT_COOKIE_PATH  == 'True') $path = '/';
$path = (defined('CUSTOM_COOKIE_PATH')) ? CUSTOM_COOKIE_PATH : $path;
// 对应后台的 Add period prefix to cookie domain
$domainPrefix = (!defined('SESSION_ADD_PERIOD_PREFIX') || SESSION_ADD_PERIOD_PREFIX == 'True') ? '.' : '';
// 这句代码是150版本以后新加 目的是当网站全站使用SSL时,设置cookie也只使用SSL来发送
$secureFlag = ((ENABLE_SSL == 'true' && substr(HTTP_SERVER, 0, 6) == 'https:' && substr(HTTPS_SERVER, 0, 6) == 'https:') || (ENABLE_SSL == 'false' && substr(HTTP_SERVER, 0, 6) == 'https:')) ? TRUE : FALSE;
// $cookieDomain在includes/init_includes/init_tlds.php中
if (PHP_VERSION >= '5.2.0') {
  	session_set_cookie_params(0, $path, (zen_not_null($cookieDomain) ? $domainPrefix . $cookieDomain : ''), $secureFlag, TRUE);
} else {
  	session_set_cookie_params(0, $path, (zen_not_null($cookieDomain) ? $domainPrefix . $cookieDomain : ''), $secureFlag);
}

if (isset($_POST[zen_session_name()])) {
  	zen_session_id($_POST[zen_session_name()]);
} elseif ( ($request_type == 'SSL') && isset($_GET[zen_session_name()]) ) {
  	zen_session_id($_GET[zen_session_name()]);
}

$ipAddressArray = explode(',', $_SERVER['REMOTE_ADDR']);
$ipAddress = (sizeof($ipAddressArray) > 0) ? $ipAddressArray[0] : '';
$_SERVER['REMOTE_ADDR'] = $ipAddress;

// 这里是启用会话的逻辑 
$session_started = false;
// 首先这里对应后台 Force Cookie use, 如果为True,那么先设置一个cookie,如果检测到有内容回发说明支持cookie。首先,第一次访问不会启用会话,第二,所有不支持cookie的都不启用会话,那么如果是爬行蜘蛛,则不会发送cookie,说明也起到了阻止机器人SESSION
if (SESSION_FORCE_COOKIE_USE == 'True') {
  	zen_setcookie('cookie_test', 'please_accept_for_session', time()+60*60*24*30, '/', (zen_not_null($current_domain) ? $current_domain : ''));

  	if (isset($_COOKIE['cookie_test'])) {
    	zen_session_start();
    	$session_started = true;
  	}
// 这里对应后台的 Prevent Spider Sessions,如果强制使用cookie,这里就没有意义(因为如果强制使用cookie,那么也就意味着阻止机器人会话了),当没有强制使用cookie来开启会话时,特别阻止机器人SESSION是有意义的。
} elseif (SESSION_BLOCK_SPIDERS == 'True') {
} else {
  	zen_session_start();
  	$session_started = true;
}
unset($spiders);
// 这个对应后台的 IP to Host Conversion Status  这个是根据IP反查主机地址 一般没有必要启用
if (!isset($_SESSION['customers_host_address'])) {
  if (SESSION_IP_TO_HOST_ADDRESS == 'true') {
    $_SESSION['customers_host_address']= @gethostbyaddr($_SERVER['REMOTE_ADDR']);
  } else {
    $_SESSION['customers_host_address'] = OFFICE_IP_TO_HOST_ADDRESS;
  }
}
// 这个对应后台的 Check SSL Session ID  如果请求类型是SSL,设置是否效验SSL_SESSION_ID,这个对应防止SSL挟持有一定作用
if ( ($request_type == 'SSL') && (SESSION_CHECK_SSL_SESSION_ID == 'True') && (ENABLE_SSL == 'true') && ($session_started == true) ) {
  	$ssl_session_id = $_SERVER['SSL_SESSION_ID'];
  	if (!$_SESSION['SSL_SESSION_ID']) {
    	$_SESSION['SSL_SESSION_ID'] = $ssl_session_id;
  	}
  	if ($_SESSION['SSL_SESSION_ID'] != $ssl_session_id) {
    	zen_session_destroy();
    	zen_redirect(zen_href_link(FILENAME_SSL_CHECK));
  	}
}
// 这个对应后台的 Check User Agent 效验用户代理是否一致,防止会话挟持,一般应该开启
if (SESSION_CHECK_USER_AGENT == 'True') {
}
// 这个对应后台的 Check IP Address 效验用户的访问IP是否以一致,一般不应该开启,现在的很多访问是同一个会话,但是来源IP可能不一样,因为用户可能从一个代理进来
if (SESSION_CHECK_IP_ADDRESS == 'True') {
}

另外一个对SESSION的封装主要在includes/functions/sessions.php文件:

  // 针对前后台设置会话有效时间,这个参数主要影用来设置保存在数据库中的会话过期时间
  if (IS_ADMIN_FLAG === true) {
    if (!$SESS_LIFE = (SESSION_TIMEOUT_ADMIN > 900 ? 900 : SESSION_TIMEOUT_ADMIN)) {
      $SESS_LIFE = (SESSION_TIMEOUT_ADMIN > 900 ? 900 : SESSION_TIMEOUT_ADMIN);
    }
  } else {
    if (!$SESS_LIFE = get_cfg_var('session.gc_maxlifetime')) {
      $SESS_LIFE = 1440;
    }
  }
  
  // 使用session_set_save_handler()函数修改会话内容保存的介质,zen-cart150以后,用户不能选择保存到文件,只能是保存到数据库
  session_set_save_handler('_sess_open', '_sess_close', '_sess_read', '_sess_write', '_sess_destroy', '_sess_gc');
  
  // 会话开始的封装,这里检查了会话ID是否是合法值,伪造的可能非法,这个检查非常有必要,另外设置一个会话安全码给securityToken,这样表单中提交的数据如果没有这个值,或者值对应不上,请求就会被终止,这个设置有效拒绝了来自第三方提交的表单数据。
  function zen_session_start() {
    @ini_set('session.gc_probability', 1);
    @ini_set('session.gc_divisor', 2);
    if (IS_ADMIN_FLAG === true) {
      @ini_set('session.gc_maxlifetime', (SESSION_TIMEOUT_ADMIN > 900 ? 900 : SESSION_TIMEOUT_ADMIN));
    }
    if (preg_replace('/[a-zA-Z0-9]/', '', session_id()) != '')
    {
      zen_session_id(md5(uniqid(rand(), true)));
    }
    $temp = session_start();
    if (!isset($_SESSION['securityToken'])) {
      $_SESSION['securityToken'] = md5(uniqid(rand(), true));
    }
    return $temp;
  }

  // 以下是对session_id()函数的封装,如果提过了SESSION ID(比如zen_session_start()函数会用到此情况),那么就要检查这个值是合法的
  function zen_session_id($sessid = '') {
    if (!empty($sessid)) {
      $tempSessid = $sessid;
  	  if (preg_replace('/[a-zA-Z0-9]/', '', $tempSessid) != '')
  	  {
  	    $sessid = md5(uniqid(rand(), true));
  	  }
      return session_id($sessid);
    } else {
      return session_id();
    }
  }

原创文章,转载务必保留出处。
http://blog.ifeeline.com/310.html