Completed
Push — master ( 8e7701...8e8829 )
by Sam
03:57 queued 11s
created

PostgreSQLQuery::__destruct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\PostgreSQL;
4
5
use SilverStripe\ORM\Connect\Query;
6
7
/**
8
 * A result-set from a PostgreSQL database.
9
 *
10
 * @package sapphire
11
 * @subpackage model
12
 */
13
class PostgreSQLQuery extends Query
14
{
15
    /**
16
     * The internal Postgres handle that points to the result set.
17
     * @var resource
18
     */
19
    private $handle;
20
21
    private $columnNames = [];
22
23
    /**
24
     * Mapping of postgresql types to PHP types
25
     * Note that the bool => int mapping is by design, designed to mimic MySQL's behaviour
26
     * @var array
27
     */
28
    protected static $typeMapping = [
29
        'bool' => 'int',
30
        'int2' => 'int',
31
        'int4' => 'int',
32
        'int8' => 'int',
33
        'float4' => 'float',
34
        'float8' => 'float',
35
        'numeric' => 'float',
36
    ];
37
38
    /**
39
     * Hook the result-set given into a Query class, suitable for use by sapphire.
40
     * @param resource $handle the internal Postgres handle that is points to the resultset.
41
     */
42
    public function __construct($handle)
43
    {
44
        $this->handle = $handle;
45
46
        $numColumns = pg_num_fields($handle);
47
        for ($i = 0; $i < $numColumns; $i++) {
48
            $this->columnNames[$i] = pg_field_name($handle, $i);
49
        }
50
    }
51
52
    public function __destruct()
53
    {
54
        if (is_resource($this->handle)) {
55
            pg_free_result($this->handle);
56
        }
57
    }
58
59
    public function getIterator()
60
    {
61
        while ($data = $this->nextRecord()) {
62
            yield $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression yield $data returns the type Generator which is incompatible with the return type mandated by SilverStripe\ORM\Connect\Query::getIterator() of array.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
63
        }
64
    }
65
66
    public function numRecords()
67
    {
68
        return pg_num_rows($this->handle);
69
    }
70
71
    public function nextRecord()
72
    {
73
        $row = pg_fetch_array($this->handle, null, PGSQL_NUM);
74
75
        // Correct non-string types
76
        if ($row) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
77
            return $this->parseResult($row);
78
        }
79
80
        return false;
81
    }
82
83
    /**
84
     * @param array $row
85
     * @return array
86
     */
87
    protected function parseResult(array $row)
88
    {
89
        $record = [];
90
91
        foreach ($row as $i => $v) {
92
            $k = $this->columnNames[$i];
93
            $record[$k] = $v;
94
            $type = pg_field_type($this->handle, $i);
95
            if (isset(self::$typeMapping[$type])) {
96
                if ($type === 'bool' && $record[$k] === 't') {
97
                    $record[$k] = 1;
98
99
                    // Note that boolean 'f' will be converted to 0 by this
100
                } else {
101
                    settype($record[$k], self::$typeMapping[$type]);
102
                }
103
            }
104
        }
105
106
        return $record;
107
    }
108
}
109