CDatabase   B
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 291
Duplicated Lines 53.26 %

Coupling/Cohesion

Components 2
Dependencies 0

Importance

Changes 0
Metric Value
dl 155
loc 291
rs 8.295
c 0
b 0
f 0
wmc 42
lcom 2
cbo 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 9 72 11
B __destruct() 26 26 6
A __get() 0 8 2
A connect() 0 9 2
C transaction() 40 40 7
C commit() 40 40 7
C rollback() 40 40 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CDatabase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CDatabase, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
//------------------------------------------------------------------------------
4
//
5
//  eTraxis - Records tracking web-based system
6
//  Copyright (C) 2004-2011  Artem Rodygin
7
//
8
//  This program is free software: you can redistribute it and/or modify
9
//  it under the terms of the GNU General Public License as published by
10
//  the Free Software Foundation, either version 3 of the License, or
11
//  (at your option) any later version.
12
//
13
//  This program is distributed in the hope that it will be useful,
14
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
//  GNU General Public License for more details.
17
//
18
//  You should have received a copy of the GNU General Public License
19
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
//
21
//------------------------------------------------------------------------------
22
23
/**
24
 * Database Abstraction Layer
25
 *
26
 * This module implements eTraxis connectivity.
27
 *
28
 * @package Engine
29
 * @subpackage DAL
30
 */
31
32
/**#@+
33
 * Dependency.
34
 */
35
require_once('../engine/debug.php');
36
require_once('../engine/utility.php');
37
require_once('../engine/locale.php');
38
/**#@-*/
39
40
/**#@+
41
 * Supported database driver.
42
 */
43
define('DRIVER_MYSQL50', 1);  // MySQL 5.0 or later
44
define('DRIVER_MSSQL2K', 2);  // Microsoft SQL Server 2000 or later
45
define('DRIVER_ORACLE9', 3);  // Oracle 9i or later
46
define('DRIVER_PGSQL80', 4);  // PostgreSQL 8.0 or later
47
/**#@-*/
48
49
$res_driver = array
50
(
51
    DRIVER_MYSQL50 => RES_MYSQL_ID,
52
    DRIVER_MSSQL2K => RES_MSSQL_ID,
53
    DRIVER_ORACLE9 => RES_ORACLE_ID,
54
    DRIVER_PGSQL80 => RES_POSTGRESQL_ID,
55
);
56
57
//------------------------------------------------------------------------------
58
//  DAL database connection.
59
//------------------------------------------------------------------------------
60
61
/**
62
 * Database connection, implemented via Singleton pattern.
63
 *
64
 * @package Engine
65
 * @subpackage DAL
66
 */
