1 | <?php |
||||||
2 | |||||||
3 | namespace Lagdo\DbAdmin\Db\Facades; |
||||||
4 | |||||||
5 | use Lagdo\DbAdmin\Command\LoggingService; |
||||||
6 | use Lagdo\DbAdmin\Command\TimerService; |
||||||
7 | use Lagdo\DbAdmin\Driver\Db\ConnectionInterface; |
||||||
8 | use Lagdo\DbAdmin\Driver\Entity\QueryEntity; |
||||||
9 | |||||||
10 | use function compact; |
||||||
11 | use function count; |
||||||
12 | use function function_exists; |
||||||
13 | use function ini_set; |
||||||
14 | use function max; |
||||||
15 | use function memory_get_usage; |
||||||
16 | use function strlen; |
||||||
17 | |||||||
18 | /** |
||||||
19 | * Facade to command functions |
||||||
20 | */ |
||||||
21 | class CommandFacade extends AbstractFacade |
||||||
22 | { |
||||||
23 | /** |
||||||
24 | * Connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) |
||||||
25 | * //! PDO - silent error |
||||||
26 | * |
||||||
27 | * @var ConnectionInterface |
||||||
28 | */ |
||||||
29 | protected $connection = null; |
||||||
30 | |||||||
31 | /** |
||||||
32 | * @var array |
||||||
33 | */ |
||||||
34 | protected $results; |
||||||
35 | |||||||
36 | /** |
||||||
37 | * @var float |
||||||
38 | */ |
||||||
39 | protected $duration; |
||||||
40 | |||||||
41 | /** |
||||||
42 | * Initialize the facade |
||||||
43 | * |
||||||
44 | * @param AbstractFacade $dbFacade |
||||||
45 | * @param TimerService $timer |
||||||
46 | * @param LoggingService|null $logging |
||||||
47 | */ |
||||||
48 | public function __construct(AbstractFacade $dbFacade, |
||||||
49 | protected TimerService $timer, protected LoggingService|null $logging) |
||||||
50 | { |
||||||
51 | parent::__construct($dbFacade); |
||||||
52 | } |
||||||
53 | |||||||
54 | /** |
||||||
55 | * Open a second connection to the server |
||||||
56 | * |
||||||
57 | * @return void |
||||||
58 | */ |
||||||
59 | private function createConnection() |
||||||
60 | { |
||||||
61 | // Connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) |
||||||
62 | //! PDO - silent error |
||||||
63 | if ($this->connection === null && $this->driver->database() !== '') { |
||||||
0 ignored issues
–
show
|
|||||||
64 | $this->connection = $this->driver ->newConnection( |
||||||
65 | $this->driver->database(), $this->driver->schema()); |
||||||
66 | } |
||||||
67 | } |
||||||
68 | |||||||
69 | /** |
||||||
70 | * @param array $row |
||||||
71 | * @param array $blobs |
||||||
72 | * @param array $types |
||||||
73 | * |
||||||
74 | * @return array |
||||||
75 | */ |
||||||
76 | protected function values(array $row, array $blobs, array $types): array |
||||||
77 | { |
||||||
78 | $values = []; |
||||||
79 | foreach ($row as $key => $value) { |
||||||
80 | // $link = $this->editLink($val); |
||||||
81 | if ($value === null) { |
||||||
82 | $value = '<i>NULL</i>'; |
||||||
83 | } elseif (isset($blobs[$key]) && $blobs[$key] && !$this->utils->str->isUtf8($value)) { |
||||||
84 | //! link to download |
||||||
85 | $value = '<i>' . $this->utils->trans->lang('%d byte(s)', strlen($value)) . '</i>'; |
||||||
86 | } else { |
||||||
87 | $value = $this->utils->str->html($value); |
||||||
88 | if (isset($types[$key]) && $types[$key] == 254) { // 254 - char |
||||||
89 | $value = "<code>$value</code>"; |
||||||
90 | } |
||||||
91 | } |
||||||
92 | $values[$key] = $value; |
||||||
93 | } |
||||||
94 | return $values; |
||||||
95 | } |
||||||
96 | |||||||
97 | /** |
||||||
98 | * @param mixed $statement |
||||||
99 | * @param int $limit |
||||||
100 | * |
||||||
101 | * @return string |
||||||
102 | */ |
||||||
103 | private function message($statement, int $limit): string |
||||||
104 | { |
||||||
105 | $numRows = $statement->rowCount(); |
||||||
106 | $message = ''; |
||||||
107 | if ($numRows > 0) { |
||||||
108 | if ($limit > 0 && $numRows > $limit) { |
||||||
109 | $message = $this->utils->trans->lang('%d / ', $limit); |
||||||
110 | } |
||||||
111 | $message .= $this->utils->trans->lang('%d row(s)', $numRows); |
||||||
112 | } |
||||||
113 | return $message; |
||||||
114 | } |
||||||
115 | |||||||
116 | /** |
||||||
117 | * Print select result |
||||||
118 | * From editing.inc.php |
||||||
119 | * |
||||||
120 | * @param mixed $statement |
||||||
121 | * @param int $limit |
||||||
122 | * |
||||||
123 | * @return array |
||||||
124 | */ |
||||||
125 | protected function select($statement, int $limit = 0): array |
||||||
126 | { |
||||||
127 | // No resultset |
||||||
128 | if ($statement === true) { |
||||||
129 | $affected = $this->driver->affectedRows(); |
||||||
130 | $message = $this->utils->trans |
||||||
131 | ->lang('Query executed OK, %d row(s) affected.', $affected); // . "$time"; |
||||||
132 | return [null, [$message]]; |
||||||
133 | } |
||||||
134 | // Fetch the first row. |
||||||
135 | if (!($row = $statement->fetchRow())) { |
||||||
136 | // Empty resultset. |
||||||
137 | $message = $this->utils->trans->lang('No rows.'); |
||||||
138 | return [null, [$message]]; |
||||||
139 | } |
||||||
140 | |||||||
141 | $blobs = []; // colno => bool - display bytes for blobs |
||||||
142 | $types = []; // colno => type - display char in <code> |
||||||
143 | $tables = []; // table => orgtable - mapping to use in EXPLAIN |
||||||
144 | $headers = []; |
||||||
145 | $details = []; |
||||||
146 | // Table headers. |
||||||
147 | $colCount = count($row); |
||||||
148 | for ($j = 0; $j < $colCount; $j++) { |
||||||
149 | $field = $statement->fetchField(); |
||||||
150 | // PostgreSQL fix: the table field can be missing. |
||||||
151 | $tables[$field->tableName()] = $field->orgTable(); |
||||||
152 | // $this->indexes($field); |
||||||
153 | if ($field->isBinary()) { |
||||||
154 | $blobs[$j] = true; |
||||||
155 | } |
||||||
156 | $types[$j] = $field->type(); // Some drivers don't set the type field. |
||||||
157 | $headers[] = $this->utils->str->html($field->name()); |
||||||
158 | } |
||||||
159 | |||||||
160 | // Table rows (the first was already fetched). |
||||||
161 | $rowCount = 0; |
||||||
162 | do { |
||||||
163 | $rowCount++; |
||||||
164 | $details[] = $this->values($row, $blobs, $types); |
||||||
165 | } while (($limit === 0 || $rowCount < $limit) && ($row = $statement->fetchRow())); |
||||||
166 | |||||||
167 | $message = $this->message($statement, $limit); |
||||||
168 | return [compact('tables', 'headers', 'details'), [$message]]; |
||||||
169 | } |
||||||
170 | |||||||
171 | /** |
||||||
172 | * @param QueryEntity $queryEntity |
||||||
173 | * |
||||||
174 | * @return bool |
||||||
175 | */ |
||||||
176 | private function executeCommand(QueryEntity $queryEntity): bool |
||||||
177 | { |
||||||
178 | if ($this->logging !== null) { |
||||||
179 | $this->logging->setCategoryToHistory(); |
||||||
180 | } |
||||||
181 | $this->timer->start(); |
||||||
182 | //! Don't allow changing of character_set_results, convert encoding of displayed query |
||||||
183 | if ($this->driver->multiQuery($queryEntity->query)) { |
||||||
184 | $this->driver->execUseQuery($queryEntity->query); |
||||||
185 | } |
||||||
186 | $this->duration += $this->timer->duration(); |
||||||
187 | |||||||
188 | do { |
||||||
189 | $select = null; |
||||||
190 | $errors = []; |
||||||
191 | $messages = []; |
||||||
192 | $statement = $this->driver->storedResult(); |
||||||
193 | |||||||
194 | if ($this->connection->hasError()) { |
||||||
0 ignored issues
–
show
The method
hasError() does not exist on Lagdo\DbAdmin\Driver\Db\ConnectionInterface . It seems like you code against a sub-type of Lagdo\DbAdmin\Driver\Db\ConnectionInterface such as Lagdo\DbAdmin\Driver\Db\Connection .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
195 | $errors[] = $this->connection->errorMessage(); |
||||||
0 ignored issues
–
show
The method
errorMessage() does not exist on Lagdo\DbAdmin\Driver\Db\ConnectionInterface . It seems like you code against a sub-type of Lagdo\DbAdmin\Driver\Db\ConnectionInterface such as Lagdo\DbAdmin\Driver\Db\Connection .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
196 | } elseif (!$queryEntity->onlyErrors) { |
||||||
197 | [$select, $messages] = $this->select($statement, $queryEntity->limit); |
||||||
198 | } |
||||||
199 | |||||||
200 | $result = compact('errors', 'messages', 'select'); |
||||||
201 | $result['query'] = $queryEntity->query; |
||||||
202 | $this->results[] = $result; |
||||||
203 | if ($this->connection->hasError() && $queryEntity->errorStops) { |
||||||
204 | return false; |
||||||
205 | } |
||||||
206 | } while ($this->driver->nextResult()); |
||||||
207 | |||||||
208 | return true; |
||||||
209 | } |
||||||
210 | |||||||
211 | /** |
||||||
212 | * Execute a set of queries |
||||||
213 | * |
||||||
214 | * @param string $queries The queries to execute |
||||||
215 | * @param int $limit The max number of rows to return |
||||||
216 | * @param bool $errorStops Stop executing the requests in case of error |
||||||
217 | * @param bool $onlyErrors Return only errors |
||||||
218 | * |
||||||
219 | * @return array |
||||||
220 | */ |
||||||
221 | public function executeCommands(string $queries, int $limit, bool $errorStops, bool $onlyErrors): array |
||||||
222 | { |
||||||
223 | if (function_exists('memory_get_usage')) { |
||||||
224 | // @ - may be disabled, 2 - substr and trim, 8e6 - other variables |
||||||
225 | try { |
||||||
226 | ini_set('memory_limit', max($this->admin->iniBytes('memory_limit'), |
||||||
227 | 2 * strlen($queries) + memory_get_usage() + 8e6)); |
||||||
228 | } |
||||||
229 | catch(\Exception $e) { |
||||||
230 | // Do nothing if the option is not modified. |
||||||
231 | } |
||||||
232 | } |
||||||
233 | |||||||
234 | // The second connection must be created before executing the queries. |
||||||
235 | $this->createConnection(); |
||||||
236 | |||||||
237 | $this->results = []; |
||||||
238 | $this->duration = 0; |
||||||
239 | $commands = 0; |
||||||
240 | $errors = 0; |
||||||
241 | $queryEntity = new QueryEntity($queries, $limit, $errorStops, $onlyErrors); |
||||||
242 | while ($this->driver->parseQueries($queryEntity)) { |
||||||
243 | $commands++; |
||||||
244 | if (!$this->executeCommand($queryEntity)) { |
||||||
245 | $errors++; |
||||||
246 | if ($errorStops) { |
||||||
247 | break; |
||||||
248 | } |
||||||
249 | } |
||||||
250 | } |
||||||
251 | |||||||
252 | $messages = []; |
||||||
253 | if ($commands === 0) { |
||||||
254 | $messages[] = $this->utils->trans->lang('No commands to execute.'); |
||||||
255 | } elseif ($onlyErrors) { |
||||||
256 | $messages[] = $this->utils->trans->lang('%d query(s) executed OK.', $commands - $errors); |
||||||
257 | } |
||||||
258 | return [ |
||||||
259 | 'results' => $this->results, |
||||||
260 | 'messages' => $messages, |
||||||
261 | 'duration' => $this->duration, |
||||||
262 | ]; |
||||||
263 | } |
||||||
264 | } |
||||||
265 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.