Completed
Push — master ( 089ad5...045fb9 )
by Richard
10:39
created

Connection::executeUpdate()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0106

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 27
ccs 14
cts 15
cp 0.9333
rs 8.439
c 2
b 0
f 0
cc 6
eloc 18
nc 9
nop 3
crap 6.0106
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 static $safe;
39
40
41
    /**
42
     * @var bool $force true means force writing to database even if safe is not true.
43
     */
44
    protected static $force;
45
46
    /**
47
     * @var bool $transactionActive true means a transaction is in process.
48
     */
49
    protected static $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 29
    public static function setSafe($safe = true)
60
    {
61 29
        if (is_bool($safe)) {
62 29
            self::$safe = $safe;
63
        }
64 29
    }
65
66
    /**
67
     * this is a public getter for the safe variable
68
     *
69
     * @return boolean
70
     */
71 22
    public static function getSafe()
72
    {
73 22
        return self::$safe;
74
    }
75
76
    /**
77
     * this is a public setter for the $force variable
78
     *
79
     * @param bool $force when true will force a write to database when safe is false.
80
     *
81
     * @return void
82
     */
83 23
    public static function setForce($force = false)
84
    {
85 23
        if (is_bool($force)) {
86 23
            self::$force = $force;
87
        }
88 23
    }
89
90
    /**
91
     * this is a public getter for the $force variable
92
     *
93
     * @return boolean
94
     */
95 1
    public static function getForce()
96
    {
97 1
        return self::$force;
98
    }
99
100
    /**
101
     * Initializes a new instance of the Connection class.
102
     *
103
     * This sets up necessary variables before calling parent constructor
104
     *
105
     * @param array         $params       Parameters for the driver
106
     * @param Driver        $driver       The driver to use
107
     * @param Configuration $config       The connection configuration
108
     * @param EventManager  $eventManager Event manager to use
109
     */
110 1
    public function __construct(
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
111
        array $params,
112
        Driver $driver,
113
        Configuration $config = null,
114
        EventManager $eventManager = null
115
    ) {
116 1
        if (!defined('XOOPS_DB_PROXY') || ($_SERVER['REQUEST_METHOD'] !== 'GET') || (php_sapi_name() === 'cli')) {
117 1
            self::setSafe(true);
118
        } else {
119
            self::setSafe(false);
120
        }
121 1
        self::setForce(false);
122 1
        self::$transactionActive = false;
123
        try {
124 1
            parent::__construct($params, $driver, $config, $eventManager);
125
        } catch (\Exception $e) {
126
            // We are dead in the water. This exception may contain very sensitive
127
            // information and cannot be allowed to be displayed as is.
128
            //\Xoops::getInstance()->events()->triggerEvent('core.exception', $e);
129
            trigger_error("Cannot get database connection", E_USER_ERROR);
130
        }
131
132 1
    }
133
134
    /**
135
     * Prepend the prefix.'_' to the given tablename
136
     * If tablename is empty, just return the prefix.
137
     *
138
     * @param string $tablename tablename
139
     *
140
     * @return string prefixed tablename, or prefix if tablename is empty
141
     */
142 138
    public static function prefix($tablename = '')
143
    {
144 138
        $prefix = \XoopsBaseConfig::get('db-prefix');
145 138
        if ($tablename != '') {
146 133
            return $prefix . '_' . $tablename;
147
        } else {
148 6
            return $prefix;
149
        }
150
    }
151
152
    /**
153
     * Inserts a table row with specified data.
154
     *
155
     * Adds prefix to the name of the table then passes to normal function.
156
     *
157
     * @param string $tableName The name of the table to insert data into.
158
     * @param array  $data      An associative array containing column-value pairs.
159
     * @param array  $types     Types of the inserted data.
160
     *
161
     * @return integer The number of affected rows.
162
     */
163 2
    public function insertPrefix($tableName, array $data, array $types = array())
