Session

这章将介绍Symfony2框架中Session的用法。

关于Session

Session相当于是服务器端的Cookie。它是在服务器端存储一些数据,然后将索引的键值作为Cookie值发送给浏览器。

例如打开页面 http://127.0.0.1:8000/ ,可以看到浏览器有条Cookie记录: PHPSESSID=e42c1bbbf2f709ef8cc7a360a1f3b5de; path=/; domain=127.0.0.1。其中Cookie名为PHPSESSID,值为e42c1bbbf2f709ef8cc7a360a1f3b5de。当浏览器发起请求时,服务器端的程序会根据这个值来查找对应的Session。默认是存储在/tmp/sess_e42c1bbbf2f709ef8cc7a360a1f3b5de文件中。我们看看该文件的内容:

  1. $ cat /tmp/sess_e42c1bbbf2f709ef8cc7a360a1f3b5de
  2. _sf2_attributes|a:2:{s:13:"_csrf/contact";s:43:"HMdTj7H9KrbD2vXslm5H-3rYyWMENM5ZpPBUhWjn5vM";s:32:"_csrf/blogger_blogbundle_comment";s:43:"76koHF_0lGJFFfZTuhOdW4ocjsTlEOLOPDJ0nv97_ZU";}_sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:"u";i:1413120687;s:1:"c";i:1413084843;s:1:"l";s:1:"0";}

它是一个经过了序列化的字符串。

上面说到的Cookie名PHPSESSID和Session的存放位置/tmp都是在php中进行配置的。查看php的配置信息:

  1. $ php -i | grep session
  2. session
  3. session.auto_start => Off => Off
  4. session.cache_expire => 180 => 180
  5. session.cache_limiter => nocache => nocache
  6. session.cookie_domain => no value => no value
  7. session.cookie_httponly => Off => Off
  8. session.cookie_lifetime => 0 => 0
  9. session.cookie_path => / => /
  10. session.cookie_secure => Off => Off
  11. session.entropy_file => /dev/urandom => /dev/urandom
  12. session.entropy_length => 32 => 32
  13. session.gc_divisor => 100 => 100
  14. session.gc_maxlifetime => 1440 => 1440
  15. session.gc_probability => 1 => 1
  16. session.hash_bits_per_character => 4 => 4
  17. session.hash_function => 0 => 0
  18. session.name => PHPSESSID => PHPSESSID
  19. session.referer_check => no value => no value
  20. session.save_handler => files => files
  21. session.save_path => no value => no value
  22. session.serialize_handler => php => php
  23. session.upload_progress.cleanup => On => On
  24. session.upload_progress.enabled => On => On
  25. session.upload_progress.freq => 1% => 1%
  26. session.upload_progress.min_freq => 1 => 1
  27. session.upload_progress.name => PHP_SESSION_UPLOAD_PROGRESS => PHP_SESSION_UPLOAD_PROGRESS
  28. session.upload_progress.prefix => upload_progress_ => upload_progress_
  29. session.use_cookies => On => On
  30. session.use_only_cookies => On => On
  31. session.use_strict_mode => Off => Off
  32. session.use_trans_sid => 0 => 0

上面的PHPSESSID这条Cookie是由框架自动设置的。现在来说说我们如何设置一条新的Cookie。根据http协议的说明,设置Cookie即是在http的响应头部添加一条Set-Cookie指令。

例如现在我们想要为浏览器设置一条名为cookie1值为123456的Cookie记录。

  1. use Symfony\Component\HttpFoundation\Cookie;
  2. $response->headers->setCookie(new Cookie('cookie1', '123456'));

这里的$response是一个Symfony\Component\HttpFoundation\Response类型的对象,它应该是在控制器中创建的。

然后如果还想要把刚刚这条Cookie删除掉,那么操作如下操作:

  1. $response->headers->clearCookie('test1');

Session的用法

在上面也看到了,Symfony2框架的Session就是将一个PHP数据进行序列化保存的。现在来介绍一下Session的数据设置和读取。

Session相当于一个php数组,直接通过getset方法进行读取和写入。例如:

  1. $session = $request->getSession();
  2. $session->set('key', 'value');
  3. $session->get('key');

这里先获取session对象,在控制器中可以直接通过$request对象来获取。也可以通过服务容器container来获取:

  1. $session = $container->get('session');

其中$container是一个Symfony\Component\DependencyInjection\ContainerInterface对象。这个在下一章再作介绍。

配置Session

上面说过了Cookie名默认为PHPSESSID,Session默认是保存在/tmp目录下。如果网站同时运行了多个PHP程序,那么Cookie名就会冲突。而且/tmp目录下的Session文件也不会自动删除的,这个会导致目录下的文件会越来越多。

针对这两个问题,我们需要修改Symfony2的Session配置参数。编辑文件app/config/config.yml,设置framework -> session -> name参数,修改Cookie名:

  1. framework:
  2. session:
  3. name: sfblog

然后再修改Session的存储方式。Symfony2框架默认提供几种存储后端,保存到SQL数据库、Memcache、MongoDb等。这里我们选择使用SQL数据库。

设置framework -> session -> handler_id参数为session.handler.pdo,然后再注册一个session.handler.pdo服务。最后的配置如下:

  1. framework:
  2. session:
  3. name: sfblog
  4. handler_id: session.handler.pdo
  5. services:
  6. pdo:
  7. class: PDO
  8. arguments:
  9. - "mysql:host=%database_host%;port=%database_port%;dbname=%database_name%"
  10. - "%database_user%"
  11. - "%database_password%"
  12. calls:
  13. - [setAttribute, [3, 2]] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION
  14. session.handler.pdo:
  15. class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
  16. arguments: ["@pdo", { db_table: session, db_id_col: session_id, db_data_col: session_value, db_time_col: session_time } ]

这里的pdo服务用于配置数据库,同样的对于开发环境我们使用SQLite数据库。编辑文件app/config/config_dev.yml,再进行一次配置:

  1. services:
  2. pdo:
  3. class: PDO
  4. arguments:
  5. - "sqlite:%kernel.root_dir%/data/data.db3"
  6. calls:
  7. - [setAttribute, [3, 2]] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION
  8. session.handler.pdo:
  9. class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
  10. arguments: ["@pdo", { db_table: session, db_id_col: session_id, db_data_col: session_value, db_time_col: session_time } ]

上面的配置表明了Session将会保存在数据库的session表中。因此我们还得再新建一个数据表,结构如下:

  1. CREATE TABLE `session` (
  2. `session_id` varchar(255) NOT NULL,
  3. `session_value` text NOT NULL,
  4. `session_time` int(11) NOT NULL,
  5. PRIMARY KEY (`session_id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

不过为了方便,最好还是再新建一个Session实体。通过Doctrine的实体来自动创建表结构。

  1. $ php app/console doctrine:generate:entity --entity=BloggerBlogBundle:Session --format=annotation --fields="session_id:string(255) session_value:text session_time:datetime"

然后再对文件稍作修改。现在有新的实体了,需要更新数据库结构。可以执行如下命令:

  1. $ php app/console doctrine:schema:update --force

注意:上面的执行在更新数据库中可以会导致数据丢失。建议使用migrations工具。对于重要的数据可能还需要手动进行迁移。

composer.json中添加两条依赖:

  1. "doctrine/doctrine-migrations-bundle": "dev-master",
  2. "doctrine/migrations": "dev-master",

然后composer.phar update进行安装。并在AppKernel中进行注册$bundles[] = new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle();

接着生成迁移文件:

  1. $ php app/console doctrine:migrations:diff

进行迁移:

  1. $ php app/console doctrine:migrations:migrate