├── LICENSE ├── README.md ├── UsagiMQ.php ├── composer.json ├── example ├── mq.php ├── mqwithworker.php ├── publisher.php ├── subscriber.php ├── worker_insert_1.php ├── worker_insert_2.php └── worker_insert_3.php ├── favicon.ico └── visio ├── MQ.jpg ├── WebService.jpg ├── WebService.vsdx ├── logo.png ├── logo2.png └── uitable.jpg /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Escuela de Formación Técnica Chile SPA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](visio/logo.png "logo") 2 | 3 | # UsagiMQ 4 | A minimalist (less than 500 lines of code) Message Queue by using Redis and PHP in a single box (one class) 5 | 6 | 7 | 8 | ## Why I should use a Message Queue (MQ)? 9 | 10 | Let’s say the next example, a system where one system sends information to another, for example a web client and a web service. 11 | 12 | If the webservice is doing a **slow operation and its receiving the information of many clients** at once then, sooner or later, the system could collapses or bottleneck. 13 | 14 | For example, if every client uses 0.1 second to do the operation (receiving the information and storing in the database), and we have 1000 customers then, every operation could take 1.6 minutes. 15 | 16 | 17 | ![Web Service](visio/WebService.jpg "Web Service") 18 | 19 | The solution is to add a Message Queue to the system. A Message Queue is only a server that stores messages/operations received by a PUBLISHER and later a SUBSCRIBER could execute. 20 | 21 | For the same example, a PUBLISHER (former client) could uses 0.001 to call the MQ. Then the SUBSCRIBER could do all the operations, for example every hour/at night without worry if all the operations take many minutes or hours. 22 | 23 | 24 | 25 | 26 | ![MQ](visio/MQ.jpg "MQ") 27 | 28 | The MQ should be as fast as possible and be able to listen and store the request (envelope) of every publisher. However, the MQ is not doing the end operation, its similar to a email server. 29 | Later, a subscriber could read this information and process as correspond. 30 | 31 | The drawback of this method is it adds a delay, the process is not executed synchronously but asynchronously, and the PUBLISHER don't know really if the information was processed correctly by the SUBSCRIBER. 32 | 33 | ## Considerations 34 | 35 | This library uses Redis. Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. 36 | 37 | ## Why UsagiMQ? 38 | 39 | While there are many Message Queue in the market (including open source / freeware / commercial) but most of them are heavyweight. 40 | UsagiMQ is lightweight, it was developed thinking in customization. You could optimize and customize it for your needing, for example, changing the structure of envelope. 41 | 42 | It is a software based solution that could be **re-programmed** as you want to. Instead, most MQ are **configured-based**. 43 | 44 | For example, you could create easily a ORCHESTATION, CHOREOGRAPHY or A CLUSTER via software. 45 | 46 | UsagiMQ lightweight: 47 | 48 | - One class, it requires Redis and nothing more. 49 | - < 500 lines of code. 50 | - Easy customization. 51 | - **It requires a single file (UsagiMQ.php)** 52 | 53 | ## Is it scalable? 54 | 55 | Yes, but it requires a LOAD BALANCER that you could program or use a software or hardware solution. Its your call. 56 | 57 | ## Envelope structure 58 | 59 | - id = the identified of the envelope (required). 60 | - from = who send the envelope (optional) 61 | - body = the content of the envelope (required). 62 | - date = date when it was received. (required, generated) 63 | - try = number of tries. (required, for use future, generated) 64 | 65 | 66 | ## MQ Server 67 | 68 | Who is listening and storing the envelope (request). It should be as fast as possible. 69 | 70 | Example: 71 | ``` 72 | include "UsagiMQ.php"; 73 | $usa=new UsagiMQ("127.0.0.1",6379,1); 74 | if ($usa->connected) { 75 | $info=$usa->receive(); 76 | if ($info=='NO INFO') { 77 | $usa->webUI(); // if not information is send, then it opens the UI. It is optional 78 | } else { 79 | echo $info; // show the result. 80 | } 81 | } else { 82 | echo "not connected"; 83 | } 84 | ``` 85 | 86 | Example MQ server that calls a worker (subscriber). See example : mqwithworker.php 87 | 88 | 89 | ### Publisher (who sends the request/envelope) 90 | 91 | A publisher requires to send a POST to the page. So it could be a CURL, JavaScript, PHP, Java, C# and many more. 92 | 93 | The url should be as follow: 94 | mq.php?id=**ID**&op=**OP**&from=**FROM** 95 | 96 | while the body (content) should be sends as POST. 97 | 98 | - ID = is an identified of the message (required but not specifically should be unique) 99 | - OP = the operation to do. Its some sort of folder or category. 100 | - FROM = who is sending the message. It could be used for authentication. 101 | 102 | 103 | ``` 104 | connected) { 133 | echo "not connected"; 134 | die(1); 135 | } 136 | 137 | $listEnveloper=$usa->listPending("insert"); 138 | 139 | foreach($listEnveloper as $id) { 140 | $env=$usa->readItem($id); 141 | var_dump($env); 142 | // todo: code goes here 143 | 144 | // $correct indicates if the operation was successful or not. For example, if the operation was to insert and the operation failed. 145 | // We also could decide to delete it for any case. Its up to us. 146 | if ($correct) { 147 | $usa->deleteItem($id); // YAY! 148 | } else { 149 | $usa->failedItem($id,$env); // booh hiss!. 150 | } 151 | } 152 | ``` 153 | 154 | # Commands 155 | 156 | ## Constructor 157 | 158 | > $usa=new UsagiMQ($IPREDIS,$PORT,$DATABASE); 159 | 160 | $IPREDIS indicates the IP of where is the Redis server. 161 | $PORT (optional) indicates the REDIS port (default value 6379) 162 | $DATABASE (optional), indicates the database of Redis (0 is the default value) 163 | ## receive() 164 | 165 | Receive information from the PUBLISHER. 166 | 167 | ## listPending($op) 168 | 169 | List all keys of pending envelopers per operation. 170 | 171 | example: 172 | 173 | > $array=$usa->listPending('insert'); 174 | 175 | > $array=$usa->listPending('update'); 176 | 177 | 178 | ## readItem($key) 179 | 180 | Read an item. When an item is read, its not deleted. It should be deleted once its processed. 181 | The key could be obtained by the command listPending. 182 | 183 | Example: 184 | > $usa->readItem('UsagiMQ_insert:2'); 185 | 186 | 187 | ## deleteItem($key) 188 | 189 | Delete an item. 190 | The key could be obtained by the command listPending. 191 | Example: 192 | > $usa->deleteItem('UsagiMQ_insert:2'); 193 | 194 | ## failedItem($key,$arr) 195 | 196 | We mark the item as failed. A failed item, is a item that we will try it again (until a limit). If we failed in all tries then, we will delete the item. 197 | - The key could be obtained by the command listPending. 198 | - $arr is the array with the envelope [id,from,body,date,try] 199 | Example: 200 | > $usa->failedItem('UsagiMQ_insert:2',array('id'=>2,'from'=>'system','body'=>'xxx','date'=>'2000-01-01','try'=>0)); 201 | 202 | 203 | ## deleteAll() 204 | 205 | Delete all items and resets the counter. 206 | 207 | ## close() 208 | 209 | Close Redis. Its not required to use. 210 | 211 | ## showUI() 212 | 213 | Optional. It shows an UI with statistics. The default user and password is **admin** and you should change it. 214 | 215 | ![logo](visio/uitable.jpg "uitable") 216 | 217 | # versions 218 | 219 | - 2017-11-12 1.0 first version 220 | - 2017-11-14 1.1 add new members and fixed the operation listPending. Now, its sorted. 221 | - 2017-11-19 1.2 add UI 222 | - 2017-11-21 1.3 add new functionalities for the UI. The code is still lower than 500 lines of code. 223 | - 2018-08-06 1.4 some fixes 224 | 225 | ## Todo 226 | 227 | - Error control / Log 228 | - Readme missing the command 229 | - Readme missing the PUBLISHER. 230 | 231 | -------------------------------------------------------------------------------- /UsagiMQ.php: -------------------------------------------------------------------------------- 1 | redis = new Redis(); 50 | $ok=@$this->redis->pconnect($redisIP, $redisPort, 5); // 5 sec timeout. 51 | if (!$ok) { 52 | $this->redis=null; 53 | $this->debugFile("Unable to open redis $redisIP : $redisPort",'__construct'); 54 | return; 55 | } 56 | $this->user=$user; 57 | $this->password=$password; 58 | @$this->redis->select($redisDB); 59 | $this->connected=true; 60 | } catch (Exception $ex) { 61 | $this->connected=false; 62 | $this->debugFile($ex->getMessage(),'__construct'); 63 | } 64 | } 65 | 66 | /** 67 | * receive a new envelope via post http://myserver/mq.php?id=&op=&from= (and post info) 68 | * @return string OKI if the operation was successful, otherwise it returns the error. 69 | */ 70 | public function receive() { 71 | try { 72 | $post = file_get_contents('php://input'); 73 | $id = @$_GET['id']; 74 | $this->op = @$_GET['op']; // operation. 75 | $from = @$_GET['from']; // security if any (optional) 76 | if (strlen($id)>1000 || strlen($this->op)>1000 || strlen($from)>1000 || strlen($post)>self::MAXPOST) { 77 | // avoid overflow. 78 | return "BAD INFO"; 79 | } 80 | if (empty($post) || empty($this->op) || empty($id)) { 81 | return "NO INFO"; 82 | } 83 | $counter =$this->redis->incr('counterUsagiMQ'); 84 | $envelope = array(); 85 | $envelope['id'] = $id; // this id is not the id used by the library. This could be repeated. 86 | $envelope['from'] = $from; // it could be used for security 87 | $envelope['body'] = $post; 88 | $envelope['date'] = time(); 89 | $envelope['try'] = 0; // use future. 90 | $this->key="UsagiMQ_{$this->op}:" . $counter; // the key is unique and is composed by the operator and a counter. 91 | $ok = $this->redis->set($this->key, json_encode($envelope), self::MAXTIMEKEEP); // 24 hours 92 | if ($ok) { 93 | return "OKI"; 94 | } 95 | $msg='Error reciving information'; 96 | $this->debugFile($msg,'receive'); 97 | return $msg; 98 | } catch (Exception $ex) { 99 | $this->debugFile($ex->getMessage(),'receive'); 100 | return $ex->getMessage(); 101 | } 102 | } 103 | 104 | /** 105 | * List with all ids of envelop pending. 106 | * @param $op 107 | * @return array 108 | */ 109 | public function listPending($op) { 110 | $it = NULL; 111 | $redisKeys=array(); 112 | while($arr_keys = $this->redis->scan($it, "UsagiMQ_{$op}:*", 1000)) { // 1000 read at the same time. 113 | foreach($arr_keys as $str_key) { 114 | $redisKeys[]=$str_key; 115 | } 116 | } 117 | natsort($redisKeys); // why sort,because we use scan/set and it doesn't sort. zadd is another alternative but it lacks of ttl 118 | return $redisKeys; 119 | } 120 | 121 | /** 122 | * @param string $key Key of the envelope. 123 | * @return array Returns an array with the form of an envelope[id,from,body,date,try] 124 | */ 125 | public function readItem($key) { 126 | return json_decode($this->redis->get($key), true); 127 | } 128 | 129 | /** 130 | * The item failed. So we will try it again soon. 131 | * @param string $key Key of the envelope. 132 | * @param array $arr . The envelope [id,from,body,date,try] 133 | */ 134 | public function failedItem($key,$arr) { 135 | try { 136 | $arr['try']++; 137 | if ($arr['try'] > self::MAXTRY) { 138 | $this->deleteItem($key); // we did the best but we failed. 139 | return; 140 | } 141 | $this->redis->set($key, json_encode($arr), self::MAXTIMEKEEP); 142 | } catch(Exception $ex) { 143 | $this->debugFile($ex->getMessage(),'failedItem'); 144 | } 145 | } 146 | 147 | /** 148 | * Delete an envelope 149 | * @param string $key Key of the envelope. 150 | */ 151 | public function deleteItem($key) { 152 | try { 153 | $this->redis->delete($key); 154 | } catch(Exception $ex) { 155 | $this->debugFile($ex->getMessage(),'deleteItem'); 156 | } 157 | } 158 | 159 | /** 160 | * Delete all envelope and reset the counters. 161 | */ 162 | function deleteAll() { 163 | try { 164 | $it = NULL; 165 | while($arr_keys = $this->redis->scan($it, "UsagiMQ_*", 10000)) { 166 | foreach($arr_keys as $v) { 167 | $this->redis->delete($v); 168 | } 169 | } 170 | $this->redis->set('counterUsagiMQ',"0"); 171 | } catch(Exception $ex) { 172 | $this->debugFile($ex->getMessage(),'deleteAll'); 173 | } 174 | } 175 | 176 | /** 177 | * Close redis. 178 | */ 179 | function close() { 180 | try { 181 | $this->redis->close(); 182 | } catch(Exception $ex) { 183 | $this->debugFile($ex->getMessage(),'close'); 184 | } 185 | } 186 | public function logFilename() { 187 | $folder=(@ini_get('error_log')!="")?dirname(ini_get('error_log')):''; 188 | $file=$folder.'\\'.self::LOGFILE; 189 | return $file; 190 | } 191 | 192 | public function debugFile($txt,$type="ERROR") { 193 | if (self::LOGFILE=='') { 194 | return; 195 | } 196 | $file=$this->logFilename(); 197 | $fz=@filesize($file); 198 | 199 | if (is_object($txt) || is_array($txt)) { 200 | $txtW=print_r($txt,true); 201 | } else { 202 | $txtW=$txt; 203 | } 204 | 205 | if ($fz>100000) { 206 | // more than 100kb, reduces it. 207 | $fp = @fopen($file, 'w'); 208 | } else { 209 | $fp = @fopen($file, 'a'); 210 | } 211 | if (!$fp) { 212 | die(1); 213 | } 214 | $today=new DateTime(); 215 | $finalMsg=$today->format('Y-m-d H:i:s')."\t[{$type}:]\t{$txtW}\n"; 216 | if ($type=='ERROR') { 217 | @$this->redis->set('LastErrorUsagiMQ', $finalMsg); 218 | } else { 219 | @$this->redis->set('LastMessageUsagiMQ',$finalMsg); 220 | } 221 | fwrite($fp,$finalMsg); 222 | fclose($fp); 223 | } 224 | public function webUI() { 225 | @session_start(); 226 | $mode=@$_REQUEST['mode']; 227 | $curUser=@$_SESSION['user']; 228 | if ($curUser=='') { 229 | if ($mode=='login') { 230 | $info['user']=@$_POST['user']; 231 | $info['password']=@$_POST['password']; 232 | $info['msg']=''; 233 | $button=@$_POST['button']; 234 | if ($button) { 235 | sleep(1); 236 | if ($info['user']!=$this->user || $info['password']!=$this->password) { 237 | $info['msg']="User or login incorrect"; 238 | $this->loginForm($info); 239 | @session_destroy(); 240 | } else { 241 | $_SESSION['user']=$info['user']; 242 | $this->tableForm(); 243 | } 244 | } else { 245 | @session_destroy(); 246 | $this->loginForm($info); 247 | } 248 | } else { 249 | $this->loginForm(array()); 250 | } 251 | } else { 252 | switch ($mode) { 253 | case 'clear': 254 | $this->deleteAll(); 255 | $this->tableForm(); 256 | break; 257 | case 'refresh': 258 | $this->tableForm(); 259 | break; 260 | case 'execute': 261 | $this->tableForm(); 262 | return "execute"; 263 | break; 264 | case 'logout': 265 | @session_destroy(); 266 | $this->loginForm(array()); 267 | break; 268 | case 'showall': 269 | if (self::LOGFILE=='') { 270 | $this->tableForm(); 271 | return ""; 272 | } 273 | $file=$this->logFilename(); 274 | $fc=file_get_contents($file); 275 | $fc=str_replace("\n",'
',$fc); 276 | $this->tableForm($fc); 277 | break; 278 | case 'deletelog': 279 | @$this->redis->set('LastErrorUsagiMQ', ""); 280 | @$this->redis->set('LastMessageUsagiMQ',""); 281 | if (self::LOGFILE=='') { 282 | $this->tableForm(); 283 | return ""; 284 | } 285 | $file=$this->logFilename(); 286 | $fp = @fopen($file, 'w'); 287 | @fclose($fp); 288 | $this->debugFile("Log deleted","DEBUG"); 289 | 290 | $this->tableForm(); 291 | break; 292 | default: 293 | $this->tableForm(); 294 | } 295 | } 296 | return ""; 297 | } 298 | private function loginForm($info) { 299 | $this->cssForm(); 300 | echo " 301 |
302 | 303 | 304 | 305 | 306 | 307 | 308 |
UsagiMQ 
User:
Password:
".@$info['msg']."
309 | 310 |
311 | "; 312 | } 313 | private function tableForm($lastMessage='') { 314 | $counter = @$this->redis->get('counterUsagiMQ'); 315 | $lastError = @$this->redis->get('LastErrorUsagiMQ'); 316 | $curUser=@$_SESSION['user']; 317 | $lastMessage =($lastMessage=='')?@$this->redis->get('LastMessageUsagiMQ'):$lastMessage; 318 | $num=0; 319 | $pending=""; 320 | while($arr_keys = $this->redis->scan($it, "UsagiMQ_*", 1000)) { // 1000 read at the same time. 321 | foreach($arr_keys as $str_key) { 322 | $num++; 323 | $pending.=$str_key."
"; 324 | } 325 | } 326 | $this->cssForm(); 327 | $myurl=$_SERVER["SCRIPT_NAME"]; 328 | $today=new DateTime(); 329 | echo " 330 | 331 | 335 | 336 | 337 | 340 | 341 | 344 |
UsagiMQ{$curUser}@UsagiMQ 332 | refresh     333 | Logout 334 |
Version:".self::VERSION." Current Date :".$today->format('Y-m-d H:i:s')."
Counter:$counter
Pending: $num$pending 338 | Run Pending    339 | Clear
Last Error:".htmlentities($lastError)."
Last Message:$lastMessage 342 | Show All 343 |     Delete log
345 | "; 346 | 347 | } 348 | private function cssForm() { 349 | header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); 350 | header("Cache-Control: post-check=0, pre-check=0", false); 351 | header("Pragma: no-cache"); 352 | echo " 353 | UsagiMQ 354 | 355 | 402 | "; 403 | } 404 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eftec/usagimq", 3 | "description": "The standalone version Blade Template Engine from Laravel in a single php file", 4 | "type": "library", 5 | "keywords": ["blade","template","view"], 6 | "homepage": "https://github.com/EFTEC/UsagiMQ", 7 | "minimum-stability": "beta", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Jorge Patricio Castro Castillo", 12 | "email": "jcastro@eftec.cl" 13 | }], 14 | "require": { 15 | "php": ">=5.4" 16 | }, 17 | "extra": { 18 | "branch-alias": { 19 | "dev-master": "1.4-dev" 20 | } 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "eftec\\": "vendor/eftec/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /example/mq.php: -------------------------------------------------------------------------------- 1 | connected) { 6 | $info=$usa->receive(); 7 | if ($info=='NO INFO') { 8 | $refresh=$usa->webUI(); // if not information is send, then it opens the UI. It is optional 9 | if ($refresh) { 10 | //todo: call the subscriber(s) 11 | } 12 | } else { 13 | echo $info; // show the result. 14 | } 15 | } else { 16 | echo "not connected"; 17 | } 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/mqwithworker.php: -------------------------------------------------------------------------------- 1 | connected) { 6 | echo $usa->receive(); 7 | if ($usa->key) { 8 | // method 1: calling a random worker. 9 | // its an example, if the operation is insert, then we call an random worker. In this case, its a local worker but it could be located in a different server 10 | if ($usa->op=='insert') { 11 | curlAsync('worker_insert_'.rand(1,3).'.php?key='.$usa->key); 12 | } 13 | // method 2: load balance 14 | if ($usa->op=='insert') { 15 | $ic=$usa->redis->incr('INSERTCOUNTER'); 16 | if ($ic>=3) { 17 | $ic=0; 18 | $usa->redis->set('INSERTCOUNTER',$ic); 19 | } 20 | curlAsync('worker_insert_'.($ic+1).'.php?key='.$usa->key); 21 | } 22 | } 23 | } else { 24 | echo "not connected"; 25 | } 26 | 27 | 28 | /** 29 | * This operation returns nothing and takes 1ms. 30 | * @param string $url relative path. 31 | */ 32 | function curlAsync($url) { 33 | $actuallink = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; 34 | $cleanLink=substr($actuallink ,0,strrpos($actuallink,'/')).'/'.$url; 35 | $ch = curl_init(); 36 | curl_setopt($ch, CURLOPT_URL, $cleanLink); 37 | curl_setopt($ch, CURLOPT_FRESH_CONNECT, true); 38 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, 1); // for php >5.2.3 39 | curl_setopt($ch, CURLOPT_NOSIGNAL, 1); 40 | 41 | curl_exec($ch); 42 | curl_close($ch); 43 | } -------------------------------------------------------------------------------- /example/publisher.php: -------------------------------------------------------------------------------- 1 | connected) { 8 | echo "not connected"; 9 | die(1); 10 | } 11 | 12 | $listEnveloper=$usa->listPending("insert"); 13 | 14 | foreach($listEnveloper as $id) { 15 | $env=$usa->readItem($id); 16 | var_dump($env); 17 | // todo: code goes here 18 | $correct=true; 19 | 20 | // $correct indicates if the operation was successful or not. For example, if the operation was to insert and the operation failed. 21 | // We also could decide to delete it for any case. Its up to us. 22 | if ($correct) { 23 | $usa->deleteItem($id); // YAY! 24 | } else { 25 | $usa->failedItem($id,$env); // booh hiss!. 26 | } 27 | } -------------------------------------------------------------------------------- /example/worker_insert_1.php: -------------------------------------------------------------------------------- 1 | connected) { 8 | echo "not connected"; 9 | die(1); 10 | } 11 | $key=@$_GET['key']; 12 | if ($key) { 13 | $envelope = $usa->readItem($key); 14 | //todo: here we do the task. 15 | } -------------------------------------------------------------------------------- /example/worker_insert_2.php: -------------------------------------------------------------------------------- 1 | connected) { 8 | echo "not connected"; 9 | die(1); 10 | } 11 | $key=@$_GET['key']; 12 | if ($key) { 13 | $envelope = $usa->readItem($key); 14 | //todo: here we do the task. 15 | } -------------------------------------------------------------------------------- /example/worker_insert_3.php: -------------------------------------------------------------------------------- 1 | connected) { 8 | echo "not connected"; 9 | die(1); 10 | } 11 | $key=@$_GET['key']; 12 | if ($key) { 13 | $envelope = $usa->readItem($key); 14 | //todo: here we do the task. 15 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/favicon.ico -------------------------------------------------------------------------------- /visio/MQ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/visio/MQ.jpg -------------------------------------------------------------------------------- /visio/WebService.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/visio/WebService.jpg -------------------------------------------------------------------------------- /visio/WebService.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/visio/WebService.vsdx -------------------------------------------------------------------------------- /visio/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/visio/logo.png -------------------------------------------------------------------------------- /visio/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/visio/logo2.png -------------------------------------------------------------------------------- /visio/uitable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFTEC/UsagiMQ/53083cd91d024f2c8bd039ac697e1c69f304a3d3/visio/uitable.jpg --------------------------------------------------------------------------------