libasynql  3.2.0
Asynchronous MySQL access library for PocketMine plugins.
MysqliThread.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * libasynql
5  *
6  * Copyright (C) 2018 SOFe
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 declare(strict_types=1);
22 
23 namespace poggit\libasynql\mysqli;
24 
25 use Closure;
27 use mysqli;
28 use mysqli_result;
29 use mysqli_stmt;
40 use function array_map;
41 use function assert;
42 use function bccomp;
43 use function bcsub;
44 use function gettype;
45 use function implode;
46 use function in_array;
47 use function is_float;
48 use function is_int;
49 use function is_string;
50 use function serialize;
51 use function strtotime;
52 use function unserialize;
53 
56  private $credentials;
57 
58  public static function createFactory(MysqlCredentials $credentials) : Closure{
59  return function(QuerySendQueue $bufferSend, QueryRecvQueue $bufferRecv) use ($credentials){
60  return new MysqliThread($credentials, $bufferSend, $bufferRecv);
61  };
62  }
63 
64  public function __construct(MysqlCredentials $credentials, QuerySendQueue $bufferSend = null, QueryRecvQueue $bufferRecv = null){
65  parent::__construct($bufferSend, $bufferRecv);
66  $this->credentials = serialize($credentials);
67  }
68 
69  protected function createConn(&$mysqli) : ?string{
71  $cred = unserialize($this->credentials, ["allowed_classes" => [MysqlCredentials::class]]);
72  try{
73  $mysqli = $cred->newMysqli();
74  return null;
75  }catch(SqlError $e){
76  return $e->getErrorMessage();
77  }
78  }
79 
80  protected function executeQuery($mysqli, int $mode, string $query, array $params) : SqlResult{
81  assert($mysqli instanceof mysqli);
82  if(empty($params)){
83  $result = $mysqli->query($query);
84  if($result === false){
85  throw new SqlError(SqlError::STAGE_EXECUTE, $mysqli->error, $query, []);
86  }
87  switch($mode){
91  if($result instanceof mysqli_result){
92  $result->close();
93  }
94  if($mode === SqlThread::MODE_INSERT){
95  return new SqlInsertResult($mysqli->affected_rows, $mysqli->insert_id);
96  }
97  if($mode === SqlThread::MODE_CHANGE){
98  return new SqlChangeResult($mysqli->affected_rows);
99  }
100  return new SqlResult();
101 
103  $ret = $this->toSelectResult($result);
104  $result->close();
105  return $ret;
106  }
107  }else{
108  $stmt = $mysqli->prepare($query);
109  if(!($stmt instanceof mysqli_stmt)){
110  throw new SqlError(SqlError::STAGE_PREPARE, $mysqli->error, $query, $params);
111  }
112  $types = implode(array_map(function($param) use ($query, $params){
113  if(is_string($param)){
114  return "s";
115  }
116  if(is_float($param)){
117  return "d";
118  }
119  if(is_int($param)){
120  return "i";
121  }
122  throw new SqlError(SqlError::STAGE_PREPARE, "Cannot bind value of type " . gettype($param), $query, $params);
123  }, $params));
124  $stmt->bind_param($types, ...$params);
125  if(!$stmt->execute()){
126  throw new SqlError(SqlError::STAGE_EXECUTE, $stmt->error, $query, $params);
127  }
128  switch($mode){
130  $ret = new SqlResult();
131  $stmt->close();
132  return $ret;
133 
135  $ret = new SqlChangeResult($stmt->affected_rows);
136  $stmt->close();
137  return $ret;
138 
140  $ret = new SqlInsertResult($stmt->affected_rows, $stmt->insert_id);
141  $stmt->close();
142  return $ret;
143 
145  $set = $stmt->get_result();
146  $ret = $this->toSelectResult($set);
147  $set->close();
148  return $ret;
149  }
150  }
151 
152  throw new InvalidArgumentException("Unknown mode $mode");
153  }
154 
155  private function toSelectResult(mysqli_result $result) : SqlSelectResult{
156  $columns = [];
157  $columnFunc = [];
158 
159  while(($field = $result->fetch_field()) !== false){
160  if($field->length === 1){
161  if($field->type === MysqlTypes::TINY){
162  $type = SqlColumnInfo::TYPE_BOOL;
163  $columnFunc[$field->name] = function($tiny){
164  return $tiny > 0;
165  };
166  }elseif($field->type === MysqlTypes::BIT){
167  $type = SqlColumnInfo::TYPE_BOOL;
168  $columnFunc[$field->name] = function($bit){
169  return $bit === "\1";
170  };
171  }
172  }
173  if($field->type === MysqlTypes::LONGLONG){
174  $type = SqlColumnInfo::TYPE_INT;
175  $columnFunc[$field->name] = function($longLong) use ($field){
176  if($field->flags & MysqlFlags::UNSIGNED_FLAG){
177  if(bccomp($longLong, "9223372036854775807") === 1){
178  $longLong = bcsub($longLong, "18446744073709551616");
179  }
180  return (int) $longLong;
181  }
182 
183  return (int) $longLong;
184  };
185  }elseif($field->flags & MysqlFlags::TIMESTAMP_FLAG){
187  $columnFunc[$field->name] = function($stamp){
188  return strtotime($stamp);
189  };
190  }elseif($field->type === MysqlTypes::NULL){
191  $type = SqlColumnInfo::TYPE_NULL;
192  }elseif(in_array($field->type, [
196  ], true)){
200  $columnFunc[$field->name] = "floatval";
202  $type = SqlColumnInfo::TYPE_INT;
203  $columnFunc[$field->name] = "intval";
204  }
205  if(!isset($type)){
207  }
208  $columns[$field->name] = new MysqlColumnInfo($field->name, $type, $field->flags, $field->type);
209  }
210 
211  $rows = [];
212  while(($row = $result->fetch_assoc()) !== null){
213  foreach($row as $col => &$cell){
214  if($cell !== null && isset($columnFunc[$col])){
215  $cell = $columnFunc[$col]($cell);
216  }
217  }
218  unset($cell);
219  $rows[] = $row;
220  }
221 
222  return new SqlSelectResult($columns, $rows);
223  }
224 
225  protected function close(&$mysqli) : void{
226  assert($mysqli instanceof mysqli);
227  $mysqli->close();
228  }
229 
230  public function getThreadName() : string{
231  return __NAMESPACE__ . " connector #$this->slaveNumber";
232  }
233 }
executeQuery($mysqli, int $mode, string $query, array $params)
static createFactory(MysqlCredentials $credentials)
__construct(MysqlCredentials $credentials, QuerySendQueue $bufferSend=null, QueryRecvQueue $bufferRecv=null)