Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Sharding / PoolingShardConnection.php
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19
20 namespace Doctrine\DBAL\Sharding;
21
22 use Doctrine\DBAL\Connection;
23 use Doctrine\DBAL\Event\ConnectionEventArgs;
24 use Doctrine\DBAL\Events;
25 use Doctrine\DBAL\Driver;
26 use Doctrine\DBAL\Configuration;
27
28 use Doctrine\Common\EventManager;
29
30 use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser;
31
32 /**
33  * Sharding implementation that pools many different connections
34  * internally and serves data from the currently active connection.
35  *
36  * The internals of this class are:
37  *
38  * - All sharding clients are specified and given a shard-id during
39  *   configuration.
40  * - By default, the global shard is selected. If no global shard is configured
41  *   an exception is thrown on access.
42  * - Selecting a shard by distribution value delegates the mapping
43  *   "distributionValue" => "client" to the ShardChooser interface.
44  * - An exception is thrown if trying to switch shards during an open
45  *   transaction.
46  *
47  * Instantiation through the DriverManager looks like:
48  *
49  * @example
50  *
51  * $conn = DriverManager::getConnection(array(
52  *    'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection',
53  *    'driver' => 'pdo_mysql',
54  *    'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
55  *    'shards' => array(
56  *        array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
57  *        array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
58  *    ),
59  *    'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser',
60  * ));
61  * $shardManager = $conn->getShardManager();
62  * $shardManager->selectGlobal();
63  * $shardManager->selectShard($value);
64  *
65  * @author Benjamin Eberlei <kontakt@beberlei.de>
66  */
67 class PoolingShardConnection extends Connection
68 {
69     /**
70      * @var array
71      */
72     private $activeConnections;
73
74     /**
75      * @var int
76      */
77     private $activeShardId;
78
79     /**
80      * @var array
81      */
82     private $connections;
83
84     /**
85      * @var ShardManager
86      */
87     private $shardManager;
88
89     public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null)
90     {
91         if ( !isset($params['global']) || !isset($params['shards'])) {
92             throw new \InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations.");
93         }
94
95         if ( !isset($params['shardChoser'])) {
96             throw new \InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'");
97         }
98
99         if (is_string($params['shardChoser'])) {
100             $params['shardChoser'] = new $params['shardChoser'];
101         }
102
103         if ( ! ($params['shardChoser'] instanceof ShardChoser)) {
104             throw new \InvalidArgumentException("The 'shardChoser' configuration is not a valid instance of Doctrine\DBAL\Sharding\ShardChoser\ShardChoser"); 
105         }
106
107         $this->connections[0] = array_merge($params, $params['global']);
108
109         foreach ($params['shards'] as $shard) {
110             if ( ! isset($shard['id'])) {
111                 throw new \InvalidArgumentException("Missing 'id' for one configured shard. Please specificy a unique shard-id.");
112             }
113
114             if ( !is_numeric($shard['id']) || $shard['id'] < 1) {
115                 throw new \InvalidArgumentException("Shard Id has to be a non-negative number.");
116             }
117
118             if (isset($this->connections[$shard['id']])) {
119                 throw new \InvalidArgumentException("Shard " . $shard['id'] . " is duplicated in the configuration.");
120             }
121
122             $this->connections[$shard['id']] = array_merge($params, $shard);
123         }
124
125         parent::__construct($params, $driver, $config, $eventManager);
126     }
127
128     /**
129      * Connect to a given shard
130      *
131      * @param mixed $shardId
132      * @return bool
133      */
134     public function connect($shardId = null)
135     {
136         if ($shardId === null && $this->_conn) {
137             return false;
138         }
139
140         if ($shardId !== null && $shardId === $this->activeShardId) {
141             return false;
142         }
143
144         if ($this->getTransactionNestingLevel() > 0) {
145             throw new ShardingException("Cannot switch shard when transaction is active.");
146         }
147
148         $this->activeShardId = (int)$shardId;
149
150         if (isset($this->activeConnections[$this->activeShardId])) {
151             $this->_conn = $this->activeConnections[$this->activeShardId];
152             return false;
153         }
154
155         $this->_conn = $this->activeConnections[$this->activeShardId] = $this->connectTo($this->activeShardId);
156
157         if ($this->_eventManager->hasListeners(Events::postConnect)) {
158             $eventArgs = new Event\ConnectionEventArgs($this);
159             $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
160         }
161
162         return true;
163     }
164
165
166     /**
167      * Connect to a specific connection
168      *
169      * @param  string $shardId
170      * @return Driver
171      */
172     protected function connectTo($shardId)
173     {
174         $params = $this->getParams();
175
176         $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array();
177
178         $connectionParams = $this->connections[$shardId];
179
180         $user = isset($connectionParams['user']) ? $connectionParams['user'] : null;
181         $password = isset($connectionParams['password']) ? $connectionParams['password'] : null;
182
183         return $this->_driver->connect($connectionParams, $user, $password, $driverOptions);
184     }
185
186     public function isConnected($shardId = null)
187     {
188         if ($shardId === null) {
189             return $this->_conn !== null;
190         }
191
192         return isset($this->activeConnections[$shardId]);
193     }
194
195     public function close()
196     {
197         $this->_conn             = null;
198         $this->activeConnections = null;
199     }
200 }
201