67
class CDatabase
68
{
69
    /**
70
     * Static object of itself.
71
     * @var CDatabase
72
     */
73
    private static $object = NULL;
74
75
    /**
76
     * Link of opened connection.
77
     * @var resource|mysqli
78
     */
79
    private $link = FALSE;
80
81
    /**
82
     * TRUE if a transaction is currently under progress, FALSE otherwise.
83
     * @var bool
84
     */
85
    private $is_transaction = FALSE;
86
87
    /**
88
     * Establishes connection to eTraxis database.
89
     */
90
    private function __construct ()
91
    {
92
        debug_write_log(DEBUG_TRACE, '[CDatabase::__construct]');
93
94
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
95
        {
96
            if (extension_loaded('mysqli'))
97
            {
98
                $this->link = mysqli_connect(DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_DBNAME);
99
100
                if ($this->link)
101
                {
102
                    mysqli_query($this->link, 'set names utf8');
103
                }
104
            }
105
            else
106
            {
107
                $this->link = mysql_connect(DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD);
108
109
                if ($this->link)
110
                {
111
                    if (mysql_select_db(DATABASE_DBNAME, $this->link))
112
                    {
113
                        mysql_query('set names utf8', $this->link);
114
                    }
115
                    else
116
                    {
117
                        debug_write_log(DEBUG_WARNING, '[CDatabase::__construct] Error on selecting MySQL database.');
118
                        mysql_close($this->link);
119
                        $this->link = FALSE;
0 ignored issues
show
Documentation Bug introduced by Artem Rodygin
It seems like FALSE of type false is incompatible with the declared type resource|object<mysqli> of property $link.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
120
                    }
121
                }
122
            }
123
        }
124
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
125
        {
126
            $conn_info = array
127
            (
128
                'APP'          => 'eTraxis',
129
                'CharacterSet' => 'UTF-8',
130
                'Database'     => DATABASE_DBNAME,
131
            );
132
133 View Code Duplication
            if (ustrlen(trim(DATABASE_USERNAME)) != 0)
0 ignored issues
show
Duplication introduced by Artem Rodygin
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134
            {
135
                $conn_info['UID'] = DATABASE_USERNAME;
136
                $conn_info['PWD'] = DATABASE_PASSWORD;
137
            }
138
139
            $this->link = sqlsrv_connect(DATABASE_HOST, $conn_info);
140
        }
141
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
142
        {
143
            $this->link = dbx_connect(DBX_OCI8, DATABASE_HOST, DATABASE_DBNAME, DATABASE_USERNAME, DATABASE_PASSWORD);
144
        }
145
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
146
        {
147
            if (strlen(trim(DATABASE_HOST)) == 0)
148
            {
149
                $this->link = pg_connect(sprintf('dbname=%s user=%s password=%s', DATABASE_DBNAME, DATABASE_USERNAME, DATABASE_PASSWORD));
150
            }
151 View Code Duplication
            else
0 ignored issues
show
Duplication introduced by Artem Rodygin
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
            {
153
                $this->link = pg_connect(sprintf('host=%s dbname=%s user=%s password=%s', DATABASE_HOST, DATABASE_DBNAME, DATABASE_USERNAME, DATABASE_PASSWORD));
154
            }
155
        }
156
        else
157
        {
158
            debug_write_log(DEBUG_WARNING, '[CDatabase::__construct] Unknown database driver.');
159
            $this->link = FALSE;
160
        }
161
    }
162
163
    /**
164
     * Closes connection to eTraxis database.
165
     */
166 View Code Duplication
    public function __destruct()
0 ignored issues
show
Duplication introduced by Artem Rodygin
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
169
        {
170
            if (extension_loaded('mysqli'))
171
            {
172
                mysqli_close($this->link);
173
            }
174
            else
175
            {
176
                mysql_close($this->link);
177
            }
178
        }
179
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
180
        {
181
            sqlsrv_close($this->link);
182
        }
183
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
184
        {
185
            dbx_close($this->link);
186
        }
187
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
188
        {
189
            pg_close($this->link);
190
        }
191
    }
192
193
    /**
194
     * @ignore
195
     */
196
    public function __get ($name)
197
    {
198
        switch ($name)
199
        {
200
            case 'link': return $this->link;
201
            default:     return NULL;
202
        }
203
    }
204
205
    /**
206
     * Connects to database.
207
     *
208
     * @return CDatabase Database singleton object.
209
     */
210
    public static function connect ()
211
    {
212
        if (is_null(self::$object))
213
        {
214
            self::$object = new CDatabase();
215
        }
216
217
        return self::$object;
218
    }
219
220
    /**
221
     * Starts transaction.
222
     *
223
     * @return bool TRUE if transaction is started successfully, FALSE otherwise.
224
     */
225 View Code Duplication
    public function transaction ()
0 ignored issues
show
Duplication introduced by Artem Rodygin
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
226
    {
227
        if ($this->is_transaction)
228
        {
229
            debug_write_log(DEBUG_WARNING, '[CDatabase::transaction] Transaction is under progress.');
230
            return FALSE;
231
        }
232
233
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
234
        {
235
            if (extension_loaded('mysqli'))
236
            {
237
                mysqli_query($this->link, 'start transaction');
238
            }
239
            else
240
            {
241
                mysql_query('start transaction', $this->link);
242
            }
243
        }
244
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
245
        {
246
            sqlsrv_begin_transaction($this->link);
247
        }
248
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
249
        {
250
            dbx_query($this->link, 'set transaction');
251
        }
252
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
253
        {
254
            pg_query($this->link, 'start transaction');
255
        }
256
        else
257
        {
258
            debug_write_log(DEBUG_WARNING, '[CDatabase::transaction] Unknown database driver.');
259
            return FALSE;
260
        }
261
262
        $this->is_transaction = TRUE;
263
        return TRUE;
264
    }
265
266
    /**
267
     * Commits current transaction.
268
     *
269
     * @return bool TRUE if transaction is committed successfully, FALSE otherwise.
270
     */
