Rev 1152 | Rev 1157 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1152 | Rev 1156 | ||
---|---|---|---|
Line 34... | Line 34... | ||
34 | * @return int |
34 | * @return int |
35 | */ |
35 | */ |
36 | abstract protected function do_num_rows(): int; |
36 | abstract protected function do_num_rows(): int; |
37 | 37 | ||
38 | /** |
38 | /** |
- | 39 | * @var array|null |
|
- | 40 | */ |
|
- | 41 | protected $prefetchedArray = null; |
|
- | 42 | ||
- | 43 | /** |
|
- | 44 | * @var int |
|
- | 45 | */ |
|
- | 46 | protected $countAlreadyFetched = 0; |
|
- | 47 | ||
- | 48 | /** |
|
- | 49 | * Please override this method if the database drive can perform a "fetch all" in its own way |
|
- | 50 | * |
|
- | 51 | * @return void |
|
- | 52 | * @throws OIDplusConfigInitializationException |
|
- | 53 | * @throws OIDplusException |
|
- | 54 | * @throws \ReflectionException |
|
- | 55 | */ |
|
- | 56 | public function prefetchAll() { |
|
- | 57 | if (!is_null($this->prefetchedArray)) return; |
|
- | 58 | $pfa = array(); |
|
- | 59 | while ($row = $this->fetch_array()) { |
|
- | 60 | $pfa[] = $row; // you may not edit $this->prefetchedArray at this step, because $this->>fetch_array() checks it |
|
- | 61 | $this->countAlreadyFetched--; // because fetch_array() increases $this->countAlreadyFetched, we need to revert it |
|
- | 62 | } |
|
- | 63 | $this->prefetchedArray = $pfa; |
|
- | 64 | } |
|
- | 65 | ||
- | 66 | /** |
|
39 | * @return int |
67 | * @return int |
- | 68 | * @throws OIDplusConfigInitializationException |
|
- | 69 | * @throws OIDplusException |
|
40 | */ |
70 | */ |
41 | public final function num_rows(): int { |
71 | public final function num_rows(): int { |
42 | if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)')); |
72 | if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)')); |
- | 73 | ||
- | 74 | if (!is_null($this->prefetchedArray)) { |
|
- | 75 | return count($this->prefetchedArray) + $this->countAlreadyFetched; |
|
- | 76 | } |
|
- | 77 | ||
43 | return $this->do_num_rows(); |
78 | $ret = $this->do_num_rows(); |
- | 79 | ||
- | 80 | if ($ret === -1) throw new OIDplusException(_L('The database driver has problems with "%1"','num_rows')); |
|
- | 81 | ||
- | 82 | return $ret; |
|
- | 83 | } |
|
- | 84 | ||
- | 85 | /** |
|
- | 86 | * Plugins can override and extend this method. It post-processes contents of fetch_array() and fetch_object() |
|
- | 87 | * to fix various issues with database drivers. |
|
- | 88 | * |
|
- | 89 | * @param array|object &$ret |
|
- | 90 | * @return void |
|
- | 91 | */ |
|
- | 92 | protected function fixFields(&$ret) { |
|
- | 93 | // ODBC gives bit(1) as binary, MySQL as integer and PDO as string. |
|
- | 94 | // We'll do it like MySQL does, even if ODBC is actually more correct. |
|
- | 95 | foreach ($ret as &$value) { |
|
- | 96 | if ($value === chr(0)) $value = 0; |
|
- | 97 | if ($value === chr(1)) $value = 1; |
|
- | 98 | } |
|
- | 99 | ||
- | 100 | // Oracle returns $ret['VALUE'] because unquoted column-names are always upper-case |
|
- | 101 | // We can't quote every single column throughout the whole program, so we use this workaround... |
|
- | 102 | if (is_array($ret)) { |
|
- | 103 | $keys = array_keys($ret); |
|
- | 104 | foreach ($keys as $key) { |
|
- | 105 | $ret[strtolower($key)] = $ret[$key]; |
|
- | 106 | $ret[strtoupper($key)] = $ret[$key]; |
|
- | 107 | } |
|
- | 108 | } else if (is_object($ret)) { |
|
- | 109 | foreach ($ret as $name => $val) { |
|
- | 110 | $ret->{strtoupper($name)} = $val; |
|
- | 111 | $ret->{strtolower($name)} = $val; |
|
- | 112 | } |
|
- | 113 | } |
|
44 | } |
114 | } |
45 | 115 | ||
46 | /** |
116 | /** |
- | 117 | * Please override do_fetch_object(), do_fetch_array(), or both. |
|
47 | * @return array|null |
118 | * @return array|null |
48 | */ |
119 | */ |
49 | protected function do_fetch_array()/*: ?array*/ { |
120 | protected function do_fetch_array()/*: ?array*/ { |
50 | assert(false); |
121 | assert(false); |
51 | return null; |
122 | return null; |
52 | } |
123 | } |
53 | 124 | ||
54 | /** |
125 | /** |
55 | * @return array|null |
126 | * @return array|null |
- | 127 | * @throws OIDplusConfigInitializationException |
|
- | 128 | * @throws OIDplusException |
|
- | 129 | * @throws \ReflectionException |
|
56 | */ |
130 | */ |
57 | public final function fetch_array()/*: ?array*/ { |
131 | public final function fetch_array()/*: ?array*/ { |
58 | if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)')); |
132 | if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)')); |
- | 133 | if (!is_null($this->prefetchedArray)) { |
|
- | 134 | // Prefetched value exists. Use it. |
|
- | 135 | $ary = array_shift($this->prefetchedArray); |
|
59 | 136 | } else { |
|
60 | $reflector = new \ReflectionMethod($this, 'do_fetch_array'); |
137 | $reflector = new \ReflectionMethod($this, 'do_fetch_array'); |
61 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
138 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
- | 139 | if ($isImplemented) { |
|
- | 140 | // do_fetch_array() is implemented. Use it. |
|
62 | if ($isImplemented) return $this->do_fetch_array(); |
141 | $ary = $this->do_fetch_array(); |
63 | 142 | } else { |
|
- | 143 | // Use the implementation of do_fetch_object() |
|
64 | $reflector = new \ReflectionMethod($this, 'do_fetch_object'); |
144 | $reflector = new \ReflectionMethod($this, 'do_fetch_object'); |
65 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
145 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
66 | if (!$isImplemented) { |
146 | if (!$isImplemented) { |
67 | throw new OIDplusException(_L("Class %1 is erroneous: At least one fetch-method needs to be overridden", get_class($this))); |
147 | throw new OIDplusException(_L("Class %1 is erroneous: At least one fetch-method needs to be overridden", get_class($this))); |
68 | } |
148 | } |
69 | - | ||
70 | // Convert object to array |
- | |
71 | $obj = $this->do_fetch_object(); |
149 | $obj = $this->do_fetch_object(); |
72 | if (!$obj) return null; |
150 | $ary = is_null($obj) ? null : stdobj_to_array($obj); |
- | 151 | } |
|
- | 152 | } |
|
73 | $ary = array(); |
153 | if (!is_null($ary)) { |
74 | foreach ($obj as $name => $val) { |
154 | $this->countAlreadyFetched++; |
75 | $ary[$name] = $val; |
155 | $this->fixFields($ary); |
76 | } |
156 | } |
77 | return $ary; |
157 | return $ary; |
78 | } |
158 | } |
79 | 159 | ||
80 | /** |
160 | /** |
- | 161 | * Please override do_fetch_object(), do_fetch_array(), or both. |
|
81 | * @return object|null |
162 | * @return object|null |
82 | */ |
163 | */ |
83 | protected function do_fetch_object()/*: ?object*/ { |
164 | protected function do_fetch_object()/*: ?\stdClass*/ { |
84 | assert(false); |
165 | assert(false); |
85 | return null; |
166 | return null; |
86 | } |
167 | } |
87 | 168 | ||
88 | /** |
169 | /** |
89 | * @return object|null |
170 | * @return object|null |
- | 171 | * @throws OIDplusConfigInitializationException |
|
- | 172 | * @throws OIDplusException |
|
- | 173 | * @throws \ReflectionException |
|
90 | */ |
174 | */ |
91 | public final function fetch_object()/*: ?object*/ { |
175 | public final function fetch_object()/*: ?\stdClass*/ { |
92 | if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)')); |
176 | if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)')); |
- | 177 | if (!is_null($this->prefetchedArray)) { |
|
- | 178 | // Prefetched value exists. Use it. |
|
- | 179 | $ary = array_shift($this->prefetchedArray); |
|
- | 180 | $obj = is_null($ary) ? null : array_to_stdobj($ary); |
|
93 | 181 | } else { |
|
94 | $reflector = new \ReflectionMethod($this, 'do_fetch_object'); |
182 | $reflector = new \ReflectionMethod($this, 'do_fetch_object'); |
95 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
183 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
- | 184 | if ($isImplemented) { |
|
- | 185 | // do_fetch_object() is implemented. Use it. |
|
96 | if ($isImplemented) return $this->do_fetch_object(); |
186 | $obj = $this->do_fetch_object(); |
97 | 187 | } else { |
|
- | 188 | // Use the implementation of do_fetch_array() |
|
98 | $reflector = new \ReflectionMethod($this, 'do_fetch_array'); |
189 | $reflector = new \ReflectionMethod($this, 'do_fetch_array'); |
99 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
190 | $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class); |
100 | if (!$isImplemented) { |
191 | if (!$isImplemented) { |
101 | throw new OIDplusException(_L("Class %1 is erroneous: At least one fetch-method needs to be overridden", get_class($this))); |
192 | throw new OIDplusException(_L("Class %1 is erroneous: At least one fetch-method needs to be overridden", get_class($this))); |
102 | } |
193 | } |
103 | - | ||
104 | // Convert array of object |
- | |
105 | $ary = $this->do_fetch_array(); |
194 | $ary = $this->do_fetch_array(); |
106 | if (!$ary) return null; |
195 | $obj = is_null($ary) ? null : array_to_stdobj($ary); |
- | 196 | } |
|
- | 197 | } |
|
107 | $obj = new \stdClass; |
198 | if (!is_null($obj)) { |
108 | foreach ($ary as $name => $val) { |
199 | $this->countAlreadyFetched++; |
109 | $obj->$name = $val; |
200 | $this->fixFields($obj); |
110 | } |
201 | } |
111 | return $obj; |
202 | return $obj; |
112 | } |
203 | } |
113 | 204 | ||
114 | /** |
205 | /** |
115 | * The any() function returns true if there is at least one |
206 | * The any() function returns true if there is at least one |
116 | * row in the section. By default, num_rows() will be used. |
207 | * row in the section. By default, num_rows() will be used. |
117 | * Plugins can override this method if they have a possibility |
208 | * Plugins can override this method if they have a possibility |
118 | * of making this functionality more efficient. |
209 | * of making this functionality more efficient. |
- | 210 | * |
|
119 | * @return bool |
211 | * @return bool |
- | 212 | * @throws OIDplusException |
|
120 | */ |
213 | */ |
121 | public function any(): bool { |
214 | public function any(): bool { |
122 | return $this->num_rows() > 0; |
215 | return $this->num_rows() > 0; |
123 | } |
216 | } |
- | 217 | ||
- | 218 | /** |
|
- | 219 | * @param string $dbField |
|
- | 220 | * @return void |
|
- | 221 | * @throws OIDplusConfigInitializationException |
|
- | 222 | * @throws OIDplusException |
|
- | 223 | * @throws \ReflectionException |
|
- | 224 | */ |
|
- | 225 | public final function naturalSortByField(string $dbField) { |
|
- | 226 | if (is_null($this->prefetchedArray)) { |
|
- | 227 | $this->prefetchAll(); |
|
- | 228 | } |
|
- | 229 | ||
- | 230 | // Sort $this->prefetchedArray by field $dbField |
|
- | 231 | natsort_field($this->prefetchedArray, $dbField); |
|
- | 232 | } |
|
124 | } |
233 | } |