Connection::setOptions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 2 Features 0
Metric Value
c 7
b 2
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/**
3
 * Fwk
4
 *
5
 * Copyright (c) 2011-2012, Julien Ballestracci <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
17
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
21
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
 * POSSIBILITY OF SUCH DAMAGE.
23
 *
24
 * PHP Version 5.3
25
 * 
26
 * @category  Database
27
 * @package   Fwk\Db
28
 * @author    Julien Ballestracci <[email protected]>
29
 * @copyright 2011-2012 Julien Ballestracci <[email protected]>
30
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
31
 * @link      http://www.phpfwk.com
32
 */
33
namespace Fwk\Db;
34
35
use Doctrine\DBAL\Driver\PDOStatement;
36
use Fwk\Db\Events\AfterQueryEvent;
37
use Fwk\Db\Events\BeforeQueryEvent;
38
use Fwk\Db\Events\ConnectEvent;
39
use Fwk\Db\Events\ConnectionErrorEvent;
40
use Fwk\Db\Events\ConnectionStateChangeEvent;
41
use Fwk\Db\Events\DisconnectEvent;
42
use Fwk\Events\Dispatcher;
43
use Doctrine\DBAL\Connection as DbalConnection;
44
use Doctrine\DBAL\DriverManager;
45
46
/**
47
 * Represents a Connection to a database
48
 * 
49
 * @category Library
50
 * @package  Fwk\Db
51
 * @author   Julien Ballestracci <[email protected]>
52
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
53
 * @link     http://www.phpfwk.com
54
 */
