Subversion Repositories oidplus

Rev

Rev 1461 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
236 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
236 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
511 daniel-mar 21
 
1086 daniel-mar 22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
730 daniel-mar 26
abstract class OIDplusQueryResult extends OIDplusBaseClass {
1116 daniel-mar 27
 
28
        /**
29
         * @return bool
30
         */
236 daniel-mar 31
        abstract public function containsResultSet(): bool;
1116 daniel-mar 32
 
33
        /**
34
         * @return int
35
         */
1152 daniel-mar 36
        abstract protected function do_num_rows(): int;
1116 daniel-mar 37
 
38
        /**
1156 daniel-mar 39
         * @var array|null
40
         */
41
        protected $prefetchedArray = null;
42
 
43
        /**
44
         * @var int
45
         */
46
        protected $countAlreadyFetched = 0;
47
 
48
        /**
1157 daniel-mar 49
         * Please override this method if the database driver can perform a "fetch all" in its own way
1156 daniel-mar 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
        /**
1152 daniel-mar 67
         * @return int
1156 daniel-mar 68
         * @throws OIDplusConfigInitializationException
69
         * @throws OIDplusException
1152 daniel-mar 70
         */
71
        public final function num_rows(): int {
72
                if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)'));
1156 daniel-mar 73
 
74
                if (!is_null($this->prefetchedArray)) {
1471 daniel-mar 75
                        return count($this->prefetchedArray);
1156 daniel-mar 76
                }
77
 
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;
1152 daniel-mar 83
        }
84
 
85
        /**
1156 daniel-mar 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.
1157 daniel-mar 94
                // We'll do it like MySQL does, although ODBC semms to be more correct.
1159 daniel-mar 95
                // We don't put this code into OIDplusQueryResultODBC.class.php, because other
96
                // DBMS might do the same - and then we would be prepared.
1156 daniel-mar 97
                foreach ($ret as &$value) {
98
                        if ($value === chr(0)) $value = 0;
99
                        if ($value === chr(1)) $value = 1;
100
                }
1316 daniel-mar 101
                unset($value);
1156 daniel-mar 102
 
1236 daniel-mar 103
                // Oracle and Firebird returns $ret['VALUE'] because unquoted column-names are always upper-case
1156 daniel-mar 104
                // We can't quote every single column throughout the whole program, so we use this workaround...
105
                if (is_array($ret)) {
1236 daniel-mar 106
                        foreach ($ret as $name => $val) {
107
                                $ret[strtolower($name)] = $val;
108
                                $ret[strtoupper($name)] = $val;
1156 daniel-mar 109
                        }
110
                } else if (is_object($ret)) {
111
                        foreach ($ret as $name => $val) {
112
                                $ret->{strtoupper($name)} = $val;
113
                                $ret->{strtolower($name)} = $val;
114
                        }
1236 daniel-mar 115
                } else {
116
                        assert(false);
1156 daniel-mar 117
                }
118
        }
119
 
120
        /**
121
         * Please override do_fetch_object(), do_fetch_array(), or both.
1116 daniel-mar 122
         * @return array|null
123
         */
1152 daniel-mar 124
        protected function do_fetch_array()/*: ?array*/ {
125
                assert(false);
126
                return null;
127
        }
1116 daniel-mar 128
 
129
        /**
1152 daniel-mar 130
         * @return array|null
1156 daniel-mar 131
         * @throws OIDplusConfigInitializationException
132
         * @throws OIDplusException
133
         * @throws \ReflectionException
1152 daniel-mar 134
         */
135
        public final function fetch_array()/*: ?array*/ {
136
                if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)'));
1156 daniel-mar 137
                if (!is_null($this->prefetchedArray)) {
138
                        // Prefetched value exists. Use it.
1471 daniel-mar 139
                        $ary = $this->prefetchedArray[$this->countAlreadyFetched] ?? null;
1156 daniel-mar 140
                } else {
141
                        $reflector = new \ReflectionMethod($this, 'do_fetch_array');
142
                        $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class);
143
                        if ($isImplemented) {
144
                                // do_fetch_array() is implemented. Use it.
145
                                $ary = $this->do_fetch_array();
146
                        } else {
147
                                // Use the implementation of do_fetch_object()
148
                                $reflector = new \ReflectionMethod($this, 'do_fetch_object');
149
                                $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class);
