1<?php
2/**
3 * CDbStatePersister class file.
4 *
5 * @author Qiang Xue <qiang.xue@gmail.com>
6 * @link http://www.yiiframework.com/
7 * @copyright 2008-2013 Yii Software LLC
8 * @license http://www.yiiframework.com/license/
9 * @package system.base
10 * @since 1.1.17
11 */
12
13/**
14 * CDbStatePersister implements a database persistent data storage.
15 *
16 * It can be used to keep data available through multiple requests and sessions.
17 *
18 * By default, CDbStatePersister stores data in a table named 'state'.
19 * You may change the location by setting the {@link stateTableName} property.
20 *
21 * To retrieve the data from CDbStatePersister, call {@link load()}. To save the data,
22 * call {@link save()}.
23 *
24 * Comparison among state persister, session and cache is as follows:
25 * <ul>
26 * <li>session: data persisting within a single user session.</li>
27 * <li>state persister: data persisting through all requests/sessions (e.g. hit counter).</li>
28 * <li>cache: volatile and fast storage. It may be used as storage medium for session or state persister.</li>
29 * </ul>
30 *
31 * @package system.base
32 * @since 1.1.17
33 */
34class CDbStatePersister extends CApplicationComponent implements IStatePersister
35{
36	/**
37	 * @var string the database table name storing the state data. Make sure the table
38	 * exists or database user is granted to CREATE tables.
39	 */
40	public $stateTableName='state';
41	/**
42	 * @var string connection ID
43	 */
44	public $dbComponent='db';
45	/**
46	 * @var CDbConnection instance
47	 */
48	public $db;
49	/**
50	 * @var string Column name for value-field
51	 */
52	public $valueField='value';
53	/**
54	 * @var string Column name for key-field
55	 */
56	public $keyField='key';
57
58
59	/**
60	 * Initializes the component.
61	 * This method overrides the parent implementation by making sure {@link stateFile}
62	 * contains valid value.
63	 */
64	public function init()
65	{
66		parent::init();
67		if($this->stateTableName===null)
68			throw new CException(Yii::t('yii', 'stateTableName param cannot be null.'));
69		$this->db=Yii::app()->getComponent($this->dbComponent);
70		if($this->db===null)
71			throw new CException(Yii::t('yii', '\'{db}\' component doesn\'t exist.',array(
72				'{db}'=>$this->dbComponent
73			)));
74		if(!($this->db instanceof CDbConnection))
75			throw new CException(Yii::t ('yii', '\'{db}\' component is not a valid CDbConnection instance.',array(
76				'{db}'=>$this->dbComponent
77			)));
78		if($this->db->schema->getTable($this->stateTableName,true)===null)
79			$this->createTable();
80	}
81
82	/**
83	 * Loads state data from persistent storage.
84	 * @return mixed state data. Null if no state data available.
85	 */
86	public function load()
87	{
88		$command=$this->db->createCommand();
89		$command=$command->select($this->valueField)->from($this->stateTableName);
90		$command=$command->where($this->db->quoteColumnName($this->keyField).'=:key',array(
91			':key'=>Yii::app()->name
92		));
93		$state=$command->queryScalar();
94		if(false!==$state)
95			return unserialize($state);
96		else
97			return null;
98	}
99
100	/**
101	 * Saves application state in persistent storage.
102	 * @param mixed $state state data (must be serializable).
103	 * @return int
104	 */
105	public function save($state)
106	{
107		$command=$this->db->createCommand();
108		if(false===$this->exists())
109			return $command->insert($this->stateTableName,array(
110				$this->keyField=>Yii::app()->name,
111				$this->valueField=>serialize($state)
112			));
113		else
114			return $command->update($this->stateTableName,array($this->valueField=>serialize($state)),
115				$this->db->quoteColumnName($this->keyField).'=:key',
116				array(':key'=>Yii::app()->name)
117		);
118	}
119
120	/**
121	 * @return mixed
122	 */
123	public function exists()
124	{
125		$command=$this->db->createCommand();
126		$command=$command->select($this->keyField)->from($this->stateTableName);
127		$command=$command->where($this->db->quoteColumnName($this->keyField).'=:key',array(
128			':key'=>Yii::app()->name
129		));
130		return $command->queryScalar();
131	}
132
133	/**
134	 * Creates state persister table
135	 * @throws CException
136	 */
137	protected function createTable()
138	{
139		try
140		{
141			$command=$this->db->createCommand();
142			$command->createTable($this->stateTableName,array(
143				$this->keyField=>'string NOT NULL',
144				$this->valueField=>'text NOT NULL',
145				'PRIMARY KEY ('.$this->db->quoteColumnName($this->keyField).')'
146			));
147		}
148		catch (CDbException $e)
149		{
150			throw new CException(Yii::t('yii','Can\'t create state persister table. Check CREATE privilege for \'{db}\' connection user or create table manually with SQL: {sql}.',array('{db}'=>$this->dbComponent,'{sql}'=>$command->text ) ) );
151		}
152	}
153}
154