271 View Code Duplication
    public function commit ()
0 ignored issues
show
Duplication introduced by Artem Rodygin
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272
    {
273
        if (!$this->is_transaction)
274
        {
275
            debug_write_log(DEBUG_WARNING, '[CDatabase::commit] No active transactions.');
276
            return FALSE;
277
        }
278
279
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
280
        {
281
            if (extension_loaded('mysqli'))
282
            {
283
                mysqli_query($this->link, 'commit');
284
            }
285
            else
286
            {
287
                mysql_query('commit', $this->link);
288
            }
289
        }
290
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
291
        {
292
            sqlsrv_commit($this->link);
293
        }
294
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
295
        {
296
            dbx_query($this->link, 'commit');
297
        }
298
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
299
        {
300
            pg_query($this->link, 'commit');
301
        }
302
        else
303
        {
304
            debug_write_log(DEBUG_WARNING, '[CDatabase::commit] Unknown database driver.');
305
            return FALSE;
306
        }
307
308
        $this->is_transaction = FALSE;
309
        return TRUE;
310
    }
311
312
    /**
313
     * Rolls back current transaction.
314
     *
315
     * @return bool TRUE if transaction is rolled back successfully, FALSE otherwise.
316
     */
317 View Code Duplication
    public function rollback ()
0 ignored issues
show
Duplication introduced by Artem Rodygin
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318
    {
319
        if (!$this->is_transaction)
320
        {
321
            debug_write_log(DEBUG_WARNING, '[CDatabase::rollback] No active transactions.');
322
            return FALSE;
323
        }
324
325
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
326
        {
327
            if (extension_loaded('mysqli'))
328
            {
329
                mysqli_query($this->link, 'rollback');
330
            }
331
            else
332
            {
333
                mysql_query('rollback', $this->link);
334
            }
335
        }
336
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
337
        {
338
            sqlsrv_rollback($this->link);
339
        }
340
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
341
        {
342
            dbx_query($this->link, 'rollback');
343
        }
344
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
345
        {
346
            pg_query($this->link, 'rollback');
347
        }
348
        else
349
        {
350
            debug_write_log(DEBUG_WARNING, '[CDatabase::rollback] Unknown database driver.');
351
            return FALSE;
352
        }
353
354
        $this->is_transaction = FALSE;
355
        return TRUE;
356
    }
357
}
358
359
//------------------------------------------------------------------------------
360
//  DAL recordset.
361
//------------------------------------------------------------------------------
362
363
/**
364
 * DAL recordset.
365
 *
366
 * The class implements DAL recordset and several functions to work with.
367
 * The implementation is universal and doesn't depend on type of database.
368
 *
369
 * @package Engine
370
 * @subpackage DAL
371
 */
