├── .gitignore ├── README.md ├── composer.json ├── doc ├── benchmarks.md ├── db.md ├── pdo_test.sql └── settings.ini.php ├── favicon.ico ├── index.php ├── server.sh ├── src ├── Controller │ ├── CtrHello.php │ ├── CtrPdo.php │ └── CtrRedis.php ├── Core │ ├── YcfCore.php │ ├── YcfDB.php │ ├── YcfDBServer.php │ ├── YcfHttpServer.php │ ├── YcfLog.php │ ├── YcfRedis.php │ ├── YcfUtils.php │ └── settings.ini.php ├── Model │ ├── ModelBase.php │ ├── ModelPdo.php │ ├── ModelProxyDb.php │ ├── ModelRedis.php │ └── ModelTask.php └── runtime │ └── .gitignore └── vendor ├── autoload.php └── composer ├── ClassLoader.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php └── installed.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | composer.lock 3 | src/Core/settings.ini.php 4 | 5 | vendor/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##queueSwoole 2 | 3 | 4 | ##目标 5 | * 基于swoole实现排队抢购系统,适用于高并发场景 6 | * 对性能要求极高,qps至少同配置php-fpm一倍以上 7 | 8 | ##难点 9 | * 库存控制 10 | * 排队公平性 11 | * 稳定性 12 | 13 | ##设计文档 14 | * [架构图](https://github.com/kcloze/queueSwoole/blob/master/project.md) 15 | 16 | 17 | ##启动 18 | ``` 19 | chmod u+x server.sh 20 | ./server.sh start|stop 21 | 22 | ``` 23 | 24 | ##压测 25 | 26 | 27 | 28 | 29 | ##感谢 30 | 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kcloze/ycf", 3 | "description": "A simple PHP framework for api or cli application", 4 | "keywords": ["microframework","api","cli"], 5 | "homepage": "https://www.kcloze.com", 6 | "license": "MIT", 7 | "require": { 8 | "php": ">=5.3.0" 9 | }, 10 | "require-dev": { 11 | "php": ">=5.3.0" 12 | }, 13 | "minimum-stability": "stable", 14 | "autoload": { 15 | "psr-4": { 16 | "Ycf\\": "src" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /doc/benchmarks.md: -------------------------------------------------------------------------------- 1 | 2 | ## Benchmarks 3 | ####1. computer and config info: 4 | ``` 5 | Architecture: x86_64 6 | CPU op-mode(s): 32-bit, 64-bit 7 | Byte Order: Little Endian 8 | CPU(s): 2 9 | On-line CPU(s) list: 0,1 10 | Thread(s) per core: 1 11 | Core(s) per socket: 2 12 | Socket(s): 1 13 | NUMA node(s): 1 14 | Vendor ID: GenuineIntel 15 | CPU family: 6 16 | Model: 60 17 | Stepping: 3 18 | CPU MHz: 800.000 19 | BogoMIPS: 5587.05 20 | Virtualization: VT-x 21 | L1d cache: 32K 22 | L1i cache: 32K 23 | L2 cache: 256K 24 | L3 cache: 2048K 25 | NUMA node0 CPU(s): 0,1 26 | 27 | Mem: 4G 28 | 29 | fpm config: 30 | pm = static 31 | pm.max_children = 150 32 | pm.start_servers = 20 33 | pm.max_requests = 500 34 | 35 | swoole config: 36 | worker_num=8 37 | max_request=1000 38 | 39 | ``` 40 | 41 | 42 | ##Benchmarks:echo hello world 43 | ####2. ab -c100 -n1000 "http://192.168.10.244/kcloze/index.php?ycf=hello&act=index" 44 | code in here:(https://github.com/kcloze/ycf/blob/master/src%2FService%2FYcfHello.php) 45 | 46 | ###php7 with php-fpm: 47 | ``` 48 | Server Software: nginx/1.6.3 49 | Server Hostname: 192.168.10.244 50 | Server Port: 80 51 | 52 | Document Path: /kcloze/index.php?ycf=hello&act=index 53 | Document Length: 21 bytes 54 | 55 | Concurrency Level: 100 56 | Time taken for tests: 0.228 seconds 57 | Complete requests: 1000 58 | Failed requests: 0 59 | Write errors: 0 60 | Total transferred: 182182 bytes 61 | HTML transferred: 21021 bytes 62 | Requests per second: 4393.92 [#/sec] (mean) 63 | Time per request: 22.759 [ms] (mean) 64 | Time per request: 0.228 [ms] (mean, across all concurrent requests) 65 | Transfer rate: 781.73 [Kbytes/sec] received 66 | 67 | Connection Times (ms) 68 | min mean[+/-sd] median max 69 | Connect: 0 0 0.2 0 1 70 | Processing: 3 22 2.7 22 28 71 | Waiting: 3 22 2.7 22 28 72 | Total: 3 22 2.5 22 28 73 | 74 | Percentage of the requests served within a certain time (ms) 75 | 50% 22 76 | 66% 22 77 | 75% 22 78 | 80% 22 79 | 90% 22 80 | 95% 22 81 | 98% 25 82 | 99% 27 83 | 100% 28 (longest request) 84 | ``` 85 | 86 | ###php7 with swoole-http-server: 87 | ``` 88 | Server Software: swoole-http-server 89 | Server Hostname: 192.168.10.244 90 | Server Port: 9501 91 | 92 | Document Path: /kcloze/index.php?ycf=hello&act=index 93 | Document Length: 9 bytes 94 | 95 | Concurrency Level: 200 96 | Time taken for tests: 0.054 seconds 97 | Complete requests: 1000 98 | Failed requests: 0 99 | Write errors: 0 100 | Total transferred: 156000 bytes 101 | HTML transferred: 9000 bytes 102 | Requests per second: 18540.49 [#/sec] (mean) 103 | Time per request: 10.787 [ms] (mean) 104 | Time per request: 0.054 [ms] (mean, across all concurrent requests) 105 | Transfer rate: 2824.53 [Kbytes/sec] received 106 | 107 | Connection Times (ms) 108 | min mean[+/-sd] median max 109 | Connect: 0 0 0.6 0 2 110 | Processing: 0 9 7.6 7 28 111 | Waiting: 0 9 7.6 7 28 112 | Total: 0 10 7.6 8 29 113 | 114 | Percentage of the requests served within a certain time (ms) 115 | 50% 8 116 | 66% 12 117 | 75% 15 118 | 80% 17 119 | 90% 22 120 | 95% 25 121 | 98% 27 122 | 99% 28 123 | 100% 29 (longest request) 124 | 125 | ``` 126 | 127 | ##Benchmarks:select one record 128 | swoole with mysql aync 129 | 130 | 131 | ####2. ab -c200 -n1000 "http://192.168.10.244/kcloze/index.php?ycf=pdo&act=test" 132 | code in here:(https://github.com/kcloze/ycf/blob/master/src%2FService%2FYcfPdo.php) 133 | 134 | ###php7 with php-fpm: 135 | ``` 136 | 137 | 138 | Server Software: nginx/1.6.3 139 | Server Hostname: 192.168.10.244 140 | Server Port: 80 141 | 142 | Document Path: /kcloze/index.php?ycf=pdo&act=test 143 | Document Length: 163 bytes 144 | 145 | Concurrency Level: 200 146 | Time taken for tests: 0.348 seconds 147 | Complete requests: 1000 148 | Failed requests: 0 149 | Write errors: 0 150 | Total transferred: 324000 bytes 151 | HTML transferred: 163000 bytes 152 | Requests per second: 2874.74 [#/sec] (mean) 153 | Time per request: 69.572 [ms] (mean) 154 | Time per request: 0.348 [ms] (mean, across all concurrent requests) 155 | Transfer rate: 909.58 [Kbytes/sec] received 156 | 157 | Connection Times (ms) 158 | min mean[+/-sd] median max 159 | Connect: 0 0 0.6 0 2 160 | Processing: 9 62 12.0 65 93 161 | Waiting: 9 62 12.0 65 93 162 | Total: 11 62 11.6 65 93 163 | 164 | Percentage of the requests served within a certain time (ms) 165 | 50% 65 166 | 66% 66 167 | 75% 67 168 | 80% 67 169 | 90% 70 170 | 95% 75 171 | 98% 83 172 | 99% 88 173 | 100% 93 (longest request) 174 | 175 | 176 | ``` 177 | 178 | 179 | 180 | ###php7 with swoole-http-server: 181 | 182 | ``` 183 | 184 | Server Software: swoole-http-server 185 | Server Hostname: 192.168.10.244 186 | Server Port: 9501 187 | 188 | Document Path: /kcloze/index.php?ycf=pdo&act=aync 189 | Document Length: 51 bytes 190 | 191 | Concurrency Level: 200 192 | Time taken for tests: 0.290 seconds 193 | Complete requests: 1000 194 | Failed requests: 0 195 | Write errors: 0 196 | Total transferred: 199000 bytes 197 | HTML transferred: 51000 bytes 198 | Requests per second: 3449.31 [#/sec] (mean) 199 | Time per request: 57.983 [ms] (mean) 200 | Time per request: 0.290 [ms] (mean, across all concurrent requests) 201 | Transfer rate: 670.33 [Kbytes/sec] received 202 | 203 | Connection Times (ms) 204 | min mean[+/-sd] median max 205 | Connect: 0 0 0.6 0 2 206 | Processing: 5 52 12.7 55 72 207 | Waiting: 5 52 12.7 55 72 208 | Total: 7 52 12.3 55 73 209 | 210 | Percentage of the requests served within a certain time (ms) 211 | 50% 55 212 | 66% 58 213 | 75% 60 214 | 80% 61 215 | 90% 64 216 | 95% 66 217 | 98% 68 218 | 99% 69 219 | 100% 73 (longest request) 220 | 221 | ``` -------------------------------------------------------------------------------- /doc/db.md: -------------------------------------------------------------------------------- 1 | ## Examples 2 | Below some examples of the basic functions of the database class. I've included a SQL dump so you can easily test the database 3 | class functions. 4 | #### The persons table 5 | | id | firstname | lastname | sex | age 6 | |:-----------:|:------------:|:------------:|:------------:|:------------:| 7 | | 1 | John | Doe | M | 19 8 | | 2 | Bob | Black | M | 41 9 | | 3 | Zoe | Chan | F | 20 10 | | 4 | Kona | Khan | M | 14 11 | | 5 | Kader| Khan | M | 56 12 | 13 | #### Fetching everything from the table 14 | ```php 15 | query("SELECT * FROM persons"); 18 | ``` 19 | #### Fetching with Bindings (ANTI-SQL-INJECTION): 20 | Binding parameters is the best way to prevent SQL injection. The class prepares your SQL query and binds the parameters 21 | afterwards. 22 | 23 | There are three different ways to bind parameters. 24 | ```php 25 | bind("id","1"); 28 | $db->bind("firstname","John"); 29 | $person = $db->query("SELECT * FROM Persons WHERE firstname = :firstname AND id = :id"); 30 | 31 | // 2. Bind more parameters 32 | $db->bindMore(array("firstname"=>"John","id"=>"1")); 33 | $person = $db->query("SELECT * FROM Persons WHERE firstname = :firstname AND id = :id")); 34 | 35 | // 3. Or just give the parameters to the method 36 | $person = $db->query("SELECT * FROM Persons WHERE firstname = :firstname",array("firstname"=>"John","id"=>"1")); 37 | ``` 38 | 39 | More about SQL injection prevention : http://indieteq.com/index/readmore/how-to-prevent-sql-injection-in-php 40 | 41 | #### Fetching Row: 42 | This method always returns only 1 row. 43 | ```php 44 | row("SELECT * FROM Persons WHERE id = :id", array("id"=>"1")); 47 | ``` 48 | ##### Result 49 | | id | firstname | lastname | sex | age 50 | |:-----------:|:------------:|:------------:|:------------:|:------------:| 51 | | 1 | John | Doe | M | 19 52 | #### Fetching Single Value: 53 | This method returns only one single value of a record. 54 | ```php 55 | bind("id","3"); 58 | $firstname = $db->single("SELECT firstname FROM Persons WHERE id = :id"); 59 | ``` 60 | ##### Result 61 | |firstname 62 | |:------------: 63 | | Zoe 64 | #### Fetching Column: 65 | ```php 66 | column("SELECT Firstname FROM Persons"); 69 | ``` 70 | ##### Result 71 | |firstname | 72 | |:-----------: 73 | | John 74 | | Bob 75 | | Zoe 76 | | Kona 77 | | Kader 78 | ### Delete / Update / Insert 79 | When executing the delete, update, or insert statement by using the query method the affected rows will be returned. 80 | ```php 81 | query("DELETE FROM Persons WHERE Id = :id", array("id"=>"1")); 85 | 86 | // Update 87 | $update = $db->query("UPDATE Persons SET firstname = :f WHERE Id = :id", array("f"=>"Jan","id"=>"32")); 88 | 89 | 90 | // Insert 91 | $insert = $db->query("INSERT INTO Persons(Firstname,Age) VALUES(:f,:age)", array("f"=>"Vivek","age"=>"20")); 92 | 93 | // Do something with the data 94 | if($insert > 0 ) { 95 | return 'Succesfully created a new person !'; 96 | } 97 | 98 | ``` 99 | ## Method parameters 100 | Every method which executes a query has the optional parameter called bindings. 101 | 102 | The row and the query method have a third optional parameter which is the fetch style. 103 | The default fetch style is PDO::FETCH_ASSOC which returns an associative array. 104 | 105 | Here an example : 106 | 107 | ```php 108 | row("SELECT * FROM Persons WHERE id = :id", array("id"=>"1"), PDO::FETCH_NUM); 111 | 112 | print_r($person_num); 113 | // Array ( [0] => 1 [1] => Johny [2] => Doe [3] => M [4] => 19 ) 114 | 115 | ``` 116 | More info about the PDO fetchstyle : http://php.net/manual/en/pdostatement.fetch.php -------------------------------------------------------------------------------- /doc/pdo_test.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.5.3.1 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: 127.0.0.1 6 | -- Generation Time: 2016-01-17 12:18:41 7 | -- 服务器版本: 5.5.37-0ubuntu0.14.04.1 8 | -- PHP Version: 7.0.2 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8mb4 */; 18 | 19 | -- 20 | -- Database: `test` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- 表的结构 `pdo_test` 27 | -- 28 | 29 | DROP TABLE IF EXISTS `pdo_test`; 30 | CREATE TABLE IF NOT EXISTS `pdo_test` ( 31 | `id` int(10) NOT NULL AUTO_INCREMENT, 32 | `pName` varchar(50) NOT NULL, 33 | `pValue` varchar(50) NOT NULL, 34 | PRIMARY KEY (`id`) 35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='pdo测试'; 36 | 37 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 38 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 39 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 40 | -------------------------------------------------------------------------------- /doc/settings.ini.php: -------------------------------------------------------------------------------- 1 | ; 2 | [Mysql] 3 | host = 127.0.0.1 4 | user = test 5 | password ="test123" 6 | dbname = test 7 | port = 3306 8 | charset = utf8 -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcloze/queueSwoole/c4a4f58e10e705d8468785ff0adbad4eb7f38be9/favicon.ico -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | init(); 13 | $ycf->run(); 14 | -------------------------------------------------------------------------------- /server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pidFile="/var/qserver_pid"; 3 | pidDbFile="/var/db_server_pid"; 4 | function start(){ 5 | php ./src/Core/YcfHttpServer.php $pidFile; 6 | 7 | printf $? 8 | if [ $? == 0 ]; then 9 | printf "\qserver start OK\r\n" 10 | return 0 11 | else 12 | printf "\qserver start FAIL\r\n" 13 | return 1 14 | fi 15 | } 16 | 17 | function stop(){ 18 | 19 | $(ps aux | grep "$pidFile" |grep -v "grep "| awk '{print $2}' | xargs kill -9) 20 | 21 | 22 | PROCESS_NUM2=$(ps aux | grep "$pidFile" |grep -v "grep "| awk '{print $2}' | wc -l ) 23 | if [ $PROCESS_NUM2 == 0 ]; then 24 | printf "\qserver stop OK\r\n" 25 | return 0 26 | else 27 | printf "\qserver stop FAIL\r\n" 28 | return 1 29 | fi 30 | 31 | } 32 | 33 | 34 | function startDB(){ 35 | php ./src/Core/YcfDBServer.php $pidDbFile 36 | 37 | if [ $? == 0 ]; then 38 | printf "\db_server start OK\r\n" 39 | return 0 40 | else 41 | printf "\db_server start FAIL\r\n" 42 | return 1 43 | fi 44 | } 45 | 46 | function stopDB(){ 47 | 48 | $(ps aux | grep "$pidDbFile" |grep -v "grep "| awk '{print $2}' | xargs kill -9) 49 | 50 | 51 | PROCESS_NUM2=$(ps aux | grep "$pidDbFile" |grep -v "grep "| awk '{print $2}' | wc -l ) 52 | if [ $PROCESS_NUM2 == 0 ]; then 53 | printf "\db_server stop OK\r\n" 54 | return 0 55 | else 56 | printf "\db_server stop FAIL\r\n" 57 | return 1 58 | fi 59 | 60 | } 61 | 62 | 63 | case $1 in 64 | 65 | start ) 66 | start 67 | ;; 68 | startDB ) 69 | startDB 70 | ;; 71 | 72 | stop) 73 | stop 74 | ;; 75 | stopDB) 76 | stopDB 77 | ;; 78 | restart) 79 | stop 80 | sleep 1 81 | start 82 | ;; 83 | 84 | *) 85 | start 86 | ;; 87 | esac 88 | 89 | -------------------------------------------------------------------------------- /src/Controller/CtrHello.php: -------------------------------------------------------------------------------- 1 | end("Greet, Klcoze!"); 13 | 14 | } 15 | public function actionHello() 16 | { 17 | echo "hello ycf" . time(); 18 | echo $this->getPPP(); 19 | 20 | } 21 | 22 | public function actionTask() 23 | { 24 | // send a task to task worker. 25 | $param = array( 26 | 'action' => 'test', 27 | 'time' => time(), 28 | ); 29 | //var_dump(HttpServer::getInstance()->http); 30 | //$this->http->task(json_encode($param)); 31 | for ($i = 0; $i < 1; $i++) { 32 | $taskId = YcfCore::$_http_server->task(json_encode($param)); 33 | } 34 | echo $taskId . " hello ycf" . time(); 35 | 36 | } 37 | 38 | public function actionLog() 39 | { 40 | //for ($i = 0; $i < 1000; $i++) { 41 | YcfCore::$_log->log('hello ycf' . time(), 'info'); 42 | YcfCore::$_response->end("Greet, Klcoze!"); 43 | //} 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/Controller/CtrPdo.php: -------------------------------------------------------------------------------- 1 | testInsert(); 14 | var_dump($result); 15 | 16 | //$result = $modelTest->testQuery(); 17 | //var_dump($result); 18 | } 19 | 20 | public function actionAync() 21 | { 22 | $result = ModelProxyDb::query('select * from pdo_test limit 1'); 23 | var_dump($result); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Controller/CtrRedis.php: -------------------------------------------------------------------------------- 1 | testRedis(); 13 | var_dump($result); 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Core/YcfCore.php: -------------------------------------------------------------------------------- 1 | routeCli(); 29 | } else { 30 | $router = $this->route(); 31 | } 32 | //route to controller 33 | $actionName = 'action' . ucfirst($router['action']); 34 | $ycfName = "Ycf\Controller\Ctr" . ucfirst($router['controller']); 35 | if (method_exists($ycfName, $actionName)) { 36 | try { 37 | $ycf = new $ycfName(); 38 | $ycf->$actionName(); 39 | } catch (Exception $e) { 40 | var_dump($e); 41 | } 42 | 43 | } else { 44 | echo ("action not find"); 45 | } 46 | $this->shutdown(); 47 | 48 | } 49 | 50 | public function shutdown() 51 | { 52 | //echo 'shutdown....'; 53 | if (!defined('SWOOLE')) { 54 | self::$_log->flush(); 55 | } else { 56 | self::$_log->sendTask(); 57 | } 58 | } 59 | 60 | public function route() 61 | { 62 | $array = array('controller' => 'Hello', 'action' => 'index'); 63 | if (!empty($_GET["ycf"])) { 64 | $array['controller'] = $_GET["ycf"]; 65 | } 66 | if (!empty($_GET["act"])) { 67 | $array['action'] = $_GET["act"]; 68 | return $array; 69 | } 70 | $uri = parse_url($_SERVER['REQUEST_URI']); 71 | if (empty($uri['path']) or '/' == $uri['path'] or '/index.php' == $uri['path']) { 72 | return $array; 73 | } 74 | $request = explode('/', trim($uri['path'], '/'), 3); 75 | if (count($request) < 2) { 76 | return $array; 77 | } 78 | $array['controller'] = $request[0]; 79 | $array['action'] = $request[1]; 80 | 81 | return $array; 82 | } 83 | /** 84 | *cli use this: /opt/php7/bin/php index.php ycf=Pdo act=test 85 | * 86 | */ 87 | public function routeCli() 88 | { 89 | $array = array('controller' => 'Hello', 'action' => 'index'); 90 | global $argv; 91 | foreach ($argv as $arg) { 92 | $e = explode("=", $arg); 93 | if (count($e) == 2) { 94 | $_GET[$e[0]] = $e[1]; 95 | } else { 96 | $_GET[$e[0]] = 0; 97 | } 98 | } 99 | if (!empty($_GET["ycf"])) { 100 | $array['controller'] = $_GET["ycf"]; 101 | } 102 | if (!empty($_GET["act"])) { 103 | $array['action'] = $_GET["act"]; 104 | } 105 | //$_SERVER['REQUEST_URI'] = $argv[0] . "?" . $argv[1] . "&" . $argv[2]; 106 | return $array; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/Core/YcfDB.php: -------------------------------------------------------------------------------- 1 | config = $config; 44 | $this->Connect(); 45 | $this->parameters = array(); 46 | } 47 | 48 | /** 49 | * This method makes connection to the database. 50 | * 51 | * 1. Reads the database settings from a ini file. 52 | * 2. Puts the ini content into the settings array. 53 | * 3. Tries to connect to the database. 54 | * 4. If connection failed, exception is displayed and a log file gets created. 55 | */ 56 | private function Connect() 57 | { 58 | $dsn = 'mysql:dbname=' . $this->config["dbname"] . ';host=' . $this->config["host"] . ''; 59 | try 60 | { 61 | # Read settings from INI file, set UTF8 62 | $this->pdo = new \PDO($dsn, $this->config["user"], $this->config["password"], array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")); 63 | 64 | # We can now log any exceptions on Fatal error. 65 | $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 66 | 67 | # Disable emulation of prepared statements, use REAL prepared statements instead. 68 | $this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false); 69 | 70 | # Connection succeeded, set the boolean to true. 71 | $this->bConnected = true; 72 | } catch (\PDOException $e) { 73 | # Write into log 74 | echo $this->ExceptionLog($e->getMessage()); 75 | return; 76 | //die(); 77 | } 78 | } 79 | /* 80 | * You can use this little method if you want to close the PDO connection 81 | * 82 | */ 83 | public function CloseConnection() 84 | { 85 | # Set the PDO object to null to close the connection 86 | # http://www.php.net/manual/en/pdo.connections.php 87 | $this->pdo = null; 88 | } 89 | 90 | /** 91 | * Every method which needs to execute a SQL query uses this method. 92 | * 93 | * 1. If not connected, connect to the database. 94 | * 2. Prepare Query. 95 | * 3. Parameterize Query. 96 | * 4. Execute Query. 97 | * 5. On exception : Write Exception into the log + SQL query. 98 | * 6. Reset the Parameters. 99 | */ 100 | private function Init($query, $parameters = "") 101 | { 102 | # Connect to database 103 | if (!$this->bConnected) {$this->Connect();} 104 | try { 105 | # Prepare query 106 | $this->sQuery = $this->pdo->prepare($query); 107 | 108 | # Add parameters to the parameter array 109 | $this->bindMore($parameters); 110 | 111 | # Bind parameters 112 | if (!empty($this->parameters)) { 113 | foreach ($this->parameters as $param) { 114 | $parameters = explode("\x7F", $param); 115 | $this->sQuery->bindParam($parameters[0], $parameters[1]); 116 | } 117 | } 118 | 119 | # Execute SQL 120 | $this->succes = $this->sQuery->execute(); 121 | } catch (\PDOException $e) { 122 | # Write into log and display Exception 123 | echo $this->ExceptionLog($e->getMessage(), $query); 124 | return; 125 | //die(); 126 | } 127 | 128 | # Reset the parameters 129 | $this->parameters = array(); 130 | } 131 | 132 | /** 133 | * @void 134 | * 135 | * Add the parameter to the parameter array 136 | * @param string $para 137 | * @param string $value 138 | */ 139 | public function bind($para, $value) 140 | { 141 | $this->parameters[sizeof($this->parameters)] = ":" . $para . "\x7F" . $value; 142 | } 143 | /** 144 | * @void 145 | * 146 | * Add more parameters to the parameter array 147 | * @param array $parray 148 | */ 149 | public function bindMore($parray) 150 | { 151 | if (empty($this->parameters) && is_array($parray)) { 152 | $columns = array_keys($parray); 153 | foreach ($columns as $i => &$column) { 154 | $this->bind($column, $parray[$column]); 155 | } 156 | } 157 | } 158 | /** 159 | * If the SQL query contains a SELECT or SHOW statement it returns an array containing all of the result set row 160 | * If the SQL statement is a DELETE, INSERT, or UPDATE statement it returns the number of affected rows 161 | * 162 | * @param string $query 163 | * @param array $params 164 | * @param int $fetchmode 165 | * @return mixed 166 | */ 167 | public function query($query, $params = null, $fetchmode = \PDO::FETCH_ASSOC) 168 | { 169 | $query = trim($query); 170 | 171 | $this->Init($query, $params); 172 | 173 | $rawStatement = explode(" ", $query); 174 | 175 | # Which SQL statement is used 176 | $statement = strtolower($rawStatement[0]); 177 | 178 | if ('select' === $statement || 'show' === $statement) { 179 | return $this->sQuery->fetchAll($fetchmode); 180 | } elseif ('insert' === $statement || 'update' === $statement || 'delete' === $statement) { 181 | return $this->sQuery->rowCount(); 182 | } else { 183 | return null; 184 | } 185 | } 186 | 187 | /** 188 | * Returns the last inserted id. 189 | * @return string 190 | */ 191 | public function lastInsertId() 192 | { 193 | return $this->pdo->lastInsertId(); 194 | } 195 | 196 | /** 197 | * Returns an array which represents a column from the result set 198 | * 199 | * @param string $query 200 | * @param array $params 201 | * @return array 202 | */ 203 | public function column($query, $params = null) 204 | { 205 | $this->Init($query, $params); 206 | $Columns = $this->sQuery->fetchAll(\PDO::FETCH_NUM); 207 | 208 | $column = null; 209 | 210 | foreach ($Columns as $cells) { 211 | $column[] = $cells[0]; 212 | } 213 | 214 | return $column; 215 | 216 | } 217 | /** 218 | * Returns an array which represents a row from the result set 219 | * 220 | * @param string $query 221 | * @param array $params 222 | * @param int $fetchmode 223 | * @return array 224 | */ 225 | public function row($query, $params = null, $fetchmode = \PDO::FETCH_ASSOC) 226 | { 227 | $this->Init($query, $params); 228 | return $this->sQuery->fetch($fetchmode); 229 | } 230 | /** 231 | * Returns the value of one single field/column 232 | * 233 | * @param string $query 234 | * @param array $params 235 | * @return string 236 | */ 237 | public function single($query, $params = null) 238 | { 239 | $this->Init($query, $params); 240 | return $this->sQuery->fetchColumn(); 241 | } 242 | /** 243 | * [insert insert自动绑定字段参数] 244 | * @param [type] $tableName [description] 245 | * @param [type] $data [description] 246 | * @return [type] [description] 247 | */ 248 | public function insert($tableName, $data) 249 | { 250 | $columnL = ""; 251 | $columnR = ":"; 252 | 253 | if (is_array($data)) { 254 | $keys = array_keys($data); 255 | $columnL .= implode(",", $keys); 256 | $columnR .= implode(",:", $keys); 257 | } else { 258 | return false; 259 | } 260 | $insert = $this->query("INSERT INTO " . $tableName . " ( " . $columnL . ") VALUES ( " . $columnR . " ) ", $data); 261 | if ($insert > 0) { 262 | return $this->lastInsertId(); 263 | } else { 264 | return false; 265 | } 266 | } 267 | /** 268 | * [update update自动绑定字段参数] 269 | * @param [type] $tableName [description] 270 | * @param [type] $udate [description] 271 | * @param [type] $where [description] 272 | * @return [type] [description] 273 | */ 274 | public function update($tableName, $udate, $where) 275 | { 276 | $columnL = " "; 277 | $columnR = " "; 278 | if (is_array($udate)) { 279 | $i = 0; 280 | foreach ($udate as $key => $value) { 281 | $i++; 282 | $columnL .= $key . " = :" . $key; 283 | if ($i < count($udate)) { 284 | $columnL .= ", "; 285 | } 286 | } 287 | } else { 288 | return false; 289 | } 290 | if (is_array($where)) { 291 | $i = 0; 292 | foreach ($where as $key => $value) { 293 | $i++; 294 | $columnR .= $key . " = :" . $key; 295 | if ($i < count($where)) { 296 | $columnR .= " and "; 297 | } 298 | } 299 | } else { 300 | return false; 301 | } 302 | //echo "UPDATE ".$tableName." SET ".$columnL." WHERE ".$columnR;exit; 303 | $data = array_merge($udate, $where); 304 | $update = $this->query("UPDATE " . $tableName . " SET " . $columnL . " WHERE " . $columnR, $data); 305 | return $update; 306 | 307 | } 308 | 309 | /** 310 | * Writes the log and returns the exception 311 | * 312 | * @param string $message 313 | * @param string $sql 314 | * @return string 315 | */ 316 | private function ExceptionLog($message, $sql = "") 317 | { 318 | $exception = 'Unhandled Exception.
'; 319 | $exception .= $message; 320 | $exception .= "
You can find the error back in the log."; 321 | 322 | if (!empty($sql)) { 323 | # Add the Raw SQL to the Log 324 | $message .= "\r\nRaw SQL : " . $sql; 325 | } 326 | # Write into log 327 | YcfCore::$_log->log($message, 'error', 'db'); 328 | 329 | return $exception; 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /src/Core/YcfDBServer.php: -------------------------------------------------------------------------------- 1 | set(array( 26 | 'worker_num' => 5, 27 | 'max_request' => 1000, 28 | )); 29 | 30 | $serv->on('WorkerStart', array($this, 'onStart')); 31 | //$serv->on('Connect', array($this, 'onConnect')); 32 | $serv->on('Receive', array($this, 'onReceive')); 33 | //$serv->on('Close', array($this, 'onClose')); 34 | $serv->start(); 35 | } 36 | 37 | public function onStart($serv) 38 | { 39 | $this->serv = $serv; 40 | $settings = parse_ini_file("settings.ini.php", true)['Mysql']; 41 | 42 | for ($i = 0; $i < $this->pool_size; $i++) { 43 | $db = new mysqli; 44 | $db->connect($settings['host'], $settings['user'], $settings['password'], $settings['dbname']); 45 | $db_sock = swoole_get_mysqli_sock($db); 46 | swoole_event_add($db_sock, array($this, 'onSQLReady')); 47 | $this->idle_pool[] = array( 48 | 'mysqli' => $db, 49 | 'db_sock' => $db_sock, 50 | 'fd' => 0, 51 | ); 52 | } 53 | echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; 54 | } 55 | 56 | public function onSQLReady($db_sock) 57 | { 58 | $db_res = $this->busy_pool[$db_sock]; 59 | $mysqli = $db_res['mysqli']; 60 | $fd = $db_res['fd']; 61 | 62 | echo __METHOD__ . ": client_sock=$fd|db_sock=$db_sock\n"; 63 | 64 | if ($result = $mysqli->reap_async_query()) { 65 | $ret = var_export($result->fetch_all(MYSQLI_ASSOC), true) . "\n"; 66 | $this->serv->send($fd, $ret); 67 | if (is_object($result)) { 68 | mysqli_free_result($result); 69 | } 70 | } else { 71 | $this->serv->send($fd, sprintf("MySQLi Error: %s\n", mysqli_error($mysqli))); 72 | } 73 | //release mysqli object 74 | $this->idle_pool[] = $db_res; 75 | unset($this->busy_pool[$db_sock]); 76 | 77 | //这里可以取出一个等待请求 78 | if (count($this->wait_queue) > 0) { 79 | $idle_n = count($this->idle_pool); 80 | for ($i = 0; $i < $idle_n; $i++) { 81 | $req = array_shift($this->wait_queue); 82 | $this->doQuery($req['fd'], $req['sql']); 83 | } 84 | } 85 | } 86 | 87 | public function onReceive($serv, $fd, $from_id, $data) 88 | { 89 | echo "Received: $data\n"; 90 | //没有空闲的数据库连接 91 | 92 | if (count($this->idle_pool) == 0) { 93 | //等待队列未满 94 | if (count($this->wait_queue) < $this->wait_queue_max) { 95 | $this->wait_queue[] = array( 96 | 'fd' => $fd, 97 | 'sql' => $data, 98 | ); 99 | } else { 100 | $this->serv->send($fd, "request too many, Please try again later."); 101 | } 102 | } else { 103 | $this->doQuery($fd, $data); 104 | } 105 | } 106 | 107 | public function doQuery($fd, $sql) 108 | { 109 | //从空闲池中移除 110 | $db = array_pop($this->idle_pool); 111 | /** 112 | * @var mysqli 113 | */ 114 | $mysqli = $db['mysqli']; 115 | 116 | for ($i = 0; $i < 2; $i++) { 117 | $result = $mysqli->query($sql, MYSQLI_ASYNC); 118 | if ($result === false) { 119 | echo $mysqli->errno . "\n"; 120 | if ($mysqli->errno == 2013 or $mysqli->errno == 2006) { 121 | $mysqli->close(); 122 | $r = $mysqli->connect(); 123 | if ($r === true) { 124 | continue; 125 | } 126 | 127 | } 128 | } 129 | break; 130 | } 131 | 132 | $db['fd'] = $fd; 133 | //加入工作池中 134 | $this->busy_pool[$db['db_sock']] = $db; 135 | } 136 | } 137 | 138 | $server = new YcfDBServer(); 139 | $server->run(); 140 | -------------------------------------------------------------------------------- /src/Core/YcfHttpServer.php: -------------------------------------------------------------------------------- 1 | http = new \swoole_http_server("0.0.0.0", 9501); 23 | 24 | $this->http->set( 25 | array( 26 | 'worker_num' => 2, 27 | 'daemonize' => false, 28 | 'max_request' => 1, 29 | 'task_worker_num' => 2, 30 | //'dispatch_mode' => 1, 31 | ) 32 | ); 33 | 34 | $this->http->on('WorkerStart', array($this, 'onWorkerStart')); 35 | 36 | $this->http->on('request', function ($request, $response) { 37 | //捕获异常 38 | register_shutdown_function(array($this, 'handleFatal')); 39 | //请求过滤 40 | if ('/favicon.ico' == $request->server['path_info'] || '/favicon.ico' == $request->server['request_uri']) { 41 | return $response->end(); 42 | } 43 | if (isset($request->server)) { 44 | self::$server = $request->server; 45 | foreach ($request->server as $key => $value) { 46 | $_SERVER[strtoupper($key)] = $value; 47 | } 48 | } 49 | if (isset($request->header)) { 50 | self::$header = $request->header; 51 | } 52 | if (isset($request->get)) { 53 | self::$get = $request->get; 54 | foreach ($request->get as $key => $value) { 55 | $_GET[$key] = $value; 56 | } 57 | } 58 | if (isset($request->post)) { 59 | self::$post = $request->post; 60 | foreach ($request->post as $key => $value) { 61 | $_POST[$key] = $value; 62 | } 63 | } 64 | if (isset($request->request_uri)) { 65 | $_SERVER['REQUEST_URI'] = $request->request_uri; 66 | } 67 | $GLOBALS['http_server'] = $this->http; 68 | ob_start(); 69 | //实例化ycf对象 70 | try { 71 | $ycf = new YcfCore; 72 | YcfCore::$_response = $response; 73 | $ycf->init($this->http); 74 | $ycf->run(); 75 | } catch (Exception $e) { 76 | var_dump($e); 77 | } 78 | $result = ob_get_contents(); 79 | ob_end_clean(); 80 | YcfCore::$_response->end($result); 81 | unset($result); 82 | }); 83 | 84 | $this->http->on('Task', array($this, 'onTask')); 85 | $this->http->on('Finish', array($this, 'onFinish')); 86 | 87 | $this->http->start(); 88 | } 89 | 90 | public function onWorkerStart() 91 | { 92 | date_default_timezone_set('Asia/Shanghai'); 93 | define('DEBUG', true); 94 | define('SWOOLE', true); 95 | define('DS', DIRECTORY_SEPARATOR); 96 | define('ROOT_PATH', realpath(dirname(__FILE__)) . DS . "../.." . DS); 97 | define('YCF_BEGIN_TIME', microtime(true)); 98 | //echo 'worker start....'; 99 | require 'vendor/autoload.php'; 100 | 101 | } 102 | public function onTask($serv, $task_id, $from_id, $data) 103 | { 104 | $ycf = new YcfCore; 105 | $ycf->init(); 106 | return ModelTask::run($serv, $task_id, $from_id, $data); 107 | } 108 | public function onFinish($serv, $task_id, $data) 109 | { 110 | echo "Task {$task_id} finish\n"; 111 | echo "Result: {$data}\n"; 112 | unset($data); 113 | } 114 | /** 115 | * Fatal Error的捕获 116 | * 117 | */ 118 | public function handleFatal() 119 | { 120 | $error = error_get_last(); 121 | if (!isset($error['type'])) { 122 | return; 123 | } 124 | 125 | switch ($error['type']) { 126 | case E_ERROR: 127 | case E_PARSE: 128 | case E_DEPRECATED: 129 | case E_CORE_ERROR: 130 | case E_COMPILE_ERROR: 131 | break; 132 | default: 133 | return; 134 | } 135 | $message = $error['message']; 136 | $file = $error['file']; 137 | $line = $error['line']; 138 | $log = "\n异常提示:$message ($file:$line)\nStack trace:\n"; 139 | $trace = debug_backtrace(1); 140 | 141 | foreach ($trace as $i => $t) { 142 | if (!isset($t['file'])) { 143 | $t['file'] = 'unknown'; 144 | } 145 | if (!isset($t['line'])) { 146 | $t['line'] = 0; 147 | } 148 | if (!isset($t['function'])) { 149 | $t['function'] = 'unknown'; 150 | } 151 | $log .= "#$i {$t['file']}({$t['line']}): "; 152 | if (isset($t['object']) && is_object($t['object'])) { 153 | $log .= get_class($t['object']) . '->'; 154 | } 155 | $log .= "{$t['function']}()\n"; 156 | } 157 | if (isset($_SERVER['REQUEST_URI'])) { 158 | $log .= '[QUERY] ' . $_SERVER['REQUEST_URI']; 159 | } 160 | YcfCore::$_log->log($log, 'fatal'); 161 | YcfCore::$_log->sendTask(); 162 | if (YcfCore::$_response) { 163 | YcfCore::$_response->status(500); 164 | YcfCore::$_response->end('程序异常'); 165 | } 166 | 167 | unset($this->response); 168 | } 169 | 170 | public static function getInstance() 171 | { 172 | if (!self::$instance) { 173 | self::$instance = new YcfHttpServer(); 174 | } 175 | return self::$instance; 176 | } 177 | } 178 | 179 | YcfHttpServer::getInstance(); 180 | -------------------------------------------------------------------------------- /src/Core/YcfLog.php: -------------------------------------------------------------------------------- 1 | _logs[$category][] = array($message, $level, $category, microtime(true)); 25 | $this->_logCount++; 26 | if ($this->_logCount >= YcfLog::MAX_LOGS || true == $flush) { 27 | $this->flush($category); 28 | } 29 | } 30 | 31 | public function processLogs() 32 | { 33 | $logsAll["application"] = "[" . $_SERVER['REQUEST_URI'] . "] " . "[runing time]: " . (microtime(true) - YCF_BEGIN_TIME) . "\n"; 34 | foreach ((array) $this->_logs as $key => $logs) { 35 | $logsAll[$key] = ''; 36 | foreach ((array) $logs as $log) { 37 | $logsAll[$key] .= $this->formatLogMessage($log[0], $log[1], $log[2], $log[3]); 38 | } 39 | } 40 | return $logsAll; 41 | } 42 | /** 43 | * 44 | * 写日志到文件 45 | */ 46 | public function flush() 47 | { 48 | 49 | if ($this->_logCount <= 0) { 50 | return false; 51 | } 52 | $logsAll = $this->processLogs(); 53 | $this->write($logsAll); 54 | $this->_logs = array(); 55 | $this->_logCount = 0; 56 | } 57 | //异步任务写日志 58 | public function sendTask() 59 | { 60 | $logsAll = $this->processLogs(); 61 | if (empty($logsAll)) { 62 | return false; 63 | } 64 | $param = array( 65 | 'action' => 'flushLog', 66 | 'name' => '日志处理', 67 | 'content' => $logsAll, 68 | ); 69 | $taskId = YcfCore::$_http_server->task(json_encode($param)); 70 | 71 | } 72 | /** 73 | * [write 根据日志类型写到不同的日志文件] 74 | * @return [type] [description] 75 | */ 76 | public function write($logsAll) 77 | { 78 | if (empty($logsAll)) { 79 | return; 80 | } 81 | 82 | $this->_logPath = ROOT_PATH . 'src/runtime/'; 83 | if (!is_dir($this->_logPath)) { 84 | self::mkdir($this->_logPath, array(), true); 85 | } 86 | foreach ($logsAll as $key => $value) { 87 | if (empty($key)) { 88 | continue; 89 | } 90 | $fileName = $this->_logPath . $key . '.log'; 91 | $fp2 = @fopen($fileName, "a+") or YcfUtils::exitMsg("Log fatal Error !"); 92 | @fwrite($fp2, $value); 93 | @fclose($fp2); 94 | } 95 | 96 | } 97 | 98 | /** 99 | * Shared environment safe version of mkdir. Supports recursive creation. 100 | * For avoidance of umask side-effects chmod is used. 101 | * 102 | * @param string $dst path to be created 103 | * @param array $options newDirMode element used, must contain access bitmask 104 | * @param boolean $recursive whether to create directory structure recursive if parent dirs do not exist 105 | * @return boolean result of mkdir 106 | * @see mkdir 107 | */ 108 | private static function mkdir($dst, array $options, $recursive) 109 | { 110 | $prevDir = dirname($dst); 111 | if ($recursive && !is_dir($dst) && !is_dir($prevDir)) { 112 | self::mkdir(dirname($dst), $options, true); 113 | } 114 | 115 | $mode = isset($options['newDirMode']) ? $options['newDirMode'] : 0777; 116 | $res = mkdir($dst, $mode); 117 | @chmod($dst, $mode); 118 | return $res; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/Core/YcfRedis.php: -------------------------------------------------------------------------------- 1 | host = $config['host']; 45 | isset($config['port']) && $this->port = $config['port']; 46 | isset($config['connectTimeout']) && $this->connectTimeout = $config['connectTimeout']; 47 | isset($config['connectTries']) && $this->connectTries = $config['connectTries']; 48 | } 49 | 50 | /** 51 | * Getter of phpredis object 52 | * @return \Redis phpredis object instance 53 | * @throws RedisNotConfiguredException if any of required redis connect parameters are loose 54 | */ 55 | private function getRedis() 56 | { 57 | if (is_null($this->Redis)) { 58 | if ($this->isConfigured()) { 59 | $this->Redis = new \Redis(); 60 | $this->reconnect(); 61 | } else { 62 | throw new RedisNotConfiguredException(); 63 | } 64 | } 65 | return $this->Redis; 66 | } 67 | 68 | /** 69 | * Check required connection parameters configuration method 70 | * @return bool check result 71 | */ 72 | private function isConfigured() 73 | { 74 | return !empty($this->host) && $this->port >= 0 && $this->port <= 65535; 75 | } 76 | 77 | /** 78 | * Reconnect to the redis instance 79 | * @return bool connection result. Always true. 80 | * @throws RedisConnectException if connection could not established by RedisException cause 81 | * @throws RedisTriesOverConnectException if connection could not established because tries was over 82 | */ 83 | private function reconnect() 84 | { 85 | $count = 0; 86 | do { 87 | $count += 1; 88 | try { 89 | if ($this->persistent) { 90 | $result = $this->Redis->pconnect($this->host, $this->port, $this->connectTimeout); 91 | } else { 92 | $result = $this->Redis->connect($this->host, $this->port, $this->connectTimeout); 93 | } 94 | } catch (\RedisException $ex) { 95 | throw new RedisConnectException(); 96 | } 97 | if (true === $result) { 98 | return true; 99 | } 100 | } while ($count < $this->connectTries); 101 | 102 | $this->Redis = null; 103 | throw new RedisTriesOverConnectException(); 104 | } 105 | 106 | /** 107 | * Setter of connection timeout parameter 108 | * @param float $connectTimeout connection timeout value 109 | * @throws \InvalidArgumentException 110 | * @return Redis self 111 | */ 112 | public function setConnectTimeout($connectTimeout) 113 | { 114 | $this->connectTimeout = (float) $connectTimeout; 115 | if ($this->connectTimeout < 0) { 116 | throw new \InvalidArgumentException(); 117 | } 118 | return $this; 119 | } 120 | 121 | /** 122 | * Getter of connection timeout exception 123 | * @return float connect timeout value 124 | */ 125 | public function getConnectTimeout() 126 | { 127 | return $this->connectTimeout; 128 | } 129 | 130 | /** 131 | * Setter of number of connection tries 132 | * @param int $connectTries connection tries count 133 | * @throws \InvalidArgumentException 134 | * @return Redis self 135 | */ 136 | public function setConnectTries($connectTries) 137 | { 138 | $this->connectTries = (int) $connectTries; 139 | if ($this->connectTries < 1) { 140 | throw new \InvalidArgumentException(); 141 | } 142 | return $this; 143 | } 144 | 145 | /** 146 | * Getter of number of connection tries 147 | * @return int connection tries count 148 | */ 149 | public function getConnectTries() 150 | { 151 | return $this->connectTries; 152 | } 153 | 154 | /** 155 | * Setter for redis instance hostname or ip address 156 | * @param string $host hostname or ip address 157 | * @throws \InvalidArgumentException 158 | * @return Redis self 159 | */ 160 | public function setHost($host) 161 | { 162 | $this->host = (string) $host; 163 | if (empty($this->host)) { 164 | throw new \InvalidArgumentException(); 165 | } 166 | return $this; 167 | } 168 | 169 | /** 170 | * Getter of redis instance hostname 171 | * @return string redis instance hostname or ip address 172 | */ 173 | public function getHost() 174 | { 175 | return $this->host; 176 | } 177 | 178 | /** 179 | * Setter of redis instance connection port 180 | * @param int $port redis instance connection port 181 | * @throws \InvalidArgumentException 182 | * @return Redis self 183 | */ 184 | public function setPort($port) 185 | { 186 | $this->port = $port; 187 | if ($this->port < 0 || $this->port > 65535) { 188 | throw new \InvalidArgumentException(); 189 | } 190 | return $this; 191 | } 192 | 193 | /** 194 | * Getter of redis instance connection port 195 | * @return int redis instance connection port 196 | */ 197 | public function getPort() 198 | { 199 | return $this->port; 200 | } 201 | 202 | /** 203 | * Use persistent connection or not 204 | * @param bool $persistent if is set to true, pconnect will use, overwise not 205 | * @return Redis self 206 | */ 207 | public function setPersistent($persistent) 208 | { 209 | $this->persistent = (bool) $persistent; 210 | return $this; 211 | } 212 | 213 | /** 214 | * Use persistent connection or not 215 | * @return bool if is set to true, pconnect will use, overwise not 216 | */ 217 | public function getPersistent() 218 | { 219 | return $this->persistent; 220 | } 221 | 222 | /* 223 | * phpredis interface implementation 224 | */ 225 | 226 | /** 227 | * Increment key value 228 | * @param string $key key 229 | * @param int $value value for increment 230 | * @return int current value 231 | * @throws RedisConnectException exception on connection to redis instance 232 | */ 233 | public function incr($key, $value = 1) 234 | { 235 | $value = (int) $value; 236 | try { 237 | $result = ($value > 1) 238 | ? $this->getRedis()->incrBy($key, $value) 239 | : $this->getRedis()->incr($key); 240 | if (false !== $result) { 241 | return $result; 242 | } 243 | throw new RedisImpossibleValueException(); 244 | } catch (\RedisException $ex) { 245 | throw new RedisConnectException(); 246 | } 247 | } 248 | 249 | /** 250 | * Decrement key value 251 | * @param string $key key 252 | * @param int $value value for increment 253 | * @return int current value 254 | * @throws RedisConnectException exception on connection to redis instance 255 | */ 256 | public function decr($key, $value = 1) 257 | { 258 | $value = (int) $value; 259 | try { 260 | $result = ($value > 1) 261 | ? $this->getRedis()->decrBy($key, $value) 262 | : $this->getRedis()->decr($key); 263 | if (false !== $result) { 264 | return $result; 265 | } 266 | throw new RedisImpossibleValueException(); 267 | } catch (\RedisException $ex) { 268 | throw new RedisConnectException(); 269 | } 270 | } 271 | 272 | /** 273 | * Append string value 274 | * @param string $key key 275 | * @param string $value appended value 276 | * @return int length of a key after append 277 | * @throws RedisConnectException 278 | */ 279 | public function append($key, $value) 280 | { 281 | try { 282 | $result = $this->getRedis()->append($key, $value); 283 | if (false !== $result) { 284 | return $result; 285 | } 286 | throw new RedisImpossibleValueException(); 287 | } catch (\RedisException $ex) { 288 | throw new RedisConnectException(); 289 | } 290 | } 291 | 292 | /** 293 | * Get key value 294 | * @param string $key key 295 | * @return mixed key value 296 | * @throws RedisConnectException exception on connection to redis instance 297 | * @throws RedisKeyNotFoundException when key not found 298 | */ 299 | public function get($key) 300 | { 301 | try { 302 | $result = $this->getRedis()->get($key); 303 | if (false === $result) { 304 | throw new RedisKeyNotFoundException(); 305 | } 306 | return $result; 307 | } catch (\RedisException $ex) { 308 | throw new RedisConnectException(); 309 | } 310 | } 311 | 312 | /** 313 | * Get multiple keys values 314 | * @param array $keys keys 315 | * @return array values 316 | * @throws RedisConnectException exception on connection to redis instance 317 | */ 318 | public function mget(array $keys) 319 | { 320 | try { 321 | $result = $this->getRedis()->mGet($keys); 322 | if (false !== $result) { 323 | return array_combine($keys, $result); 324 | } 325 | throw new RedisImpossibleValueException(); 326 | } catch (\RedisException $ex) { 327 | throw new RedisConnectException(); 328 | } 329 | } 330 | 331 | /** 332 | * Set key value 333 | * @param string $key key 334 | * @param mixed $value value 335 | * @param int $timeout ttl timeout in milliseconds 336 | * @return bool operation result 337 | * @throws RedisConnectException exception on connection to redis instance 338 | */ 339 | public function set($key, $value, $timeout = 0) 340 | { 341 | try { 342 | $result = (0 == $timeout) 343 | ? $this->getRedis()->set($key, $value) 344 | : $this->getRedis()->psetex($key, $timeout, $value); 345 | if (false !== $result) { 346 | return $result; 347 | } 348 | throw new RedisImpossibleValueException(); 349 | } catch (\RedisException $ex) { 350 | throw new RedisConnectException(); 351 | } 352 | } 353 | 354 | /** 355 | * Set multiple key values 356 | * @param array $values key and values 357 | * @return bool operation result 358 | * @throws RedisConnectException exception on connection to redis instance 359 | */ 360 | public function mset(array $values) 361 | { 362 | try { 363 | return $this->getRedis()->mset($values); 364 | } catch (\RedisException $ex) { 365 | throw new RedisConnectException(); 366 | } 367 | } 368 | 369 | /** 370 | * Set key value if not exists 371 | * @param string $key key 372 | * @param mixed $value value 373 | * @return bool returns true, if operation complete succesfull, else false 374 | * @throws RedisConnectException exception on connection to redis instance 375 | */ 376 | public function setnx($key, $value) 377 | { 378 | try { 379 | return $this->getRedis()->setnx($key, $value); 380 | } catch (\RedisException $ex) { 381 | throw new RedisConnectException(); 382 | } 383 | } 384 | 385 | /** 386 | * Set multiple key values 387 | * @param array $values key and values 388 | * @return bool operation result 389 | * @throws RedisConnectException exception on connection to redis instance 390 | */ 391 | public function msetnx(array $values) 392 | { 393 | try { 394 | return $this->getRedis()->msetnx($values); 395 | } catch (\RedisException $ex) { 396 | throw new RedisConnectException(); 397 | } 398 | } 399 | 400 | /** 401 | * GetSet implementation 402 | * @param string $key key 403 | * @param mixed $value value 404 | * @return bool|mixed previous value of a key. If key did not set, method returns false 405 | * @throws RedisConnectException exception on connection to redis instance 406 | */ 407 | public function getset($key, $value) 408 | { 409 | try { 410 | return $this->getRedis()->getSet($key, $value); 411 | } catch (\RedisException $ex) { 412 | throw new RedisConnectException(); 413 | } 414 | } 415 | 416 | /** 417 | * Delete key or keys 418 | * @param string|array $keys key or keys array 419 | * @return int count of deleted keys 420 | * @throws RedisConnectException exception on connection to redis instance 421 | */ 422 | public function delete($keys) 423 | { 424 | try { 425 | $result = $this->getRedis()->delete($keys); 426 | if (false !== $result) { 427 | return $result; 428 | } 429 | throw new RedisImpossibleValueException(); 430 | } catch (\RedisException $ex) { 431 | throw new RedisConnectException(); 432 | } 433 | } 434 | 435 | /** 436 | * Check if key exists 437 | * @param string $key key 438 | * @return bool check result 439 | * @throws RedisConnectException exception on connection to redis instance 440 | */ 441 | public function exists($key) 442 | { 443 | try { 444 | return $this->getRedis()->exists($key); 445 | } catch (\RedisException $ex) { 446 | throw new RedisConnectException(); 447 | } 448 | } 449 | 450 | /** 451 | * Rename key 452 | * @param string $source current key name 453 | * @param string $destination needed key name 454 | * @return bool operation result. If false, source key not found 455 | * @throws RedisConnectException exception on connection to redis instance 456 | */ 457 | public function rename($source, $destination) 458 | { 459 | try { 460 | return $this->getRedis()->rename($source, $destination); 461 | } catch (\RedisException $ex) { 462 | throw new RedisConnectException(); 463 | } 464 | } 465 | 466 | /** 467 | * Rename key if needed key name was not 468 | * @param string $source current key name 469 | * @param string $destination needed key name 470 | * @return bool operation result. If false, source key not found or needed key name found 471 | * @throws RedisConnectException exception on connection to redis instance 472 | */ 473 | public function renamenx($source, $destination) 474 | { 475 | try { 476 | return $this->getRedis()->renamenx($source, $destination); 477 | } catch (\RedisException $ex) { 478 | throw new RedisConnectException(); 479 | } 480 | } 481 | 482 | /** 483 | * Get string length of a key 484 | * @param string $key key 485 | * @return int key value length 486 | * @throws RedisConnectException exception on connection to redis instance 487 | */ 488 | public function strlen($key) 489 | { 490 | try { 491 | $result = $this->getRedis()->strlen($key); 492 | if (false !== $result) { 493 | return $result; 494 | } 495 | throw new RedisImpossibleValueException(); 496 | } catch (\RedisException $ex) { 497 | throw new RedisConnectException(); 498 | } 499 | } 500 | 501 | /** 502 | * Set ttl for a key 503 | * @param string $key key 504 | * @param int $timeout ttl in milliseconds 505 | * @return bool operation result. If false ttl cound not be set, or key not found 506 | * @throws RedisConnectException exception on connection to redis instance 507 | */ 508 | public function expire($key, $timeout) 509 | { 510 | try { 511 | return $this->getRedis()->pexpire($key, $timeout); 512 | } catch (\RedisException $ex) { 513 | throw new RedisConnectException(); 514 | } 515 | } 516 | 517 | /** 518 | * Set time of life for the key 519 | * @param string $key key 520 | * @param int $timestamp unix timestamp of time of death 521 | * @return bool operation result. If false timestamp cound not be set, or key not found 522 | * @throws RedisConnectException exception on connection to redis instance 523 | */ 524 | public function expireat($key, $timestamp) 525 | { 526 | try { 527 | return $this->getRedis()->expireat($key, $timestamp); 528 | } catch (\RedisException $ex) { 529 | throw new RedisConnectException(); 530 | } 531 | } 532 | 533 | /** 534 | * Get ttl of the key 535 | * @param string $key key 536 | * @return int|bool ttl in milliseconds or false, if ttl is not set or key not found 537 | * @throws RedisConnectException exception on connection to redis instance 538 | */ 539 | public function ttl($key) 540 | { 541 | try { 542 | $result = $this->getRedis()->pttl($key); 543 | return (-1 != $result) ? $result : false; 544 | } catch (\RedisException $ex) { 545 | throw new RedisConnectException(); 546 | } 547 | } 548 | 549 | /** 550 | * Remove ttl from the key 551 | * @param string $key key 552 | * @return bool if true ttl was removed successful, if false ttl did not set, or key not found 553 | * @throws RedisConnectException exception on connection to redis instance 554 | */ 555 | public function persist($key) 556 | { 557 | try { 558 | return $this->getRedis()->persist($key); 559 | } catch (\RedisException $ex) { 560 | throw new RedisConnectException(); 561 | } 562 | } 563 | 564 | /** 565 | * Get key bit 566 | * @param string $key key 567 | * @param int $offset bit offset 568 | * @return int bit value at the offset 569 | * @throws RedisConnectException exception on connection to redis instance 570 | */ 571 | public function getbit($key, $offset) 572 | { 573 | $offset = (int) $offset; 574 | try { 575 | $result = $this->getRedis()->getBit($key, $offset); 576 | if (false !== $result) { 577 | return $result; 578 | } 579 | throw new RedisImpossibleValueException(); 580 | } catch (\RedisException $ex) { 581 | throw new RedisConnectException(); 582 | } 583 | } 584 | 585 | /** 586 | * Set key bit 587 | * @param string $key key 588 | * @param int $offset bit offset 589 | * @param int $value bit value. May be 0 or 1 590 | * @return int bit value before operation complete 591 | * @throws RedisConnectException exception on connection to redis instance 592 | */ 593 | public function setbit($key, $offset, $value) 594 | { 595 | $offset = (int) $offset; 596 | $value = (int) (bool) $value; 597 | try { 598 | $result = $this->getRedis()->setBit($key, $offset, $value); 599 | if (false !== $result) { 600 | return $result; 601 | } 602 | throw new RedisImpossibleValueException(); 603 | } catch (\RedisException $ex) { 604 | throw new RedisConnectException(); 605 | } 606 | } 607 | 608 | /** 609 | * Evaluate Lua code 610 | * @param string $code string of Lua code 611 | * @param array $arguments array of Lua script arguments 612 | * @return mixed code execution result 613 | * @throws RedisConnectException exception on connection to redis instance 614 | * @throws RedisScriptExecutionException when script execution faled 615 | */ 616 | public function evaluate($code, array $arguments = array()) 617 | { 618 | try { 619 | if (empty($arguments)) { 620 | $result = $this->getRedis()->eval($code); 621 | } else { 622 | $result = $this->getRedis()->eval($code, $arguments, count($arguments)); 623 | } 624 | 625 | $lastError = $this->getRedis()->getLastError(); 626 | $this->getRedis()->clearLastError(); 627 | if (is_null($lastError)) { 628 | return $result; 629 | } 630 | throw new RedisScriptExecutionException($lastError); 631 | } catch (\RedisException $ex) { 632 | throw new RedisConnectException(); 633 | } 634 | } 635 | 636 | /** 637 | * Evaluate Lua code by hash 638 | * @param string $sha SHA1 string of Lua code 639 | * @param array $arguments array of Lua script arguments 640 | * @return mixed code execution result 641 | * @throws RedisConnectException exception on connection to redis instance 642 | * @throws RedisScriptExecutionException when script execution faled 643 | */ 644 | public function evalSha($sha, array $arguments = array()) 645 | { 646 | try { 647 | if (empty($arguments)) { 648 | $result = $this->getRedis()->evalSha($sha); 649 | } else { 650 | $result = $this->getRedis()->evalSha($sha, $arguments, count($arguments)); 651 | } 652 | 653 | $lastError = $this->getRedis()->getLastError(); 654 | $this->getRedis()->clearLastError(); 655 | if (is_null($lastError)) { 656 | return $result; 657 | } 658 | throw new RedisScriptExecutionException($lastError); 659 | } catch (\RedisException $ex) { 660 | throw new RedisConnectException(); 661 | } 662 | } 663 | 664 | /** 665 | * Add member to the set 666 | * @param string $key key 667 | * @param mixed $member set member 668 | * @return int count of added members 669 | * @throws RedisConnectException exception on connection to redis instance 670 | */ 671 | public function sadd($key, $member) 672 | { 673 | try { 674 | $result = $this->getRedis()->sAdd($key, $member); 675 | if (false !== $result) { 676 | return $result; 677 | } 678 | throw new RedisImpossibleValueException(); 679 | } catch (\RedisException $ex) { 680 | throw new RedisConnectException(); 681 | } 682 | } 683 | 684 | /** 685 | * Pop (remove and return) a random member from the set 686 | * @param string $key key 687 | * @return mixed set member 688 | * @throws RedisConnectException exception on connection to redis instance 689 | */ 690 | public function spop($key) 691 | { 692 | try { 693 | return $this->getRedis()->sPop($key); 694 | } catch (\RedisException $ex) { 695 | throw new RedisConnectException(); 696 | } 697 | } 698 | 699 | /** 700 | * Return random member from the set 701 | * @param string $key key 702 | * @return mixed set member 703 | * @throws RedisConnectException exception on connection to redis instance 704 | */ 705 | public function srandmember($key) 706 | { 707 | try { 708 | return $this->getRedis()->sRandMember($key); 709 | } catch (\RedisException $ex) { 710 | throw new RedisConnectException(); 711 | } 712 | } 713 | 714 | /** 715 | * Returns size of the set 716 | * @param string $key set 717 | * @return int members count of the set 718 | * @throws RedisConnectException exception on connection to redis instance 719 | */ 720 | public function scard($key) 721 | { 722 | try { 723 | return $this->getRedis()->sCard($key); 724 | } catch (\RedisException $ex) { 725 | throw new RedisConnectException(); 726 | } 727 | } 728 | 729 | /** 730 | * Check that member is a member of the set 731 | * @param string $key key 732 | * @param mixed $member member 733 | * @return bool check result 734 | * @throws RedisConnectException exception on connection to redis instance 735 | */ 736 | public function sismembers($key, $member) 737 | { 738 | try { 739 | return $this->getRedis()->sIsMember($key); 740 | } catch (\RedisException $ex) { 741 | throw new RedisConnectException(); 742 | } 743 | } 744 | 745 | /** 746 | * Returns all members of the set 747 | * @param string $key key 748 | * @return array all members of the set 749 | * @throws RedisConnectException exception on connection to redis instance 750 | */ 751 | public function smembers($key) 752 | { 753 | try { 754 | return $this->getRedis()->sMembers($key); 755 | } catch (\RedisException $ex) { 756 | throw new RedisConnectException(); 757 | } 758 | } 759 | 760 | /** 761 | * Remove member from the set 762 | * @param string $key key 763 | * @param mixed $member set member 764 | * @return int count of removed elements 765 | * @throws RedisConnectException exception on connection to redis instance 766 | */ 767 | public function srem($key, $member) 768 | { 769 | try { 770 | return $this->getRedis()->sRem($key); 771 | } catch (\RedisException $ex) { 772 | throw new RedisConnectException(); 773 | } 774 | } 775 | 776 | /** 777 | * Create difference set 778 | * @param string $destination key for result set 779 | * @param array $sources source keys 780 | * @return int size of result set 781 | * @throws RedisConnectException exception on connection to redis instance 782 | */ 783 | public function sdiffstore($destination, array $sources) 784 | { 785 | try { 786 | return call_user_func_array(array( 787 | $this->getRedis(), 788 | 'sDiffStore', 789 | ), array_merge(array($destination), $sources)); 790 | } catch (\RedisException $ex) { 791 | throw new RedisConnectException(); 792 | } 793 | } 794 | } 795 | 796 | class PhpRedisException extends \Exception 797 | {} 798 | class RedisConnectException extends PhpRedisException 799 | {} 800 | final class RedisTriesOverConnectException extends RedisConnectException 801 | {} 802 | final class RedisNotConfiguredException extends PhpRedisException 803 | {} 804 | final class RedisKeyNotFoundException extends PhpRedisException 805 | {} 806 | final class RedisScriptExecutionException extends PhpRedisException 807 | {} 808 | final class RedisImpossibleValueException extends PhpRedisException 809 | {} 810 | -------------------------------------------------------------------------------- /src/Core/YcfUtils.php: -------------------------------------------------------------------------------- 1 | 2 | [Mysql] 3 | ;host = 119.29.152.216 4 | ;user = db_kcloze 5 | ;password ="@#uwhK677RHuh$%" 6 | 7 | host = 192.168.9.24 8 | user = yongfu_b 9 | password ="%#@SwEsdf43738" 10 | 11 | dbname = test 12 | port = 3306 13 | charset = utf8 -------------------------------------------------------------------------------- /src/Model/ModelBase.php: -------------------------------------------------------------------------------- 1 | _db = $this->load('_db'); 15 | } 16 | 17 | protected function load($obj) 18 | { 19 | switch ($obj) { 20 | case '_db': 21 | return $this->getDbInstance(); 22 | break; 23 | case '_redis': 24 | return $this->getRedisInstance(); 25 | break; 26 | default: 27 | break; 28 | } 29 | } 30 | 31 | protected function getDbInstance() 32 | { 33 | // Create Mysql Client instance with you configuration settings 34 | if (null == $this->_db) { 35 | $this->_db = new YcfDB(YcfCore::$_settings['Mysql']); 36 | } 37 | return $this->_db; 38 | } 39 | protected function getRedisInstance() 40 | { 41 | if (!extension_loaded('redis')) { 42 | throw new \RuntimeException('php redis extension not found'); 43 | return null; 44 | } 45 | // Create Redis Client instance with you configuration settings 46 | $this->_redis = new YcfRedis(YcfCore::$_settings['Redis']); 47 | return $this->_redis; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Model/ModelPdo.php: -------------------------------------------------------------------------------- 1 | _db->query("INSERT INTO pdo_test( pName,pValue) VALUES ( :pName,:pValue)", $data); 14 | if ($insert > 0) { 15 | echo $this->_db->lastInsertId() . "\r\n"; 16 | } else { 17 | echo false . "\r\n"; 18 | } 19 | 20 | } 21 | 22 | public function testQuery() 23 | { 24 | return $this->_db->query("select * from pdo_test limit 1"); 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/Model/ModelProxyDb.php: -------------------------------------------------------------------------------- 1 | connect('127.0.0.1', 9509, 0.5, 0); 16 | $client->send($sql); 17 | return $client->recv(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Model/ModelRedis.php: -------------------------------------------------------------------------------- 1 | _redis = YcfCore::load('_redis'); 12 | } 13 | 14 | public function testRedis() 15 | { 16 | $this->_redis->sadd('test1', 1); 17 | $this->_redis->sadd('test1', 2); 18 | $this->_redis->sadd('test1', 3); 19 | $this->_redis->sadd('test2', 2); 20 | $this->_redis->sdiffstore('test3', array('test1', 'test2')); 21 | var_dump($this->_redis->smembers('test3')); 22 | // Use Redis commands 23 | $this->_redis->set('test', '7'); 24 | var_dump($this->_redis->get('test')); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Model/ModelTask.php: -------------------------------------------------------------------------------- 1 | testInsert(); 27 | sleep(1); 28 | echo "This Task {$task_id} from Worker {$from_id}\n"; 29 | //echo "Data: {$data}\n"; 30 | /*for ($i = 0; $i < 10; $i++) { 31 | sleep(1); 32 | echo "Taks {$task_id} Handle {$i} times...\n"; 33 | */ 34 | //$fd = json_decode($data, true)['fd']; 35 | //$serv->send($fd, "Data in Task {$task_id}"); 36 | echo "Task {$task_id}'s result {$result}"; 37 | } 38 | 39 | public static function flushLogTask($serv, $task_id, $from_id, $data) 40 | { 41 | if (isset($data['content'])) { 42 | YcfCore::$_log && YcfCore::$_log->write($data['content']); 43 | } 44 | echo "Task {$task_id} have done"; 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/runtime/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | 57 | private $classMapAuthoritative = false; 58 | 59 | public function getPrefixes() 60 | { 61 | if (!empty($this->prefixesPsr0)) { 62 | return call_user_func_array('array_merge', $this->prefixesPsr0); 63 | } 64 | 65 | return array(); 66 | } 67 | 68 | public function getPrefixesPsr4() 69 | { 70 | return $this->prefixDirsPsr4; 71 | } 72 | 73 | public function getFallbackDirs() 74 | { 75 | return $this->fallbackDirsPsr0; 76 | } 77 | 78 | public function getFallbackDirsPsr4() 79 | { 80 | return $this->fallbackDirsPsr4; 81 | } 82 | 83 | public function getClassMap() 84 | { 85 | return $this->classMap; 86 | } 87 | 88 | /** 89 | * @param array $classMap Class to filename map 90 | */ 91 | public function addClassMap(array $classMap) 92 | { 93 | if ($this->classMap) { 94 | $this->classMap = array_merge($this->classMap, $classMap); 95 | } else { 96 | $this->classMap = $classMap; 97 | } 98 | } 99 | 100 | /** 101 | * Registers a set of PSR-0 directories for a given prefix, either 102 | * appending or prepending to the ones previously set for this prefix. 103 | * 104 | * @param string $prefix The prefix 105 | * @param array|string $paths The PSR-0 root directories 106 | * @param bool $prepend Whether to prepend the directories 107 | */ 108 | public function add($prefix, $paths, $prepend = false) 109 | { 110 | if (!$prefix) { 111 | if ($prepend) { 112 | $this->fallbackDirsPsr0 = array_merge( 113 | (array) $paths, 114 | $this->fallbackDirsPsr0 115 | ); 116 | } else { 117 | $this->fallbackDirsPsr0 = array_merge( 118 | $this->fallbackDirsPsr0, 119 | (array) $paths 120 | ); 121 | } 122 | 123 | return; 124 | } 125 | 126 | $first = $prefix[0]; 127 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 128 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 129 | 130 | return; 131 | } 132 | if ($prepend) { 133 | $this->prefixesPsr0[$first][$prefix] = array_merge( 134 | (array) $paths, 135 | $this->prefixesPsr0[$first][$prefix] 136 | ); 137 | } else { 138 | $this->prefixesPsr0[$first][$prefix] = array_merge( 139 | $this->prefixesPsr0[$first][$prefix], 140 | (array) $paths 141 | ); 142 | } 143 | } 144 | 145 | /** 146 | * Registers a set of PSR-4 directories for a given namespace, either 147 | * appending or prepending to the ones previously set for this namespace. 148 | * 149 | * @param string $prefix The prefix/namespace, with trailing '\\' 150 | * @param array|string $paths The PSR-4 base directories 151 | * @param bool $prepend Whether to prepend the directories 152 | * 153 | * @throws \InvalidArgumentException 154 | */ 155 | public function addPsr4($prefix, $paths, $prepend = false) 156 | { 157 | if (!$prefix) { 158 | // Register directories for the root namespace. 159 | if ($prepend) { 160 | $this->fallbackDirsPsr4 = array_merge( 161 | (array) $paths, 162 | $this->fallbackDirsPsr4 163 | ); 164 | } else { 165 | $this->fallbackDirsPsr4 = array_merge( 166 | $this->fallbackDirsPsr4, 167 | (array) $paths 168 | ); 169 | } 170 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 171 | // Register directories for a new namespace. 172 | $length = strlen($prefix); 173 | if ('\\' !== $prefix[$length - 1]) { 174 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 175 | } 176 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 177 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 178 | } elseif ($prepend) { 179 | // Prepend directories for an already registered namespace. 180 | $this->prefixDirsPsr4[$prefix] = array_merge( 181 | (array) $paths, 182 | $this->prefixDirsPsr4[$prefix] 183 | ); 184 | } else { 185 | // Append directories for an already registered namespace. 186 | $this->prefixDirsPsr4[$prefix] = array_merge( 187 | $this->prefixDirsPsr4[$prefix], 188 | (array) $paths 189 | ); 190 | } 191 | } 192 | 193 | /** 194 | * Registers a set of PSR-0 directories for a given prefix, 195 | * replacing any others previously set for this prefix. 196 | * 197 | * @param string $prefix The prefix 198 | * @param array|string $paths The PSR-0 base directories 199 | */ 200 | public function set($prefix, $paths) 201 | { 202 | if (!$prefix) { 203 | $this->fallbackDirsPsr0 = (array) $paths; 204 | } else { 205 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 206 | } 207 | } 208 | 209 | /** 210 | * Registers a set of PSR-4 directories for a given namespace, 211 | * replacing any others previously set for this namespace. 212 | * 213 | * @param string $prefix The prefix/namespace, with trailing '\\' 214 | * @param array|string $paths The PSR-4 base directories 215 | * 216 | * @throws \InvalidArgumentException 217 | */ 218 | public function setPsr4($prefix, $paths) 219 | { 220 | if (!$prefix) { 221 | $this->fallbackDirsPsr4 = (array) $paths; 222 | } else { 223 | $length = strlen($prefix); 224 | if ('\\' !== $prefix[$length - 1]) { 225 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 226 | } 227 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 228 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 229 | } 230 | } 231 | 232 | /** 233 | * Turns on searching the include path for class files. 234 | * 235 | * @param bool $useIncludePath 236 | */ 237 | public function setUseIncludePath($useIncludePath) 238 | { 239 | $this->useIncludePath = $useIncludePath; 240 | } 241 | 242 | /** 243 | * Can be used to check if the autoloader uses the include path to check 244 | * for classes. 245 | * 246 | * @return bool 247 | */ 248 | public function getUseIncludePath() 249 | { 250 | return $this->useIncludePath; 251 | } 252 | 253 | /** 254 | * Turns off searching the prefix and fallback directories for classes 255 | * that have not been registered with the class map. 256 | * 257 | * @param bool $classMapAuthoritative 258 | */ 259 | public function setClassMapAuthoritative($classMapAuthoritative) 260 | { 261 | $this->classMapAuthoritative = $classMapAuthoritative; 262 | } 263 | 264 | /** 265 | * Should class lookup fail if not found in the current class map? 266 | * 267 | * @return bool 268 | */ 269 | public function isClassMapAuthoritative() 270 | { 271 | return $this->classMapAuthoritative; 272 | } 273 | 274 | /** 275 | * Registers this instance as an autoloader. 276 | * 277 | * @param bool $prepend Whether to prepend the autoloader or not 278 | */ 279 | public function register($prepend = false) 280 | { 281 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 282 | } 283 | 284 | /** 285 | * Unregisters this instance as an autoloader. 286 | */ 287 | public function unregister() 288 | { 289 | spl_autoload_unregister(array($this, 'loadClass')); 290 | } 291 | 292 | /** 293 | * Loads the given class or interface. 294 | * 295 | * @param string $class The name of the class 296 | * @return bool|null True if loaded, null otherwise 297 | */ 298 | public function loadClass($class) 299 | { 300 | if ($file = $this->findFile($class)) { 301 | includeFile($file); 302 | 303 | return true; 304 | } 305 | } 306 | 307 | /** 308 | * Finds the path to the file where the class is defined. 309 | * 310 | * @param string $class The name of the class 311 | * 312 | * @return string|false The path if found, false otherwise 313 | */ 314 | public function findFile($class) 315 | { 316 | // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 317 | if ('\\' == $class[0]) { 318 | $class = substr($class, 1); 319 | } 320 | 321 | // class map lookup 322 | if (isset($this->classMap[$class])) { 323 | return $this->classMap[$class]; 324 | } 325 | if ($this->classMapAuthoritative) { 326 | return false; 327 | } 328 | 329 | $file = $this->findFileWithExtension($class, '.php'); 330 | 331 | // Search for Hack files if we are running on HHVM 332 | if ($file === null && defined('HHVM_VERSION')) { 333 | $file = $this->findFileWithExtension($class, '.hh'); 334 | } 335 | 336 | if ($file === null) { 337 | // Remember that this class does not exist. 338 | return $this->classMap[$class] = false; 339 | } 340 | 341 | return $file; 342 | } 343 | 344 | private function findFileWithExtension($class, $ext) 345 | { 346 | // PSR-4 lookup 347 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 348 | 349 | $first = $class[0]; 350 | if (isset($this->prefixLengthsPsr4[$first])) { 351 | foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { 352 | if (0 === strpos($class, $prefix)) { 353 | foreach ($this->prefixDirsPsr4[$prefix] as $dir) { 354 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { 355 | return $file; 356 | } 357 | } 358 | } 359 | } 360 | } 361 | 362 | // PSR-4 fallback dirs 363 | foreach ($this->fallbackDirsPsr4 as $dir) { 364 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 365 | return $file; 366 | } 367 | } 368 | 369 | // PSR-0 lookup 370 | if (false !== $pos = strrpos($class, '\\')) { 371 | // namespaced class name 372 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 373 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 374 | } else { 375 | // PEAR-like class name 376 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 377 | } 378 | 379 | if (isset($this->prefixesPsr0[$first])) { 380 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 381 | if (0 === strpos($class, $prefix)) { 382 | foreach ($dirs as $dir) { 383 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 384 | return $file; 385 | } 386 | } 387 | } 388 | } 389 | } 390 | 391 | // PSR-0 fallback dirs 392 | foreach ($this->fallbackDirsPsr0 as $dir) { 393 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 394 | return $file; 395 | } 396 | } 397 | 398 | // PSR-0 include paths. 399 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 400 | return $file; 401 | } 402 | } 403 | } 404 | 405 | /** 406 | * Scope isolated include. 407 | * 408 | * Prevents access to $this/self from included files. 409 | */ 410 | function includeFile($file) 411 | { 412 | include $file; 413 | } 414 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2016 Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | $path) { 28 | $loader->set($namespace, $path); 29 | } 30 | 31 | $map = require __DIR__ . '/autoload_psr4.php'; 32 | foreach ($map as $namespace => $path) { 33 | $loader->setPsr4($namespace, $path); 34 | } 35 | 36 | $classMap = require __DIR__ . '/autoload_classmap.php'; 37 | if ($classMap) { 38 | $loader->addClassMap($classMap); 39 | } 40 | 41 | $loader->register(true); 42 | 43 | return $loader; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [] 2 | --------------------------------------------------------------------------------