Completed
Push — master ( 850e70...ce4a45 )
by Ivan
03:43
created

TableQueryIterator::values()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 8
cts 8
cp 1
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 8
nc 4
nop 1
crap 6
1
<?php
2
namespace vakata\database\schema;
3
4
use vakata\collection\Collection;
5
use vakata\database\DBException;
6
7
/**
8
 * A table query iterator
9
 */
10
class TableQueryIterator implements \Iterator, \ArrayAccess
11
{
12
    const SEP = '___';
13
    /**
14
     * @var array
15
     */
16
    protected $pkey;
17
    /**
18
     * @var Result
19
     */
20
    protected $result;
21
    /**
22
     * @var array[]
23
     */
24
    protected $relations;
25
    /**
26
     * @var string|null
27
     */
28
    protected $primary = null;
29
    /**
30
     * @var int
31
     */
32
    protected $fetched = 0;
33
34 11
    public function __construct(Collection $result, array $pkey, array $relations = [])
35
    {
36 11
        $this->pkey = $pkey;
37 11
        $this->result = $result;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result of type object<vakata\collection\Collection> is incompatible with the declared type object<vakata\database\schema\Result> of property $result.

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...
38 11
        $this->relations = $relations;
39 11
    }
40
41 11
    public function key()
42
    {
43 11
        return $this->fetched;
44
    }
45 11
    public function current()
46
    {
47 11
        $result = null;
48 11
        $remove = [];
49 11
        while ($this->result->valid()) {
50 11
            $row = $this->result->current();
51 11
            $pk = [];
52 11
            foreach ($this->pkey as $field) {
53 11
                $pk[$field] = $row[$field];
54
            }
55 11
            $pk = json_encode($pk);
56 11
            if ($this->primary !== null && $pk !== $this->primary) {
57 8
                break;
58
            }
59 11
            $this->primary = $pk;
60 11
            if (!$result) {
61 11
                $result = $row;
62
            }
63 11
            foreach ($this->relations as $name => $relation) {
64 4
                $relation = $relation[0];
65 4
                $fields = [];
66 4
                $exists = false;
67 4
                foreach ($relation->table->getColumns() as $column) {
68 4
                    $fields[$column] = $row[$name . static::SEP . $column];
69 4
                    if (!$exists && $row[$name . static::SEP . $column] !== null) {
70 4
                        $exists = true;
71
                    }
72 4
                    $remove[] = $name . static::SEP . $column;
73
                }
74 4
                $temp  = &$result;
75 4
                $parts = explode(static::SEP, $name);
76 4
                $name  = array_pop($parts);
77 4
                $full  = '';
78 4
                foreach ($parts as $item) {
79 1
                    $full = $full ? $full . static::SEP . $item : $item;
80 1
                    $temp = &$temp[$item];
81 1
                    $rpk = [];
82 1
                    foreach ($this->relations[$full][0]->table->getPrimaryKey() as $pkey) {
83 1
                        $rpk[$field] = $row[$full . static::SEP . $field];
0 ignored issues
show
Bug introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
84
                    }
85 1
                    $temp = &$temp[json_encode($rpk)];
86
                }
87 4
                if (!isset($temp[$name])) {
88 4
                    $temp[$name] = $relation->many ? [ '___clean' => true ] : null;
89
                }
90 4
                $temp = &$temp[$name];
91 4
                if ($exists) {
92 4
                    if ($relation->many) {
93 4
                        $rpk = [];
94 4
                        foreach ($relation->table->getPrimaryKey() as $field) {
95 4
                            $rpk[$field] = $fields[$field];
96
                        }
97 4
                        $temp[json_encode($rpk)] = array_merge($temp[json_encode($rpk)] ?? [], $fields);
98
                    } else {
99 4
                        $temp = array_merge($temp ?? [], $fields);
100
                    }
101
                }
102
            }
103 11
            $this->result->next();
104
        }
105 11
        if ($result) {
106 11
            foreach ($remove as $name) {
107 4
                unset($result[$name]);
108
            }
109 11
            $result = $this->values($result);
110
        }
111 11
        return $result;
112
    }
113 11
    protected function values(array $data)
114
    {
115 11
        foreach ($data as $k => $v) {
116 11
            if (is_array($v) && isset($v['___clean']) && $v['___clean'] === true) {
117 4
                unset($v['___clean']);
118 4
                $data[$k] = array_values($v);
119 4
                foreach ($data[$k] as $kk => $vv) {
120 11
                    $data[$k][$kk] = $this->values($vv);
121
                }
122
            }
123
        }
124 11
        return $data;
125
    }
126
127 11
    public function rewind()
128
    {
129 11
        $this->fetched = 0;
130 11
        $this->primary = null;
131 11
        return $this->result->rewind();
132
    }
133 11
    public function next()
134
    {
135 11
        if ($this->primary === null) {
136
            $this->result->next();
137
            if ($this->result->valid()) {
138
                $row = $this->result->current();
139
                $temp = [];
140
                foreach ($this->pkey as $field) {
141
                    $temp[$field] = $row[$field];
142
                }
143
                $this->primary = json_encode($temp);
144
                return;
145
            }
146
        }
147 11
        $this->fetched ++;
148 11
        while ($this->result->valid()) {
149 8
            $row = $this->result->current();
150 8
            $pk = [];
151 8
            foreach ($this->pkey as $field) {
152 8
                $pk[$field] = $row[$field];
153
            }
154 8
            $pk = json_encode($pk);
155 8
            if ($this->primary !== $pk) {
156 8
                $this->primary = $pk;
157 8
                break;
158
            }
159
            $this->result->next();
160
        }
161 11
    }
162 11
    public function valid()
163
    {
164 11
        return $this->result->valid();
165
    }
166
167 9
    public function offsetGet($offset)
168
    {
169 9
        $index = $this->fetched;
170 9
        $item = null;
171 9
        foreach ($this as $k => $v) {
172 9
            if ($k === $offset) {
173 9
                $item = $v;
174
            }
175
        }
176 9
        foreach ($this as $k => $v) {
177 9
            if ($k === $index) {
178 9
                break;
179
            }
180
        }
181 9
        return $item;
182
    }
183
    public function offsetExists($offset)
184
    {
185
        $index = $this->fetched;
186
        $exists = false;
187
        foreach ($this as $k => $v) {
188
            if ($k === $offset) {
189
                $exists = true;
190
            }
191
        }
192
        foreach ($this as $k => $v) {
193
            if ($k === $index) {
194
                break;
195
            }
196
        }
197
        return $exists;
198
    }
199
    public function offsetSet($offset, $value)
200
    {
201
        throw new DBException('Invalid call to offsetSet');
202
    }
203
    public function offsetUnset($offset)
204
    {
205
        throw new DBException('Invalid call to offsetUnset');
206
    }
207
}
208