372
class CRecordset
373
{
374
    /**#@+
375
     * @ignore For internal use only.
376
     */
377
    private $handle;  // [resource] connection
378
    private $result;  // [resource] query result
379
    private $resptr;  // [int]      recordset cursor (number of current record from 0)
380
    /**#@-*/
381
382
    /**
383
     * Number of rows in resulted recordset (read-only).
384
     * @var int
385
     */
386
    protected $rows;
387
388
    /**
389
     * Number of columns in resulted recordset (read-only).
390
     * @var int
391
     */
392
    protected $cols;
393
394
    /**
395
     * Executes specified query and constructs itself as resulted recordset.
396
     *
397
     * @param string $sql SQL-query being executed.
398
     */
399
    public function __construct ($sql)
400
    {
401
        $this->handle = CDatabase::connect()->link;
0 ignored issues
show
Documentation introduced by Artem Rodygin
The property $link is declared private in CDatabase. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
402
        $this->result = FALSE;
403
        $this->resptr = 0;
404
        $this->rows   = 0;
405
        $this->cols   = 0;
406
407
        list($msec, $sec) = explode(' ', microtime());
408
        $start = (float)$msec + (float)$sec;
409
410
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
411
        {
412
            if (extension_loaded('mysqli'))
413
            {
414
                $this->result = mysqli_query($this->handle, $sql);
415
416
                if (is_object($this->result))
417
                {
418
                    $this->rows = $this->result->num_rows;
419
                    $this->cols = $this->result->field_count;
420
                }
421
            }
422
            else
423
            {
424
                $this->result = mysql_query($sql, $this->handle);
425
426
                if (is_resource($this->result))
427
                {
428
                    $this->rows = mysql_num_rows($this->result);
429
                    $this->cols = mysql_num_fields($this->result);
430
                }
431
            }
432
        }
433
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
434
        {
435
            $this->result = sqlsrv_query($this->handle, $sql, NULL, array('Scrollable' => SQLSRV_CURSOR_STATIC));
436
437
            if (is_resource($this->result))
438
            {
439
                $this->rows = sqlsrv_num_rows($this->result);
440
                $this->cols = sqlsrv_num_fields($this->result);
441
            }
442
443
            $this->resptr = -1;
444
        }
445
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
446
        {
447
            $this->result = dbx_query($this->handle, $sql, DBX_COLNAMES_LOWERCASE);
448
449
            if (is_object($this->result))
450
            {
451
                $this->rows = $this->result->rows;
452
                $this->cols = $this->result->cols;
453
            }
454
        }
455
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
456
        {
457
            $this->result = pg_query($this->handle, $sql);
458
459
            if (is_resource($this->result))
460
            {
461
                $this->rows = pg_num_rows($this->result);
462
                $this->cols = pg_num_fields($this->result);
463
            }
464
        }
465
        else
466
        {
467
            debug_write_log(DEBUG_WARNING, '[CRecordset::__construct] Unknown database driver.');
468
        }
469
470
        list($msec, $sec) = explode(' ', microtime());
471
        $stop = (float)$msec + (float)$sec;
472
473
        debug_write_log(DEBUG_DUMP,        'SQL text = ' . $sql);
474
        debug_write_log(DEBUG_PERFORMANCE, 'SQL time = ' . ($stop - $start));
475
476
        if (!$this->result)
477
        {
478
            debug_write_log(DEBUG_WARNING, '[CRecordset::__construct] ' . $this->error());
479
        }
480
    }
481
482
    /**
483
     * Frees all resources associated with the recordset.
484
     */
485 View Code Duplication
    public function __destruct()
0 ignored issues
show
Duplication introduced by Artem Rodygin
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
486
    {
487
        if (is_resource($this->result))
488
        {
489
            if (DATABASE_DRIVER == DRIVER_MYSQL50)
490
            {
491
                if (extension_loaded('mysqli'))
492
                {
493
                    mysqli_free_result($this->result);
494
                }
495
                else
496
                {
497
                    mysql_free_result($this->result);
498
                }
499
            }
500
            elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
501
            {
502
                sqlsrv_free_stmt($this->result);
503
            }
504
            elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
505
            {
506
                // nothing to do in case of DBX
507
            }
508
            elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
509
            {
510
                pg_free_result($this->result);
511
            }
512
            else
513
            {
514
                debug_write_log(DEBUG_WARNING, '[CRecordset::__destruct] Unknown database driver.');
515
            }
516
        }
517
    }
518
519
    /**
520
     * @ignore
521
     */
522
    public function __get ($name)
523
    {
524
        switch ($name)
525
        {
526
            case 'rows': return $this->rows;
527
            case 'cols': return $this->cols;
528
            default:     return NULL;
529
        }
530
    }
531
532
    /**
533
     * Returns error message of last operation.
534
     *
535
     * @return string Error message of last operation, or NULL on failure.
536
     */
537
    public function error ()
538
    {
539
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
540
        {
541
            if (extension_loaded('mysqli'))
542
            {
543
                $errno = mysqli_errno($this->handle);
544
                $error = mysqli_error($this->handle);
545
            }
546
            else
547
            {
548
                $errno = mysql_errno($this->handle);
549
                $error = mysql_error($this->handle);
550
            }
551
            $retval = "MySQL error {$errno}: {$error}";
552
        }
553
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
554
        {
555
            $error  = sqlsrv_errors(SQLSRV_ERR_ALL);
556
            $retval = (is_null($error)
557
                    ? NULL
558
                    : "MSSQL error {$error[0]['code']}: {$error[0]['message']}");
559
        }
560
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
561
        {
562
            $error  = ocierror($this->handle->handle);
563
            $retval = "Oracle error {$error['code']}: {$error['message']}";
564
        }
565
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
566
        {
567
            $error  = pg_last_error($this->handle);
568
            $retval = "PostgreSQL error: {$error}";
569
        }
570
        else
571
        {
572
            debug_write_log(DEBUG_WARNING, '[CRecordset::error] Unknown database driver.');
573
            return NULL;
574
        }
575
576
        return $retval;
577
    }
578
579
    /**
580
     * Moves cursor to specified record.
581
     *
582
     * @param int $row_number Number of record, zero-based.
583
     * @return bool TRUE on success, FALSE otherwise.
584
     */
585
    public function seek ($row_number = 0)
586
    {
587
        if (!$this->result)
588
        {
589
            debug_write_log(DEBUG_WARNING, '[CRecordset::seek] No stored recordset.');
590
            return FALSE;
591
        }
592
593
        if ($row_number < 0 || $row_number >= $this->rows)
594
        {
595
            debug_write_log(DEBUG_WARNING, '[CRecordset::seek] Row number is out of stored recordset.');
596
            return FALSE;
597
        }
598
599
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
600
        {
601
            if (extension_loaded('mysqli'))
602
            {
603
                $retval = mysqli_data_seek($this->result, $row_number);
604
            }
605
            else
606
            {
607
                $retval = mysql_data_seek($this->result, $row_number);
608
            }
609
        }
610
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
611
        {
612
            $this->resptr = $row_number;
613
            $retval = TRUE;
614
        }
615
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
616
        {
617
            $this->resptr = $row_number;
618
            $retval = TRUE;
619
        }
620
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
621
        {
622
            $retval = pg_result_seek($this->result, $row_number);
623
        }
624
        else
625
        {
626
            debug_write_log(DEBUG_WARNING, '[CRecordset::seek] Unknown database driver.');
627
            return FALSE;
628
        }
629
630
        if (!$retval)
631
        {
632
            debug_write_log(DEBUG_WARNING, '[CRecordset::seek] ' . $this->error());
633
        }
634
635
        return $retval;
636
    }
637
638
    /**
639
     * Returns next record from recordset.
640
     *
641
     * Returns the record for current cursor and then moves cursor forward to the next one.
642
     * The record is returned as array with two sets of keys - one set is zero-based indexes, another is names of record fields.
643
     *
644
     * @param int|string $field Optional field name or zero-based index.
645
     * @return mixed|array If <i>field</i> is not specified, returns whole record as an array, or FALSE if there is no more record to return.
646
     * If <i>field</i> is specified, then returns value of specified field (it could be both zero-based index, or field name).
647
     *
648
     * Example #1:
649
     * <code>
650
     * $rs = new CRecordset("select my_id, my_field from my_table");
651
     *
652
     * while ($row = $rs->fetch())
653
     * {
654
     *     printf("%u\t%s\n", $row["my_id"], $row["my_field"]);
655
     * }
656
     * </code>
657
     *
658
     * Example #2:
659
     * <code>
660
     * $rs = new CRecordset("select count(*) from my_table");
661
     * echo($rs->fetch(0));
662
     * </code>
663
     */
664
    public function fetch ($field = NULL)
665
    {
666
        if (!$this->result)
667
        {
668
            debug_write_log(DEBUG_WARNING, '[CRecordset::fetch] No stored recordset.');
669
            return FALSE;
670
        }
671
672
        if (DATABASE_DRIVER == DRIVER_MYSQL50)
673
        {
674
            if (extension_loaded('mysqli'))
675
            {
676
                $retval = mysqli_fetch_array($this->result, MYSQLI_BOTH);
677
            }
678
            else
679
            {
680
                $retval = mysql_fetch_array($this->result, MYSQL_BOTH);
681
            }
682
        }
683
        elseif (DATABASE_DRIVER == DRIVER_MSSQL2K)
684
        {
685
            if ($this->resptr == -1)
686
            {
687
                $retval = sqlsrv_fetch_array($this->result, SQLSRV_FETCH_BOTH, SQLSRV_SCROLL_NEXT);
688
            }
689
            else
690
            {
691
                $retval = sqlsrv_fetch_array($this->result, SQLSRV_FETCH_BOTH, SQLSRV_SCROLL_ABSOLUTE, $this->resptr);
692
                $this->resptr = -1;
693
            }
694
        }
695
        elseif (DATABASE_DRIVER == DRIVER_ORACLE9)
696
        {
697
            if ($this->resptr == $this->result->rows)
698
            {
699
                debug_write_log(DEBUG_WARNING, '[CRecordset::fetch] No more rows to return.');
700
                return FALSE;
701
            }
702
703
            $retval = $this->result->data[$this->resptr++];
704
        }
705
        elseif (DATABASE_DRIVER == DRIVER_PGSQL80)
706
        {
707
            $retval = pg_fetch_array($this->result);
708
        }
709
        else
710
        {
711
            debug_write_log(DEBUG_WARNING, '[CRecordset::fetch] Unknown database driver.');
712
            return FALSE;
713
        }
714
715
        if (!is_array($retval))
716
        {
717
            return NULL;
718
        }
719
720
        return (is_null($field) ? $retval : $retval[$field]);
721
    }
722
}
723
724
//------------------------------------------------------------------------------
725
//  Functions.
726
//------------------------------------------------------------------------------
727
728
/**
729
 * Executes specified SQL-file from "sql" eTraxis directory.
730
 *
731
 * The function accepts variable number of arguments. It opens specified SQL-file and replaces each "%i"
732
 * (where <i>i</i> is a natural number) substring with related additional argument.
733
 *
734
 * @param string $query Path to file with SQL-query (path is related to "sql" directory).
735
 * @param mixed Value, which each "%1" substring will be replaced with.
736
 * @param mixed Value, which each "%2" substring will be replaced with.
737
 * @param mixed ... (and so on)
738
 * @return CRecordset Resulted {@link CRecordset DAL recordset}.
739
 *
740
 * Example of usage:
741
 * <code>
742
 * $rs = dal_query("accounts/list.sql", "username");
743
 *
744
 * while ($row = $rs->fetch())
745
 * {
746
 *     foreach ($row as $item)
747
 *     {
748
 *         echo("$item\t");
749
 *     }
750
 *
751
 *     echo("\n");
752
 * }
753
 * </code>
754
 */
