Gentics Portal.Node PHP API
 All Classes Namespaces Functions Variables Pages
DbConnectionMan.php
1 <?php
2 /**
3  * Gentics Portal.Node PHP
4  * Author & Copyright (c) by Gentics Software GmbH
5  * sales@gentics.com
6  * http://www.gentics.com
7  * Licenses can be found in the LICENSE.txt file in the root-folder of this installation
8  * You must not use this software without a valid license agreement.
9  *
10  * DbConnectionMan(Database Connection Manager) class is a manager of database connections.
11  * for the purpose of database read/write splitting.
12  * It override the createCommand method,
13  * detect the sql statement to decide which connection will be used.
14  * Default it use the master connection.
15  *
16  */
17 class DbConnectionMan extends CDbConnection
18 {
19 
20  /**
21  * @var array $slaves.Slave database connection(Read) config array.
22  * The array value's format is the same as CDbConnection.
23  * @example
24  * 'components'=>array(
25  * 'db'=>array(
26  * 'connectionString'=>'mysql://<master>',
27  * 'slaves'=>array(
28  * array('connectionString'=>'mysql://<slave01>'),
29  * array('connectionString'=>'mysql://<slave02>'),
30  * )
31  * )
32  * )
33  *
34  */
35  public $slaves = array();
36 
37 
38  /**
39  * Whether enable the slave database connection.
40  * Defaut is true.Set this property to false for the purpose of only use the master database.
41  * @var bool $enableSlave
42  */
43  public $enableSlave = true;
44 
45  /**
46  * @var DbConnectionMan
47  */
48  private $_slave;
49 
50 
51  public function init()
52  {
53  parent::init();
54  //this connection also can be slave
55  $this->slaves[] = $this;
56  }
57 
58 
59  /**
60  * Creates a CDbCommand object for excuting sql statement.
61  * It will detect the sql statement's behavior.
62  * While the sql is a simple read operation.
63  * It will use a slave database connection to contruct a CDbCommand object.
64  * Default it use current connection(master database).
65  *
66  * @param string $sql query
67  *
68  * @return CDbCommand
69  */
70  public function createCommand($sql = null)
71  {
72  if ($this->enableSlave && !$this->getCurrentTransaction() && self::isReadOperation($sql)) {
73  return $this->getSlave()->createCommand($sql);
74  } else {
75  return parent::createCommand($sql);
76  }
77  }
78 
79 
80  /**
81  * Construct a slave connection CDbConnection for read operation.
82  *
83  * @return DbConnectionMan
84  */
85  public function getSlave()
86  {
87  if (!isset($this->_slave)) {
88  $slaves = $this->slaves;
89  //Add randomness to slaves
90  shuffle($slaves);
91  //create first available slave
92  foreach ($slaves as $slaveConfig) {
93  try {
94  if ($slaveConfig instanceof DbConnectionMan) {
95  $slave = $slaveConfig;
96  } elseif (!isset($slaveConfig['class'])) {
97  //cloning for copying parent properties
98  $slave = clone $this;
99  foreach ($slaveConfig as $prop => $val) {
100  $slave->$prop = $val;
101  }
102  } else {
103  $slave = Yii::createComponent($slaveConfig);
104  }
105  Yii::app()->setComponent('dbslave', $slave);
106  $this->_slave = $slave;
107  Yii::log('Slave connection: ' . $this->_slave->connectionString, CLogger::LEVEL_TRACE, 'system.db.CDbCommand');
108  break;
109  } catch (Exception $e) {
110  Yii::log('Create slave database connection failed!', CLogger::LEVEL_WARNING);
111  continue;
112  }
113  }
114  //if slave not created use this connection as slave
115  if (!$this->_slave) {
116  $this->_slave = clone $this;
117  }
118  $this->_slave->enableSlave = false;
119  }
120  return $this->_slave;
121  }
122 
123  /**
124  * Detect whether the sql statement is just a simple read operation.
125  * Read Operation means this sql will not change any thing ang aspect of the database.
126  * Such as SELECT,DECRIBE,SHOW etc.
127  * On the other hand:UPDATE,INSERT,DELETE is write operation.
128  *
129  * @param string $sql query
130  *
131  * @return bool
132  */
133  public function isReadOperation($sql)
134  {
135  return preg_match('/^\s*(SELECT|SHOW|DESCRIBE|PRAGMA)/i', $sql);
136  }
137 
138 }