164
    {
165 2
        $tableName = $this->prefix($tableName);
166 2
        return $this->insert($tableName, $data, $types);
167
    }
168
169
170
    /**
171
     * Executes an SQL UPDATE statement on a table.
172
     *
173
     * Adds prefix to the name of the table then passes to normal function.
174
     *
175
     * @param string $tableName  The name of the table to update.
176
     * @param array  $data       The data to update
177
     * @param array  $identifier The update criteria.
178
     * An associative array containing column-value pairs.
179
     * @param array  $types      Types of the merged $data and
180
     * $identifier arrays in that order.
181
     *
182
     * @return integer The number of affected rows.
183
     */
184 1
    public function updatePrefix($tableName, array $data, array $identifier, array $types = array())
185
    {
186 1
        $tableName = $this->prefix($tableName);
187 1
        return $this->update($tableName, $data, $identifier, $types);
188
    }
189
190
    /**
191
     * Executes an SQL DELETE statement on a table.
192
     *
193
     * Adds prefix to the name of the table then passes to delete function.
194
     *
195
     * @param string $tableName  The name of the table on which to delete.
196
     * @param array  $identifier The deletion criteria.
197
     * An associative array containing column-value pairs.
198
     *
199
     * @return integer The number of affected rows.
200
     *
201
     */
202 1
    public function deletePrefix($tableName, array $identifier)
203
    {
204 1
        $tableName = $this->prefix($tableName);
205 1
        return $this->delete($tableName, $identifier);
206
    }
207
208
    /**
209
     * Executes an, optionally parametrized, SQL query.
210
     *
211
     * If the query is parametrized, a prepared statement is used.
212
     * If an SQLLogger is configured, the execution is logged.
213
     *
214
     * @param string            $query  The SQL query to execute.
215
     * @param array             $params The parameters to bind to the query, if any.
216
     * @param array             $types  The types the previous parameters are in.
217
     * @param QueryCacheProfile $qcp    The query Cache profile
218
     *
219
     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
220
     *
221
     * @internal PERF: Directly prepares a driver statement, not a wrapper.
222
     */
223 76
    public function executeQuery(
224
        $query,
225
        array $params = array(),
226
        $types = array(),
227
        QueryCacheProfile $qcp = null
228
    ) {
229 76
        return parent::executeQuery($query, $params, $types, $qcp);
230
    }
231
232
    /**
233
     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
234
     * and returns the number of affected rows.
235
     *
236
     * This method supports PDO binding types as well as DBAL mapping types.
237
     *
238
     * This over ridding process checks to make sure it is safe to do these.
239
     * If force is active then it will over ride the safe setting.
240
     *
241
     * @param string $query  The SQL query.
242
     * @param array  $params The query parameters.
243
     * @param array  $types  The parameter types.
244
     *
245
     * @return integer The number of affected rows.
246
     *
247
     * @internal PERF: Directly prepares a driver statement, not a wrapper.
248
     *
249
     * @todo build a better exception catching system.
250
     */
251 21
    public function executeUpdate($query, array $params = array(), array $types = array())
252
    {
253 21
        $events = \Xoops::getInstance()->events();
254 21
        if (self::getSafe() || self::getForce()) {
255 21
            if (!self::$transactionActive) {
256 21
                self::setForce(false);
257
            };
258 21
            $events->triggerEvent('core.database.query.start');
259
            try {
260 21
                $result = parent::executeUpdate($query, $params, $types);
261 5
            } catch (\Exception $e) {
262 5
                $events->triggerEvent('core.exception', $e);
263 5
                $result = 0;
264
            }
265 21
            $events->triggerEvent('core.database.query.end');
266
        } else {
267
            //$events->triggerEvent('core.database.query.failure', (array('Not safe:')));
268
            return (int) 0;
269
        }
270 21
        if ($result != 0) {
271
            //$events->triggerEvent('core.database.query.success', (array($query)));
272 10
            return (int) $result;
273
        } else {
274
            //$events->triggerEvent('core.database.query.failure', (array($query)));
275 16
            return (int) 0;
276
        }
277
    }