755
function dal_query ($query)
756
{
757
    debug_write_log(DEBUG_TRACE, '[dal_query] ' . $query);
758
759
    $sql = file_get_contents(LOCALROOT . '/sql/' . $query);
760
    $sql = str_replace("\n", ' ', $sql);
761
    $sql = preg_replace('([ ]+)', ' ', $sql);
762
763
    if (DATABASE_DRIVER == DRIVER_ORACLE9)
764
    {
765
        $pos = ustrrpos($sql, 'order by');
766
767
        if ($pos !== FALSE)
768
        {
769
            $sql = usubstr($sql, 0, $pos) . preg_replace('(\s[a-z]+\.)', ' ', usubstr($sql, $pos));
770
        }
771
    }
772
773
    $count = func_num_args() - 1;
774
775
    for ($i = $count; $i >= 1; $i--)
776
    {
777
        $search  = '%' . $i;
778
        $replace = func_get_arg($i);
779
780
        if (strpos($sql, "'{$search}'") === FALSE)
781
        {
782
            if (is_null($replace))
783
            {
784
                $replace = 'NULL';
785
            }
786
        }
787
        else
788
        {
789
            if (is_null($replace))
790
            {
791
                $search  = "'{$search}'";
792
                $replace = 'NULL';
793
            }
794
            else
795
            {
796
                $replace = ustr2sql($replace);
797
            }
798
        }
799
800
        $sql = ustr_replace($search, $replace, $sql);
801
    }
802
803
    return new CRecordset($sql);
804
}
805
806
/**
807
 * Starts transaction.
808
 *
809
 * @return bool TRUE if transaction is started successfully, FALSE otherwise.
810
 */
811
function dal_transaction_start ()
812
{
813
    debug_write_log(DEBUG_TRACE, '[dal_transaction_start]');
814
815
    return CDatabase::connect()->transaction();
816
}
817
818
/**
819
 * Commits or rolls back current transaction.
820
 *
821
 * @param bool $commit TRUE if transaction must be committed, FALSE - rolled back.
822
 * @return bool TRUE if transaction is stopped successfully, FALSE otherwise.
823
 */
824
function dal_transaction_stop ($commit)
825
{
826
    debug_write_log(DEBUG_TRACE, '[dal_transaction_stop] ' . $commit);
827
828
    return ($commit ? CDatabase::connect()->commit()
829
                    : CDatabase::connect()->rollback());
830
}
831
832
?>
0 ignored issues
show
Best Practice introduced by Artem Rodygin
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
833