150
                                if (!$isImplemented) {
151
                                        throw new OIDplusException(_L("Class %1 is erroneous: At least one fetch-method needs to be overridden", get_class($this)));
152
                                }
153
                                $obj = $this->do_fetch_object();
154
                                $ary = is_null($obj) ? null : stdobj_to_array($obj);
155
                        }
1152 daniel-mar 156
                }
1156 daniel-mar 157
                if (!is_null($ary)) {
158
                        $this->countAlreadyFetched++;
159
                        $this->fixFields($ary);
1152 daniel-mar 160
                }
161
                return $ary;
162
        }
163
 
164
        /**
1156 daniel-mar 165
         * Please override do_fetch_object(), do_fetch_array(), or both.
1116 daniel-mar 166
         * @return object|null
167
         */
1156 daniel-mar 168
        protected function do_fetch_object()/*: ?\stdClass*/ {
1152 daniel-mar 169
                assert(false);
170
                return null;
171
        }
790 daniel-mar 172
 
1116 daniel-mar 173
        /**
1152 daniel-mar 174
         * @return object|null
1156 daniel-mar 175
         * @throws OIDplusConfigInitializationException
176
         * @throws OIDplusException
177
         * @throws \ReflectionException
1152 daniel-mar 178
         */
1156 daniel-mar 179
        public final function fetch_object()/*: ?\stdClass*/ {
1152 daniel-mar 180
                if (!$this->containsResultSet()) throw new OIDplusException(_L('The query has returned no result set (i.e. it was not a SELECT query)'));
1156 daniel-mar 181
                if (!is_null($this->prefetchedArray)) {
1164 daniel-mar 182
                        // Prefetched value exists (as array). Convert and use it.
1471 daniel-mar 183
                        $ary = $this->prefetchedArray[$this->countAlreadyFetched] ?? null;
1156 daniel-mar 184
                        $obj = is_null($ary) ? null : array_to_stdobj($ary);
185
                } else {
186
                        $reflector = new \ReflectionMethod($this, 'do_fetch_object');
187
                        $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class);
188
                        if ($isImplemented) {
189
                                // do_fetch_object() is implemented. Use it.
190
                                $obj = $this->do_fetch_object();
191
                        } else {
192
                                // Use the implementation of do_fetch_array()
193
                                $reflector = new \ReflectionMethod($this, 'do_fetch_array');
194
                                $isImplemented = ($reflector->getDeclaringClass()->getName() !== self::class);
195
                                if (!$isImplemented) {
196
                                        throw new OIDplusException(_L("Class %1 is erroneous: At least one fetch-method needs to be overridden", get_class($this)));
197
                                }
198
                                $ary = $this->do_fetch_array();
199
                                $obj = is_null($ary) ? null : array_to_stdobj($ary);
200
                        }
1152 daniel-mar 201
                }
1156 daniel-mar 202
                if (!is_null($obj)) {
203
                        $this->countAlreadyFetched++;
204
                        $this->fixFields($obj);
1152 daniel-mar 205
                }
206
                return $obj;
207
        }
208
 
209
        /**
1116 daniel-mar 210
         * The any() function returns true if there is at least one
211
         * row in the section. By default, num_rows() will be used.
212
         * Plugins can override this method if they have a possibility
213
         * of making this functionality more efficient.
1156 daniel-mar 214
         *
1116 daniel-mar 215
         * @return bool
1156 daniel-mar 216
         * @throws OIDplusException
1116 daniel-mar 217
         */
790 daniel-mar 218
        public function any(): bool {
219
                return $this->num_rows() > 0;
220
        }
1156 daniel-mar 221
 
222
        /**
223
         * @param string $dbField
224
         * @return void
225
         * @throws OIDplusConfigInitializationException
226
         * @throws OIDplusException
227
         * @throws \ReflectionException
228
         */
1204 daniel-mar 229
        public final function naturalSortByField(string $dbField) { // TODO: Argument asc or desc order
1156 daniel-mar 230
                if (is_null($this->prefetchedArray)) {
231
                        $this->prefetchAll();
232
                }
233
 
234
                // Sort $this->prefetchedArray by field $dbField
235
                natsort_field($this->prefetchedArray, $dbField);
236
        }
236 daniel-mar 237
}