55
class Connection extends Dispatcher
56
{
57
    /**
58
     * State when initialized
59
     */
60
    const STATE_INITIALIZED     = 0;
61
    
62
    /**
63
     * State when connected to SGDB
64
     */
65
    const STATE_CONNECTED       = 1;
66
    
67
    /**
68
     * State when disconnected from SGDB
69
     */
70
    const STATE_DISCONNECTED    = 2;
71
    
72
    /**
73
     * State when an exception has been thrown
74
     */
75
    const STATE_ERROR           = 3;
76
77
    /**
78
     * Connection options
79
     *
80
     * @var array
81
     */
82
    protected $options = array();
83
84
    /**
85
     * DBAL Connection object
86
     *
87
     * @var DbalConnection
88
     */
89
    protected $driver;
90
91
    /**
92
     * Schema object
93
     *
94
     * @var \Doctrine\DBAL\Schema\Schema
95
     */
96
    protected $schema;
97
98
    /**
99
     * Current state of the connection
100
     *
101
     * @var integer
102
     */
103
    protected $state = self::STATE_INITIALIZED;
104
105
    /**
106
     * Tables objects (cache)
107
     *
108
     * @var array
109
     */
110
    protected $tables;
111
112
    /**
113
     * Constructor with generic configuration parameters (array)
114
     * Options are used by Doctrine\DBAL\Connection, please refer to
115
     * documentation:
116
     * 
117
     * http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest
118
     * 
119
     * other options:
120
     *      - autoConnect:  (boolean) should connect on init (defaults to false)
121
     *
122
     * @param array $options Configuration options
123
     *
124
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
125
     */
126
    public function __construct(array $options = array())
127
    {
128
        $this->options      = $options;
129
130
        if (true === $this->get('autoConnect', false)) {
131
            $this->connect();
132
        }
133
    }
134
135
    /**
136
     * Establish connection to database
137
     *
138
     * @throws Exceptions\ConnectionErrorException when failing to connect
139
     * @return boolean
140
     */
141
    public function connect()
0 ignored issues
show
Coding Style introduced by
function connect() does not seem to conform to the naming convention (^(?:is|has|should|may|su...ster|unregister|exists)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
142
    {
143
        if (!$this->isConnected()) {
144
            try {
145
                $dbal = $this->getDriver();
146
                $dbal->connect();
147
            } catch (\Doctrine\DBAL\DBALException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
148
            } catch (\PDOException $e) {
149
                throw $this->setErrorException(
150
                    new Exceptions\ConnectionErrorException($e->getMessage())
151
                );
152
            }
153
            
154
            $this->setState(self::STATE_CONNECTED);
155
            $this->notify(new ConnectEvent($this));
156
        }
157
158
        return true;
159
    }
160
161
    /**
162
     * End connection to database
163
     *
164
     * @throws Exceptions\ConnectionErrorException when failing to disconnect (?)
165
     * @return boolean
166
     */
167
    public function disconnect()
0 ignored issues
show
Coding Style introduced by
function disconnect() does not seem to conform to the naming convention (^(?:is|has|should|may|su...ster|unregister|exists)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
168
    {
169
        if (!$this->isConnected()) {
170
            return true;
171
        }
172
173
        $dbal = $this->getDriver();
174
        $dbal->close();
175
176
        $this->setState(self::STATE_DISCONNECTED);
177
        $this->notify(new DisconnectEvent($this));
178
        
179
        return true;
180
    }
181
182
    /**
183
     * Sets an option value
184
     *
185
     * @param string $option Option's key
186
     * @param mixed  $value  Option value
187
     *
188
     * @return Connection
189
     */
190
    public function set($option, $value)
191
    {
192
        $this->options[$option] = $value;
193
194
        return $this;
195
    }
196
197
    /**
198
     * Returns an option value or $default if option is not defined.
199
     *
200
     * @param string $option  Option key
201
     * @param mixed  $default Option value
202
     *
203
     * @return mixed
204
     */
205
    public function get($option, $default = null)
206
    {
207
        return array_key_exists($option, $this->options) ?
208
                $this->options[$option] :
209
                $default;
210
    }
211
212
    /**
213
     * Returns all options
214
     *
215
     * @return array
216
     */
217
    public function getOptions()
218
    {
219
        return $this->options;
220
    }
221
222
    /**
223
     * Sets (merge) multiple options values
224
     *
225
     * @param array $options List of options (keys->values)
226
     * 
227
     * @return Connection
228
     */
229
    public function setOptions(array $options = array())
230
    {
231
        $this->options = array_merge($this->options, $options);
232
233
        return $this;
234
    }
235
236
    /**
237
     * Returns the DBAL instance for this connection
238
     *
239
     * @return DbalConnection
0 ignored issues
show
Documentation introduced by
Should the return type not be DbalConnection|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
240
     */
241
    public function getDriver()
242
    {
243
        if (!isset($this->driver)) {
244
            $this->setDriver(DriverManager::getConnection($this->options));
245
        }
246
247
        return $this->driver;
248
    }
249
250
    /**
251
     * Defines a driver
252
     *
253
     * @param DbalConnection $driver The DBAL Connection object
254
     * 
255
     * @return Connection
256
     */
257
    public function setDriver(DbalConnection $driver)
258
    {
259
        $this->driver = $driver;
260
        
261
        return $this;
262
    }
263
264
    /**
265
     * Returns current database schema
266
     * 
267
     * @return \Doctrine\DBAL\Schema\Schema
268
     */
269
    public function getSchema()
270
    {
271
        if (!isset($this->schema)) {
272
            $this->connect();
273
            $this->schema = $this->getDriver()
274
                ->getSchemaManager()
275
                ->createSchema();
276
        }
277
278
        return $this->schema;
279
    }
280
281
    /**
282
     * Tells if the connection is established
283
     *
284
     * @return boolean
285
     */
286
    public function isConnected()
287
    {
288
289
        return ($this->state === self::STATE_CONNECTED);
290
    }
291
292
    /**
293
     * Tells if the connection is in error state
294
     *
295
     * @return boolean
296
     */
297
    public function isError()
298
    {
299
300
        return ($this->state === self::STATE_ERROR);
301
    }
302
303
    /**
304
     * Executes a query and return results
305
     *
306
     * @param Query $query   The Query object
307
     * @param array $params  Query values (if any)
308
     * @param array $options Extras query options
309
     *
310
     * @return mixed
311
     */
312
    public function execute(Query $query, array $params = array(),
313
        array $options = array()
314
    ) {
315
        $bridge = $this->newQueryBrige();
316
        $event  = new BeforeQueryEvent($this, $query, $params, $options);
317
        $event->setQueryBridge($bridge);
318
319
        $this->notify($event);
320
321
        if ($event->isStopped()) {
322
            return $event->getResults();
323
        }
324
325
        $stmt = $bridge->execute($query, $params, $options);
326
327
        if ($query->getType() == Query::TYPE_SELECT) {
328
            $stmt->execute($params);
0 ignored issues
show
Unused Code introduced by
The call to QueryBuilder::execute() has too many arguments starting with $params.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
329
330
            if (!$stmt instanceof PDOStatement) {
331
                return false; // never happend
332
            }
333
334
            $tmp = $stmt->fetchAll(
335
                ($query->getFetchMode() != Query::FETCH_SPECIAL ?
336
                    $query->getFetchMode() :
337
                    \PDO::FETCH_ASSOC
338
                )
339
            );
340
341
            if ($query->getFetchMode() === Query::FETCH_SPECIAL) {
342
                $hyd = new Hydrator($query, $this, $bridge->getColumnsAliases());
343
                $results = $hyd->hydrate($tmp);
344
            } else {
345
                $results = $tmp;
346
            }
347
        } else {
348
            $results = $stmt;
349
        }
350
351
        $aevent = new AfterQueryEvent($this, $query, $params, $options, $results);
352
        $this->notify($aevent);
353
354
        return $aevent->getResults();
355
    }
356
357
    /**
358
     * Returns a new instance of a QueryBridge
359
     * 
360
     * @return QueryBridge
361
     */
362
    public function newQueryBrige()
363
    {
364
        return new QueryBridge($this);
365
    }
366
367
    /**
368
     * Defines current state and trigger a STATE_CHANGE event
369
     *
370
     * @param integer $state New connection's state
371
     *
372
     * @return Connection
373
     */
374
    public function setState($state)
375
    {
376
        $newState       = (int)$state;
377
        if ($newState != $this->state) {
378
            $this->notify(
379
                new ConnectionStateChangeEvent($this, $this->state, $newState)
380
            );
381
            $this->state = $newState;
382
        }
383
384
        return $this;
385
    }
386
387
    /**
388
     * Returns current connection state
389
     *
390
     * @return integer
391
     */
392
    public function getState()
393
    {
394
        return $this->state;
395
    }
396
397
    /**
398
     * Sets an error Exception and toggle error state
399
     *
400
     * @param \Exception $exception The exception to be thrown
401
     *
402
     * @return \Exception
403
     */
404
    public function setErrorException(\Exception $exception)
405
    {
406
        $this->setState(self::STATE_ERROR);
407
        $this->notify(new ConnectionErrorEvent($this, $exception));
408
409
        return $exception;
410
    }
411
412
    /**
413
     * Returns a table object representing a database table
414
     *
415
     * @param string $tableName Table name
416
     *
417
     * @throws Exceptions\TableNotFoundException if table is not found
418
     * @return Table
419
     */
420
    public function table($tableName)
421
    {
422
        if (isset($this->tables[$tableName])) {
423
            return $this->tables[$tableName];
424
        }
425
        
426
        if ($this->getSchema()->hasTable($tableName)) {
427
            $table = new Table($tableName);
428
            $table->setConnection($this);
429
            $this->tables[$tableName] = $table;
430
            
431
            return $table;
432
        }
433
434
        throw $this->setErrorException(
435
            new Exceptions\TableNotFoundException(
436
                sprintf(
437
                    'Inexistant table "%s"', 
438
                    $tableName
439
                )
440
            )
441
        );
442
    }
443
444
    /**
445
     * Returns the last inserted ID in the database (if driver supports it)
446
     * 
447
     * @return integer 
448
     */
449
    public function lastInsertId()
450
    {
451
452
        return $this->getDriver()->lastInsertId();
453
    }
454
455
    /**
456
     * Starts a new transaction
457
     *
458
     * @return Connection
459
     */
460
    public function beginTransaction()
461
    {
462
        $this->getDriver()->beginTransaction();
463
464
        return $this;
465
    }
466
467
    /**
468
     * Commits the current transaction
469
     *
470
     * @return Connection
471
     */
472
    public function commit()
473
    {
474
        $this->getDriver()->commit();
475
476
        return $this;
477
    }
478
479
    /**
480
     * Cancels the current transaction
481
     *
482
     * @return Connection
483
     */
484
    public function rollBack()
485
    {
486
        $this->getDriver()->rollBack();
487
488
        return $this;
489
    }
490
}