1 | <?php |
||||||||
2 | /** |
||||||||
3 | * Plasma Driver MySQL component |
||||||||
4 | * Copyright 2018-2019 PlasmaPHP, All Rights Reserved |
||||||||
5 | * |
||||||||
6 | * Website: https://github.com/PlasmaPHP |
||||||||
7 | * License: https://github.com/PlasmaPHP/driver-mysql/blob/master/LICENSE |
||||||||
8 | */ |
||||||||
9 | |||||||||
10 | namespace Plasma\Drivers\MySQL\Commands; |
||||||||
11 | |||||||||
12 | /** |
||||||||
13 | * Abstract command which has a promise. |
||||||||
14 | * @internal |
||||||||
15 | */ |
||||||||
16 | abstract class PromiseCommand implements CommandInterface { |
||||||||
17 | use \Evenement\EventEmitterTrait; |
||||||||
18 | |||||||||
19 | /** |
||||||||
20 | * @var \Plasma\DriverInterface |
||||||||
21 | */ |
||||||||
22 | protected $driver; |
||||||||
23 | |||||||||
24 | /** |
||||||||
25 | * @var \React\Promise\Deferred |
||||||||
26 | */ |
||||||||
27 | protected $deferred; |
||||||||
28 | |||||||||
29 | /** |
||||||||
30 | * @var mixed |
||||||||
31 | */ |
||||||||
32 | protected $resolveValue; |
||||||||
33 | |||||||||
34 | /** |
||||||||
35 | * @var bool |
||||||||
36 | */ |
||||||||
37 | protected $finished = false; |
||||||||
38 | |||||||||
39 | /** |
||||||||
40 | * @var int|null |
||||||||
41 | */ |
||||||||
42 | protected $fieldsCount; |
||||||||
43 | |||||||||
44 | /** |
||||||||
45 | * Constructor. |
||||||||
46 | * @param \Plasma\DriverInterface $driver |
||||||||
47 | */ |
||||||||
48 | 62 | function __construct(\Plasma\DriverInterface $driver) { |
|||||||
49 | 62 | $this->driver = $driver; |
|||||||
50 | 62 | $this->deferred = new \React\Promise\Deferred(); |
|||||||
51 | |||||||||
52 | $this->once('error', function (\Throwable $error) { |
||||||||
53 | 1 | $this->deferred->reject($error); |
|||||||
54 | 62 | }); |
|||||||
55 | |||||||||
56 | $this->once('end', function () { |
||||||||
57 | // Let the event loop read the stream buffer before resolving |
||||||||
58 | $this->driver->getLoop()->futureTick(function () { |
||||||||
59 | 60 | $this->deferred->resolve($this->resolveValue); |
|||||||
60 | 60 | $this->resolveValue = null; |
|||||||
61 | 60 | }); |
|||||||
62 | 62 | }); |
|||||||
63 | 62 | } |
|||||||
64 | |||||||||
65 | /** |
||||||||
66 | * Get the encoded message for writing to the database connection. |
||||||||
67 | * @return string |
||||||||
68 | */ |
||||||||
69 | abstract function getEncodedMessage(): string; |
||||||||
70 | |||||||||
71 | /** |
||||||||
72 | * Get the promise. |
||||||||
73 | * @return \React\Promise\PromiseInterface |
||||||||
74 | */ |
||||||||
75 | 61 | function getPromise(): \React\Promise\PromiseInterface { |
|||||||
76 | 61 | return $this->deferred->promise(); |
|||||||
77 | } |
||||||||
78 | |||||||||
79 | /** |
||||||||
80 | * Sets the parser state, if necessary. If not, return `-1`. |
||||||||
81 | * @return int |
||||||||
82 | */ |
||||||||
83 | 61 | function setParserState(): int { |
|||||||
84 | 61 | return -1; |
|||||||
85 | } |
||||||||
86 | |||||||||
87 | /** |
||||||||
88 | * Sets the command as completed. This state gets reported back to the user. |
||||||||
89 | * @return void |
||||||||
90 | */ |
||||||||
91 | 60 | function onComplete(): void { |
|||||||
92 | 60 | $this->finished = true; |
|||||||
93 | 60 | $this->emit('end'); |
|||||||
94 | 60 | } |
|||||||
95 | |||||||||
96 | /** |
||||||||
97 | * Sets the command as errored. This state gets reported back to the user. |
||||||||
98 | * @param \Throwable $throwable |
||||||||
99 | * @return void |
||||||||
100 | */ |
||||||||
101 | 1 | function onError(\Throwable $throwable): void { |
|||||||
102 | 1 | $this->finished = true; |
|||||||
103 | |||||||||
104 | 1 | if($this->resolveValue !== null) { |
|||||||
105 | // Let the event loop read the stream buffer and resolve the promise before emitting |
||||||||
106 | // Works around a race condition, where the promise resolves |
||||||||
107 | // after receiving an error response packet |
||||||||
108 | // and therefore the user misses the error event |
||||||||
109 | $this->driver->getLoop()->futureTick(function () use (&$throwable) { |
||||||||
110 | $this->emit('error', array($throwable)); |
||||||||
111 | }); |
||||||||
112 | |||||||||
113 | return; |
||||||||
114 | } |
||||||||
115 | |||||||||
116 | 1 | $this->emit('error', array($throwable)); |
|||||||
117 | 1 | } |
|||||||
118 | |||||||||
119 | /** |
||||||||
120 | * Sends the next received value into the command. |
||||||||
121 | * @param mixed $value |
||||||||
122 | * @return void |
||||||||
123 | */ |
||||||||
124 | abstract function onNext($value): void; |
||||||||
125 | |||||||||
126 | /** |
||||||||
127 | * Whether the command has finished. |
||||||||
128 | * @return bool |
||||||||
129 | */ |
||||||||
130 | 60 | function hasFinished(): bool { |
|||||||
131 | 60 | return $this->finished; |
|||||||
132 | } |
||||||||
133 | |||||||||
134 | /** |
||||||||
135 | * Whether this command sets the connection as busy. |
||||||||
136 | * @return bool |
||||||||
137 | */ |
||||||||
138 | 61 | function waitForCompletion(): bool { |
|||||||
139 | 61 | return true; |
|||||||
140 | } |
||||||||
141 | |||||||||
142 | /** |
||||||||
143 | * Whether the sequence ID should be resetted. |
||||||||
144 | * @return bool |
||||||||
145 | */ |
||||||||
146 | abstract function resetSequence(): bool; |
||||||||
147 | |||||||||
148 | /** |
||||||||
149 | * Handles the column definitions of query commands on next caller. |
||||||||
150 | * @param \Plasma\BinaryBuffer $buffer |
||||||||
151 | * @param \Plasma\Drivers\MySQL\ProtocolParser $parser |
||||||||
152 | * @return \Plasma\ColumnDefinitionInterface|null |
||||||||
153 | */ |
||||||||
154 | 48 | function handleQueryOnNextCallerColumns(\Plasma\BinaryBuffer $buffer, \Plasma\Drivers\MySQL\ProtocolParser $parser): ?\Plasma\ColumnDefinitionInterface { |
|||||||
155 | 48 | if($this->fieldsCount === null) { |
|||||||
156 | 10 | $fieldCount = $buffer->readIntLength(); |
|||||||
157 | |||||||||
158 | 10 | if($fieldCount === 0x00) { |
|||||||
159 | $this->driver->getLoop()->futureTick(function () use (&$buffer, &$parser) { |
||||||||
160 | $msg = new \Plasma\Drivers\MySQL\Messages\OkResponseMessage($parser); |
||||||||
161 | $parser->handleMessage($buffer, $msg); |
||||||||
162 | }); |
||||||||
163 | |||||||||
164 | return null; |
||||||||
165 | 10 | } elseif($fieldCount === 0xFB) { |
|||||||
166 | // Handle it on future tick, so we can cleanly finish the buffer of this call |
||||||||
167 | $this->driver->getLoop()->futureTick(function () use (&$buffer, &$parser) { |
||||||||
168 | $msg = new \Plasma\Drivers\MySQL\Messages\LocalInFileRequestMessage($parser); |
||||||||
169 | $parser->handleMessage($buffer, $msg); |
||||||||
170 | }); |
||||||||
171 | |||||||||
172 | return null; |
||||||||
173 | 10 | } elseif($fieldCount === 0xFF) { |
|||||||
174 | $this->driver->getLoop()->futureTick(function () use (&$buffer, &$parser) { |
||||||||
175 | $msg = new \Plasma\Drivers\MySQL\Messages\ErrResponseMessage($parser); |
||||||||
176 | $parser->handleMessage($buffer, $msg); |
||||||||
177 | }); |
||||||||
178 | |||||||||
179 | return null; |
||||||||
180 | } |
||||||||
181 | |||||||||
182 | 10 | $this->fieldsCount = $fieldCount; |
|||||||
183 | 10 | return null; |
|||||||
184 | } |
||||||||
185 | |||||||||
186 | 48 | return static::parseColumnDefinition($buffer); |
|||||||
187 | } |
||||||||
188 | |||||||||
189 | /** |
||||||||
190 | * Parses the column definition. |
||||||||
191 | * @param \Plasma\BinaryBuffer $buffer |
||||||||
192 | * @return \Plasma\ColumnDefinitionInterface |
||||||||
193 | */ |
||||||||
194 | 48 | static function parseColumnDefinition(\Plasma\BinaryBuffer $buffer): \Plasma\ColumnDefinitionInterface { |
|||||||
195 | 48 | $buffer->readStringLength(); // catalog - always "def" |
|||||||
196 | 48 | $database = $buffer->readStringLength(); |
|||||||
197 | |||||||||
198 | 48 | $table = $buffer->readStringLength(); |
|||||||
199 | 48 | $buffer->readStringLength(); // orgTable |
|||||||
200 | |||||||||
201 | 48 | $name = $buffer->readStringLength(); |
|||||||
202 | 48 | $buffer->readStringLength(); // orgName |
|||||||
203 | |||||||||
204 | 48 | $buffer->read(1); // 0x0C |
|||||||
205 | |||||||||
206 | 48 | $charset = $buffer->readInt2(); |
|||||||
207 | 48 | $length = $buffer->readInt4(); |
|||||||
208 | 48 | $type = $buffer->readInt1(); |
|||||||
209 | 48 | $flags = $buffer->readInt2(); |
|||||||
210 | 48 | $decimals = $buffer->readInt1(); |
|||||||
211 | |||||||||
212 | 48 | $buffer->read(2); // fillers |
|||||||
213 | |||||||||
214 | /*if($this instanceof COM_FIELD_LIST) { |
||||||||
215 | $buffer->readStringLength(); |
||||||||
216 | }*/ |
||||||||
217 | |||||||||
218 | 48 | $charset = \Plasma\Drivers\MySQL\CharacterSetFlags::CHARSET_MAP[$charset] ?? null; |
|||||||
219 | 48 | $type = \Plasma\Drivers\MySQL\FieldFlags::TYPE_MAP[$type] ?? 'Unknown type "'.$type.'"'; |
|||||||
220 | |||||||||
221 | 48 | return (new \Plasma\Drivers\MySQL\ColumnDefinition($database, $table, $name, $type, $charset, $length, $flags, $decimals)); |
|||||||
0 ignored issues
–
show
Bug
introduced
by
![]() It seems like
$table can also be of type null ; however, parameter $name of Plasma\Drivers\MySQL\Col...finition::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() The call to
Plasma\Drivers\MySQL\Col...finition::__construct() has too many arguments starting with $decimals .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() It seems like
$name can also be of type null ; however, parameter $type of Plasma\Drivers\MySQL\Col...finition::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||
222 | } |
||||||||
223 | |||||||||
224 | /** |
||||||||
225 | * Parses the text resultset row and returns the row. |
||||||||
226 | * @param \Plasma\BinaryBuffer $buffer |
||||||||
227 | * @return array |
||||||||
228 | */ |
||||||||
229 | protected function parseResultsetRow(\Plasma\BinaryBuffer $buffer): array { |
||||||||
230 | $row = array(); |
||||||||
231 | |||||||||
232 | /** @var \Plasma\ColumnDefinitionInterface $column */ |
||||||||
233 | foreach($this->fields as $column) { |
||||||||
234 | $rawValue = $buffer->readStringLength(); |
||||||||
235 | |||||||||
236 | try { |
||||||||
237 | $value = \Plasma\Types\TypeExtensionsManager::getManager('driver-mysql') |
||||||||
238 | ->decodeType($column->getType(), $rawValue) |
||||||||
239 | ->getValue(); |
||||||||
240 | } catch (\Plasma\Exception $e) { |
||||||||
241 | $value = \Plasma\Drivers\MySQL\TextProtocolValues::decode($column, $rawValue); |
||||||||
242 | } |
||||||||
243 | |||||||||
244 | $row[$column->getName()] = $value; |
||||||||
245 | } |
||||||||
246 | |||||||||
247 | return $row; |
||||||||
248 | } |
||||||||
249 | } |
||||||||
250 |