Completed
Push — master ( 370c73...8403d4 )
by Richard
11:17
created

Connection::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0625

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 19
rs 9.4285
c 2
b 0
f 0
ccs 6
cts 8
cp 0.75
cc 2
eloc 12
nc 2
nop 4
crap 2.0625
1
<?php
2
/**
3
 * You may not change or alter any portion of this comment or credits
4
 * of supporting developers from this source code or any supporting source code
5
 * which is considered copyrighted (c) material of the original comment or credit authors.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
namespace Xoops\Core\Database;
13
14
use Doctrine\DBAL\Configuration;
15
use Doctrine\DBAL\Driver;
16
use Doctrine\DBAL\Cache\QueryCacheProfile;
17
use Doctrine\Common\EventManager;
18
19
/**
20
 * Connection wrapper for Doctrine DBAL Connection
21
 *
22
 * @category  Xoops\Core\Database\Connection
23
 * @package   Connection
24
 * @author    readheadedrod <[email protected]>
25
 * @author    Richard Griffith <[email protected]>
26
 * @copyright 2013-2015 XOOPS Project (http://xoops.org)
27
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
28
 * @version   Release: 2.6
29
 * @link      http://xoops.org
30
 * @since     2.6.0
31
 */
32
class Connection extends \Doctrine\DBAL\Connection
33
{
34
    /**
35
     * @var bool $safe true means it is safe to write to database
36
     * removed allowedWebChanges as unnecessary. Using this instead.
37
     */
38
    protected $safe;
39
40
41
    /**
42
     * @var bool $force true means force writing to database even if safe is not true.
43
     */
44
    protected $force;
45
46
    /**
47
     * @var bool $transactionActive true means a transaction is in process.
48
     */
49
    protected $transactionActive;
50
51
52
    /**
53
     * this is a public setter for the safe variable
54
     *
55
     * @param bool $safe set safe to true if safe to write data to database
56
     *
57
     * @return void
58
     */
59 28
    public function setSafe($safe = true)
60
    {
61 28
        $this->safe = (bool)$safe;
62 28
    }
63
64
    /**
65
     * this is a public getter for the safe variable
66
     *
67
     * @return boolean
68
     */
69 1
    public function getSafe()
70
    {
71 1
        return $this->safe;
72
    }
73
74
    /**
75
     * this is a public setter for the $force variable
76
     *
77
     * @param bool $force when true will force a write to database when safe is false.
78
     *
79
     * @return void
80
     */
81 10
    public function setForce($force = false)
82
    {
83 10
        $this->force = (bool) $force;
84 10
    }
85
86
    /**
87
     * this is a public getter for the $force variable
88
     *
89
     * @return boolean
90
     */
91 1
    public function getForce()
92
    {
93 1
        return $this->force;
94
    }
95
96
    /**
97
     * Initializes a new instance of the Connection class.
98
     *
99
     * This sets up necessary variables before calling parent constructor
100
     *
101
     * @param array         $params       Parameters for the driver
102
     * @param Driver        $driver       The driver to use
103
     * @param Configuration $config       The connection configuration
104
     * @param EventManager  $eventManager Event manager to use
105
     */
106 18
    public function __construct(
107
        array $params,
108
        Driver $driver,
109
        Configuration $config = null,
110
        EventManager $eventManager = null
111
    ) {
112 18
        $this->safe = false;
113 18
        $this->force = false;
114 18
        $this->transactionActive = false;
115
116
        try {
117 18
            parent::__construct($params, $driver, $config, $eventManager);
118
        } catch (\Exception $e) {
119
            // We are dead in the water. This exception may contain very sensitive
120
            // information and cannot be allowed to be displayed as is.
121
            //\Xoops::getInstance()->events()->triggerEvent('core.exception', $e);
122
            trigger_error("Cannot get database connection", E_USER_ERROR);
123
        }
124 18
    }
125
126
    /**
127
     * Prepend the prefix.'_' to the given tablename
128
     * If tablename is empty, just return the prefix.
129
     *
130
     * @param string $tablename tablename
131
     *
132
     * @return string prefixed tablename, or prefix if tablename is empty
133
     */
134 138
    public function prefix($tablename = '')
135
    {
136 138
        $prefix = \XoopsBaseConfig::get('db-prefix');
137 138
        if ($tablename != '') {
138 133
            return $prefix . '_' . $tablename;
139
        } else {
140 6
            return $prefix;
141
        }
142
    }
143
144
    /**
145
     * Inserts a table row with specified data.
146
     *
147
     * Adds prefix to the name of the table then passes to normal function.
148
     *
149
     * @param string $tableName The name of the table to insert data into.
150
     * @param array  $data      An associative array containing column-value pairs.
151
     * @param array  $types     Types of the inserted data.
152
     *
153
     * @return integer The number of affected rows.
154
     */
155 2
    public function insertPrefix($tableName, array $data, array $types = array())
156
    {
157 2
        $tableName = $this->prefix($tableName);
158 2
        return $this->insert($tableName, $data, $types);
159
    }
160
161
162
    /**
163
     * Executes an SQL UPDATE statement on a table.
164
     *
165
     * Adds prefix to the name of the table then passes to normal function.
166
     *
167
     * @param string $tableName  The name of the table to update.
168
     * @param array  $data       The data to update
169
     * @param array  $identifier The update criteria.
170
     * An associative array containing column-value pairs.
171
     * @param array  $types      Types of the merged $data and
172
     * $identifier arrays in that order.
173
     *
174
     * @return integer The number of affected rows.
175
     */
176 1
    public function updatePrefix($tableName, array $data, array $identifier, array $types = array())
177
    {
178 1
        $tableName = $this->prefix($tableName);
179 1
        return $this->update($tableName, $data, $identifier, $types);
180
    }
181
182
    /**
183
     * Executes an SQL DELETE statement on a table.
184
     *
185
     * Adds prefix to the name of the table then passes to delete function.
186
     *
187
     * @param string $tableName  The name of the table on which to delete.
188
     * @param array  $identifier The deletion criteria.
189
     * An associative array containing column-value pairs.
190
     *
191
     * @return integer The number of affected rows.
192
     *
193
     */
194 1
    public function deletePrefix($tableName, array $identifier)
195
    {
196 1
        $tableName = $this->prefix($tableName);
197 1
        return $this->delete($tableName, $identifier);
198
    }
199
200
    /**
201
     * Executes an, optionally parametrized, SQL query.
202
     *
203
     * If the query is parametrized, a prepared statement is used.
204
     * If an SQLLogger is configured, the execution is logged.
205
     *
206
     * @param string            $query  The SQL query to execute.
207
     * @param array             $params The parameters to bind to the query, if any.
208
     * @param array             $types  The types the previous parameters are in.
209
     * @param QueryCacheProfile $qcp    The query Cache profile
210
     *
211
     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
212
     *
213
     * @internal PERF: Directly prepares a driver statement, not a wrapper.
214
     */
215 76
    public function executeQuery(
216
        $query,
217
        array $params = array(),
218
        $types = array(),
219
        QueryCacheProfile $qcp = null
220
    ) {
221 76
        return parent::executeQuery($query, $params, $types, $qcp);
222
    }
223
224
    /**
225
     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
226
     * and returns the number of affected rows.
227
     *
228
     * This method supports PDO binding types as well as DBAL mapping types.
229
     *
230
     * This over ridding process checks to make sure it is safe to do these.
231
     * If force is active then it will over ride the safe setting.
232
     *
233
     * @param string $query  The SQL query.
234
     * @param array  $params The query parameters.
235
     * @param array  $types  The parameter types.
236
     *
237
     * @return integer The number of affected rows.
238
     *
239
     * @internal PERF: Directly prepares a driver statement, not a wrapper.
240
     *
241
     * @todo build a better exception catching system.
242
     */
243 21
    public function executeUpdate($query, array $params = array(), array $types = array())
244
    {
245 21
        $events = \Xoops::getInstance()->events();
246 21
        if ($this->safe || $this->force) {
247 21
            if (!$this->transactionActive) {
248 21
                $this->force = false;
249
            };
250 21
            $events->triggerEvent('core.database.query.start');
251
            try {
252 21
                $result = parent::executeUpdate($query, $params, $types);
253 5
            } catch (\Exception $e) {
254 5
                $events->triggerEvent('core.exception', $e);
255 5
                $result = 0;
256
            }
257 21
            $events->triggerEvent('core.database.query.end');
258
        } else {
259
            //$events->triggerEvent('core.database.query.failure', (array('Not safe:')));
260
            return (int) 0;
261
        }
262 21
        if ($result != 0) {
263
            //$events->triggerEvent('core.database.query.success', (array($query)));
264 10
            return (int) $result;
265
        } else {
266
            //$events->triggerEvent('core.database.query.failure', (array($query)));
267 16
            return (int) 0;
268
        }
269
    }
270
271
    /**
272
     * Starts a transaction by suspending auto-commit mode.
273
     *
274
     * @return void
275
     */
276
    public function beginTransaction()
277
    {
278
        $this->transactionActive = true;
279
        parent::beginTransaction();
280
    }
281
282
    /**
283
     * Commits the current transaction.
284
     *
285
     * @return void
286
     */
287
    public function commit()
288
    {
289
        $this->transactionActive = false;
290
        $this->force = false;
291
        parent::commit();
292
    }
293
294
    /**
295
     * rolls back the current transaction.
296
     *
297
     * @return void
298
     */
299
    public function rollBack()
300
    {
301
        $this->transactionActive = false;
302
        $this->force = false;
303
        parent::rollBack();
304
    }
305
306
    /**
307
     * perform a safe query if allowed
308
     * can receive variable number of arguments
309
     *
310
     * @return mixed returns the value received or null if nothing received.
311
     *
312
     * @todo add error report for using non select while not safe.
313
     * @todo need to check if doctrine allows more than one query to be sent.
314
     * This code assumes only one query is sent and anything else sent is not
315
     * a query. This will have to be readdressed if this is wrong.
316
     *
317
     */
318
    public function query()
319
    {
320
        $events = \Xoops::getInstance()->events();
321
        if (!$this->safe && !$this->force) {
322
            $sql = ltrim(func_get_arg(0));
323 View Code Duplication
            if (!$this->safe && strtolower(substr($sql, 0, 6)) !== 'select') {
324
                // $events->triggerEvent('core.database.query.failure', (array('Not safe:')));
325
                return null;
326
            }
327
        }
328
        $this->force = false; // resets $force back to false
329
        $events->triggerEvent('core.database.query.start');
330
        try {
331
            $result = call_user_func_array(array('parent', 'query'), func_get_args());
332
        } catch (\Exception $e) {
333
            $events->triggerEvent('core.exception', $e);
334
            $result=null;
335
        }
336
        $events->triggerEvent('core.database.query.end');
337
        if ($result) {
338
            //$events->triggerEvent('core.database.query.success', (array('')));
339
            return $result;
340
        } else {
341
            //$events->triggerEvent('core.database.query.failure', (array('')));
342
            return null;
343
        }
344
    }
345
346
    /**
347
     * perform queries from SQL dump file in a batch
348
     *
349
     * @param string $file file path to an SQL dump file
350
     *
351
     * @return bool FALSE if failed reading SQL file or
352
     * TRUE if the file has been read and queries executed
353
     */
354 View Code Duplication
    public function queryFromFile($file)
355
    {
356
        if (false !== ($fp = fopen($file, 'r'))) {
357
            $sql_queries = trim(fread($fp, filesize($file)));
358
            \SqlUtility::splitMySqlFile($pieces, $sql_queries);
359
            foreach ($pieces as $query) {
360
                $prefixed_query = \SqlUtility::prefixQuery(trim($query), $this->prefix());
361
                if ($prefixed_query != false) {
362
                    $this->query($prefixed_query[0]);
363
                }
364
            }
365
            return true;
366
        }
367
        return false;
368
    }
369
370
    /**
371
     * Create a new instance of a SQL query builder.
372
     *
373
     * @return QueryBuilder
374
     */
375 89
    public function createXoopsQueryBuilder()
376
    {
377 89
        return new QueryBuilder($this);
378
    }
379
}
380