278
279
    /**
280
     * Starts a transaction by suspending auto-commit mode.
281
     *
282
     * @return void
283
     */
284
    public function beginTransaction()
285
    {
286
        self::$transactionActive = true;
287
        parent::beginTransaction();
288
    }
289
290
    /**
291
     * Commits the current transaction.
292
     *
293
     * @return void
294
     */
295
    public function commit()
296
    {
297
        self::$transactionActive = false;
298
        self::setForce(false);
299
        parent::commit();
300
    }
301
302
    /**
303
     * rolls back the current transaction.
304
     *
305
     * @return void
306
     */
307
    public function rollBack()
308
    {
309
        self::$transactionActive = false;
310
        self::setForce(false);
311
        parent::rollBack();
312
    }
313
314
    /**
315
     * perform a safe query if allowed
316
     * can receive variable number of arguments
317
     *
318
     * @return mixed returns the value received or null if nothing received.
319
     *
320
     * @todo add error report for using non select while not safe.
321
     * @todo need to check if doctrine allows more than one query to be sent.
322
     * This code assumes only one query is sent and anything else sent is not
323
     * a query. This will have to be readdressed if this is wrong.
324
     *
325
     */
326
    public function query()
327
    {
328
        $events = \Xoops::getInstance()->events();
329
        if (!self::getSafe() && !self::getForce()) {
330
            $sql = ltrim(func_get_arg(0));
331
            if (!self::getSafe() && strtolower(substr($sql, 0, 6))!== 'select') {
332
                // $events->triggerEvent('core.database.query.failure', (array('Not safe:')));
333
                return null;
334
            }
335
        }
336
        self::setForce(false); // resets $force back to false
337
        $events->triggerEvent('core.database.query.start');
338
        try {
339
            $result = call_user_func_array(array('parent', 'query'), func_get_args());
340
        } catch (\Exception $e) {
341
            $events->triggerEvent('core.exception', $e);
342
            $result=null;
343
        }
344
        $events->triggerEvent('core.database.query.end');
345
        if ($result) {
346
            //$events->triggerEvent('core.database.query.success', (array('')));
347
            return $result;
348
        } else {
349
            //$events->triggerEvent('core.database.query.failure', (array('')));
350
            return null;
351
        }
352
    }
353
354
    /**
355
     * perform queries from SQL dump file in a batch
356
     *
357
     * @param string $file file path to an SQL dump file
358
     *
359
     * @return bool FALSE if failed reading SQL file or
360
     * TRUE if the file has been read and queries executed
361
     */
362 View Code Duplication
    public function queryFromFile($file)
363
    {
364
        if (false !== ($fp = fopen($file, 'r'))) {
365
            $sql_queries = trim(fread($fp, filesize($file)));
366
            \SqlUtility::splitMySqlFile($pieces, $sql_queries);
367
            foreach ($pieces as $query) {
368
                $prefixed_query = \SqlUtility::prefixQuery(trim($query), $this->prefix());
369
                if ($prefixed_query != false) {
370
                    $this->query($prefixed_query[0]);
371
                }
372
            }
373
            return true;
374
        }
375
        return false;
376
    }
377
378
    /**
379
     * Duplicates original xoops quote.
380
     * Name changed to not confuse normal usage of quote.
381
     *
382
     * @param string $input test to convert
383
     *
384
     * @return mixed converted text
385
     */
386
    public function quoteSlash($input)
387
    {
388
        return $this->quote($input);
389
    }
390
391
392
    /**
393
     * Create a new instance of a SQL query builder.
394
     *
395
     * @return QueryBuilder
396
     */
397 89
    public function createXoopsQueryBuilder()
398
    {
399 89
        return new QueryBuilder($this);
400
    }
401
}
402