├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── examples └── yii2 │ └── storage │ ├── config │ ├── bootstrap.php │ ├── ini │ │ └── server.ini │ ├── main-beta.php │ ├── main-dev.php │ ├── main-huidu.php │ ├── main-prod.php │ ├── main-test.php │ ├── main.php │ └── params.php │ ├── crontab │ └── TestCron.php │ ├── helpers │ └── StringHelper.php │ ├── modules │ └── v1 │ │ ├── Module.php │ │ └── controllers │ │ ├── BaseController.php │ │ ├── DemoController.php │ │ └── StorageController.php │ ├── service │ └── UploadService.php │ └── web │ └── start.php └── src ├── Module.php ├── Start.php ├── classes.php ├── db ├── Command.php └── Connection.php ├── di └── Container.php ├── redis ├── Connection.php └── Session.php ├── traits └── SessionTrait.php └── web ├── Application.php ├── Controller.php ├── ErrorHandler.php ├── Request.php ├── Response.php ├── Session.php └── User.php /.gitignore: -------------------------------------------------------------------------------- 1 | ### Yii template 2 | api/runtime 3 | app/runtime 4 | console/runtime 5 | backend/runtime 6 | */assets/* 7 | !*/assets/.gitignore 8 | */runtime/* 9 | !*/runtime/.gitignore 10 | .idea 11 | # Created by .ignore support plugin (hsz.mobi) 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © Lu Shun Cheng (https://github.com/lscgzwd) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [yiiswoole](https://github.com/lsccgzwd/yii2-swoole) 2 | Yii2 run with swoole 3 | # Requirements 4 | * swoole 1.9.18 5 | * briarbear/briarbear 1.0.0 6 | * yiisoft/yii2 2.0.8 7 | * php 7.0 8 | 9 | # Feature: 10 | * Elastic Job 11 | * Dubbo (developing) 12 | * Compute Cluster (developing) 13 | 14 | # Usage 15 | * composer install briarbear/yii2 16 | * modify the examples\yii2\storage\web\start.php, correct the path. You can give $config to the run method or set the property of Start class. 17 | * $config must have server and logger key. view examples\yii2\storage\config\params.php 18 | * if your want to use crontab, must config the zookeeperHost, otherwise set START_CRONTAB to false 19 | * by default we use config separate, you should mkdir -p /data/conf/qiye then cp the ini file to that directory 20 | * run php examples/yii2/storage/web/start.php restart to start the service 21 | * curl -d "" http://127.0.0.1:9502/v1/demo/index 22 | 23 | # 使用方法 24 | * 使用composer安装本程序,package: briarbear/yii2 25 | * 修改examples里面的试例 26 | * Start类支持直接配置属性,自动加载Yii2的配置文件,也可以通过run方法的参数传入配置数组 27 | * 如果要使用定时任务功能,需要配置zookeeper, 否则把常量START_CRONTAB 设置成false 28 | * 我们线上使用了配置分离,你可能需要将ini文件夹中的配置文件拷贝到对应目录 29 | * 执行php start.php restart 启动服务 30 | 31 | 32 | # Upgrade 33 | * 2017-08-29 support composer 34 | 35 | # Contact 36 | * QQ: 1474212 37 | * EMAIL: lscgzwd@gmail.com 38 | * 欢迎直接提issues, welcome pull request and submit issues 39 | 40 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "briarbear/yii2", 3 | "description": "Run Yii2 With Swoole.", 4 | "keywords": [ 5 | "yii2", 6 | "swoole", 7 | "yii2", 8 | "BriarBear" 9 | ], 10 | "homepage": "https://github.com/lscgzwd/briarbear", 11 | "license": "Apache License 2.0", 12 | "authors": [ 13 | { 14 | "name": "lscgzwd", 15 | "email": "lscgzwd@gmail.com", 16 | "homepage": "http://github.com/lscgzwd" 17 | } 18 | ], 19 | "autoload": { 20 | "psr-4": { 21 | "yiiswoole\\": "src/" 22 | } 23 | }, 24 | "require": { 25 | "php":">=7.0", 26 | "briarbear/briarbear": "~1.0.0", 27 | "yiisoft/yii2": "~2.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 12:26 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | Yii::setAlias('@storage', dirname(__DIR__)); 13 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/ini/server.ini: -------------------------------------------------------------------------------- 1 | [env] 2 | env = "dev" 3 | 4 | [redis] 5 | hostname = "192.168.2.1" 6 | port = 6379 7 | database = 0 8 | 9 | [idGen] 10 | ; ID生成器 11 | host="http://100.73.16.32:5021/id?type=userid" 12 | 13 | [enterprise] 14 | ; 15 | host="http://100.73.17.33:8888" 16 | 17 | [server] 18 | setting.user = "nginx" 19 | setting.group = "nginx" 20 | setting.worker_num = 4 21 | ; 服务器的IP 22 | serverIP = "192.168.2.1" 23 | crontab.zookeeperHost.0 = "domain://zk.test.com:2181" 24 | 25 | [buckets] 26 | qiye.authCallbackUrl = "http://192.168.2.2/public/check-over-time" 27 | 28 | [storage] 29 | directory = "/tmp/storage/" -------------------------------------------------------------------------------- /examples/yii2/storage/config/main-beta.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'log' => [ 5 | 'targets' => [ 6 | 'email' => [ 7 | 'message' => [ 8 | 'subject' => 'beta 日志报警', 9 | ], 10 | ], 11 | ], 12 | ], 13 | 'redis' => [], 14 | ], 15 | ]; 16 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/main-dev.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'log' => [ 5 | 'targets' => [ 6 | 'email' => [ 7 | 'message' => [ 8 | 'subject' => 'dev 日志报警', 9 | ], 10 | ], 11 | ], 12 | ], 13 | 'redis' => [], 14 | ], 15 | ]; 16 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/main-huidu.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'db' => [ 5 | 'enableSchemaCache' => true, 6 | ], 7 | 'log' => [ 8 | 'targets' => [ 9 | 'email' => [ 10 | 'message' => [ 11 | 'subject' => 'prod 日志报警', 12 | ], 13 | ], 14 | ], 15 | ], 16 | 'redis' => [], 17 | ], 18 | ]; 19 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/main-prod.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'db' => [ 5 | 'enableSchemaCache' => true, 6 | ], 7 | 'log' => [ 8 | 'targets' => [ 9 | 'email' => [ 10 | 'message' => [ 11 | 'subject' => 'prod 日志报警', 12 | ], 13 | ], 14 | ], 15 | ], 16 | 'redis' => [], 17 | ], 18 | ]; 19 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/main-test.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'log' => [ 5 | 'targets' => [ 6 | 'email' => [ 7 | 'message' => [ 8 | 'subject' => 'TEST 日志报警', 9 | ], 10 | ], 11 | ], 12 | ], 13 | 'redis' => [], 14 | ], 15 | ]; 16 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/main.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 12:27 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | return [ 13 | 'id' => 'app-storage', 14 | 'basePath' => dirname(__DIR__), 15 | 'bootstrap' => ['log'], 16 | 'controllerNamespace' => 'storage\controllers', 17 | 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 18 | 'timeZone' => 'PRC', 19 | 'language' => 'zh-CN', 20 | 'sourceLanguage' => 'zh-CN', 21 | 'defaultRoute' => 'demo/index', 22 | 'runtimePath' => '/tmp/storage', 23 | 'modules' => [ 24 | 'v1' => [ 25 | 'class' => 'storage\modules\v1\Module', 26 | ], 27 | ], 28 | 'components' => [ 29 | 'db' => [ 30 | 'class' => 'yiiswoole\db\Connection', 31 | ], 32 | 'request' => [ 33 | 'enableCsrfValidation' => false, 34 | 'enableCookieValidation' => false, 35 | 'class' => 'yiiswoole\web\Request', 36 | ], 37 | 'urlManager' => [ 38 | 'enablePrettyUrl' => true, 39 | 'showScriptName' => false, 40 | 'cache' => 'schemaCache', 41 | 'rules' => [ 42 | 'v1/demo/' => 'v1/demo/', 43 | 'PUT v1/' => 'v1/storage/put', 44 | 'DELETE v1/' => 'v1/storage/delete', 45 | 'GET v1/' => 'v1/storage/get', 46 | 'HEAD v1/' => 'v1/storage/head', 47 | 'POST v1/' => 'v1/storage/post', 48 | ], 49 | ], 50 | 'cache' => [ 51 | 'class' => 'yii\redis\Cache', 52 | 'redis' => 'redis', 53 | 'keyPrefix' => 'jdb_enterprise:cache', 54 | ], 55 | 'schemaCache' => [ 56 | 'class' => 'yii\caching\FileCache', 57 | ], 58 | 'response' => [ 59 | 'charset' => 'UTF-8', 60 | 'class' => 'yiiswoole\web\Response', 61 | 'format' => 'json', 62 | ], 63 | 'session' => [ 64 | 'class' => 'yiiswoole\redis\Session', 65 | 'keyPrefix' => 'jdb_enterprise:session', 66 | ], 67 | 'formatter' => [ 68 | 'class' => 'yii\i18n\Formatter', 69 | 'dateFormat' => 'php:Y-m-d', 70 | 'datetimeFormat' => 'php:Y-m-d H:i:s', 71 | 'timeFormat' => 'php:H:i:s', 72 | 'defaultTimeZone' => 'PRC', 73 | ], 74 | 'redis' => [ 75 | 'class' => 'yiiswoole\redis\Connection', 76 | 'hostname' => '127.0.0.1', 77 | 'port' => 6379, 78 | 'database' => 0, 79 | ], 80 | 'smtp' => [ 81 | 'class' => 'yii\swiftmailer\Mailer', 82 | 'transport' => [ 83 | 'class' => 'Swift_SmtpTransport', 84 | 'host' => 'smtp.google.com', 85 | 'username' => 'lscgzwd@gmail.com', 86 | 'password' => '123456', 87 | 'port' => '25', 88 | ], 89 | ], 90 | 'log' => [ 91 | 'traceLevel' => YII_DEBUG ? 3 : 0, 92 | 'targets' => [ 93 | 'logStash' => [ 94 | 'class' => 'yiilog\LogstashFileTarget', 95 | 'categories' => ['application*', 'yii*', 'apps*', 'api*', 'common*', 'console*', 'activity*'], 96 | 'logFile' => '', 97 | 'logPath' => '@runtime', 98 | 'logFileSuffix' => '_logstash', 99 | 'logFileExt' => '.log', 100 | 'logFilePrefix' => 'storage_', 101 | 'levels' => ['info', 'error', 'warning'], 102 | 'logVars' => [], 103 | 'exportInterval' => 100, 104 | 'maxFileSize' => 2048000, 105 | 'maxLogFiles' => 10, 106 | 'rotateByCopy' => false, 107 | 'fileMode' => 0777, 108 | ], 109 | 'email' => [ 110 | 'class' => 'yiilog\EmailTarget', 111 | 'categories' => ['application', 'yii*', 'apps*', 'storage*', 'common*', 'console*', 'activity*'], 112 | 'except' => ['yii\web\HttpException:404'], 113 | 'levels' => ['error'], 114 | 'mailer' => 'smtp', 115 | 'logVars' => [], 116 | 'message' => [ 117 | 'subject' => '日志报警', 118 | 'from' => ['lscgzwd@gmail.com'], 119 | 'to' => ['lscgzwd@gmail.com'], 120 | ], 121 | ], 122 | ], 123 | ], 124 | ], 125 | ]; 126 | -------------------------------------------------------------------------------- /examples/yii2/storage/config/params.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 12:27 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | return [ 13 | 'buckets' => [ 14 | 'lusc' => [ 15 | 'operator' => 'lusc', 16 | 'password' => '12345678', 17 | 'authCallbackUrl' => '', 18 | 'authParamName' => 'key', 19 | ], 20 | 'test' => [ 21 | 22 | ], 23 | ], 24 | 'storage' => ['directory' => ''], 25 | 'env' => [], 26 | 'idGen' => [], 27 | 'logger' => [ 28 | 'class' => 'BriarBear\\Log\\FileLogger', 29 | 'logPath' => '/data/logs/storage/', 30 | ], 31 | 'server' => [ 32 | 'setting' => [ 33 | 'worker_num' => 4, //worker process num 34 | 'backlog' => 16, //listen backlog 35 | 'max_request' => 5000, 36 | 'task_worker_num' => 4, 37 | 'dispatch_mode' => 2, // must been 2, if not ,you can not get a full package for each request 38 | 'open_tcp_nodelay' => 1, 39 | 'enable_reuse_port' => 1, 40 | 'log_file' => '/data/logs/storage/BriarBearServer.log', 41 | 'log_level' => 0, 42 | 'daemonize' => 1, 43 | 'user' => 'nginx', 44 | 'group' => 'nginx', 45 | ], 46 | 'host' => '0.0.0.0', 47 | 'port' => '80', 48 | 'openHttpProtocol' => true, 49 | 'httpGetMaxSize' => 8192, 50 | 'httpPostMaxSize' => 52428800, // 50MB 51 | 'tcpMaxPackageSize' => 1024000, // 1MB 52 | 'pidFile' => '/data/logs/storage/server.pid', 53 | 'httpStaticRoot' => WEB_PATH, 54 | 'serverIP' => '', 55 | 'serverName' => 'storage', // server process name 56 | 'gzip' => 1, 57 | 'keepalive' => 1, 58 | 'webSocket' => [ 59 | 'port' => '9502', 60 | 'host' => '0.0.0.0', 61 | ], 62 | 'class' => 'BriarBear\\Server', 63 | 'crontab' => [ 64 | 'cronList' => [ 65 | [ 66 | 'rule' => '1 */1 * * * *', 67 | 'class' => '\common\crontab\TestCron', 68 | 'method' => 'test', 69 | ], 70 | ], 71 | 'zookeeperHost' => [ 72 | 'domain://127.0.0.1:2118' 73 | ], 74 | ], 75 | ], 76 | ]; 77 | -------------------------------------------------------------------------------- /examples/yii2/storage/crontab/TestCron.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 15:49 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | 13 | namespace storage\helpers; 14 | 15 | class StringHelper 16 | { 17 | /** 18 | * ID 生成器,生成long型唯一数字 19 | * @return string 20 | */ 21 | public static function uuid($url = '') 22 | { 23 | $opts = array( 24 | 'http' => array( 25 | 'method' => 'GET', 26 | 'timeout' => 1, 27 | ), 28 | ); 29 | $context = stream_context_create($opts); 30 | if ($url == '') { 31 | $url = \Yii::$app->params['idGen']['host']; 32 | } 33 | return file_get_contents($url, false, $context); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/yii2/storage/modules/v1/Module.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 14:03 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | 13 | namespace storage\modules\v1; 14 | 15 | class Module extends \yii\base\Module 16 | { 17 | public $controllerNamespace = 'storage\modules\v1\controllers'; 18 | } 19 | -------------------------------------------------------------------------------- /examples/yii2/storage/modules/v1/controllers/BaseController.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/9 6 | * Time: 17:23 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | 13 | namespace storage\modules\v1\controllers; 14 | 15 | use yiiswoole\web\Controller; 16 | 17 | class BaseController extends Controller 18 | { 19 | protected $requestBegin; 20 | /** 21 | * @param $id 22 | * @param array $params 23 | * @return mixed 24 | */ 25 | public function runAction($id, $params = []) 26 | { 27 | $this->requestBegin = microtime(true) * 1000; // record request begin time 28 | // record request 29 | $this->logRequest($id); 30 | 31 | $result = parent::runAction($id, $params); // TODO: Change the autogenerated stub 32 | // record the response 33 | $this->logResponse($result, $id); 34 | return $result; 35 | } 36 | /** 37 | * log the request 38 | */ 39 | public function logRequest($actionId = '') 40 | { 41 | \Start::$instance->addLog('controller_request_begin', 'info', [ 42 | 'post' => \Yii::$app->getRequest()->post(), 43 | 'get' => \Yii::$app->getRequest()->get(), 44 | 'server' => $_SERVER, 45 | 'header' => \Yii::$app->getRequest()->getHeaders(), 46 | ], $this->id . '-' . (empty($actionId) ? 'default' : $actionId)); 47 | } 48 | 49 | /** 50 | * log the output 51 | * @param $content 52 | */ 53 | public function logResponse($content, $actionId = '') 54 | { 55 | $elapsed = microtime(true) * 1000 - $this->requestBegin; 56 | \Start::$instance->addLog('controller_request_end', 'info', [ 57 | 'content' => $content, 58 | 'elapsed' => floatval($elapsed), // time the request cost 59 | ], $this->id . '-' . (empty($actionId) ? 'default' : $actionId)); 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/yii2/storage/modules/v1/controllers/DemoController.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 20:18 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | 13 | namespace storage\modules\v1\controllers; 14 | 15 | class DemoController extends BaseController 16 | { 17 | public function actionIndex() 18 | { 19 | \Yii::$app->getSession()->open(); 20 | \Yii::$app->getResponse()->format = 'json'; 21 | $_SESSION['abc'] = microtime(true); 22 | return $_SESSION; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/yii2/storage/modules/v1/controllers/StorageController.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/7 6 | * Time: 11:54 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | 13 | namespace storage\modules\v1\controllers; 14 | 15 | use storage\service\UploadService; 16 | use yiiswoole\web\Response; 17 | 18 | class StorageController extends BaseController 19 | { 20 | public function actionPut() 21 | { 22 | $uri = str_replace('/v1', '', $_SERVER['REQUEST_URI']); 23 | $headers = \Yii::$app->getRequest()->getHeaders(); 24 | $authorization = $headers['Authorization'] ?? ''; 25 | $method = $_SERVER['REQUEST_METHOD']; 26 | $date = $headers['date'] ?? ''; 27 | $contentMD5 = $headers['Content-MD5'] ?? ''; 28 | $putTempFile = \Yii::$app->getRequest()->request->getPutRawTempFile(); 29 | \Yii::$app->getResponse()->format = 'json'; 30 | try { 31 | $config = [ 32 | 'uri' => urldecode($uri), 33 | 'authorization' => $authorization, 34 | 'method' => $method, 35 | 'date' => $date, 36 | 'contentMD5' => $contentMD5, 37 | 'putTempFile' => $putTempFile, 38 | 'uploadDir' => \Yii::$app->params['storage']['directory'], 39 | ]; 40 | $service = new UploadService($config); 41 | $service->doUpload(); 42 | return ['status' => 200]; 43 | } catch (\Throwable $e) { 44 | $code = $e->getCode(); 45 | if (array_key_exists($code, Response::$httpStatuses)) { 46 | \Yii::$app->getResponse()->setStatusCode($code); 47 | } else { 48 | \Yii::$app->getResponse()->setStatusCode(500); 49 | } 50 | 51 | return $e->__toString(); 52 | } 53 | } 54 | public function actionPost() 55 | { 56 | $uri = str_replace('/v1', '', $_SERVER['REQUEST_URI']); 57 | $headers = \Yii::$app->getRequest()->getHeaders(); 58 | $authorization = $headers['Authorization'] ?? ''; 59 | $method = $_SERVER['REQUEST_METHOD']; 60 | $date = $headers['date'] ?? ''; 61 | $contentMD5 = $headers['Content-MD5'] ?? ''; 62 | \Yii::$app->getResponse()->format = 'json'; 63 | try { 64 | $config = [ 65 | 'uri' => $uri, 66 | 'authorization' => $authorization, 67 | 'method' => $method, 68 | 'date' => $date, 69 | 'contentMD5' => $contentMD5, 70 | 'uploadDir' => \Yii::$app->params['storage']['directory'], 71 | ]; 72 | $service = new UploadService($config); 73 | $service->doUpload(); 74 | return ['status' => 200]; 75 | } catch (\Throwable $e) { 76 | $code = $e->getCode(); 77 | if (array_key_exists($code, Response::$httpStatuses)) { 78 | \Yii::$app->getResponse()->setStatusCode($code); 79 | } else { 80 | \Yii::$app->getResponse()->setStatusCode(500); 81 | } 82 | 83 | return $e->__toString(); 84 | } 85 | } 86 | public function actionDelete() 87 | { 88 | $uri = str_replace('/v1', '', $_SERVER['REQUEST_URI']); 89 | $headers = \Yii::$app->getRequest()->getHeaders(); 90 | $authorization = $headers['Authorization'] ?? ''; 91 | $method = $_SERVER['REQUEST_METHOD']; 92 | $date = $headers['date'] ?? ''; 93 | \Yii::$app->getResponse()->format = 'json'; 94 | try { 95 | $config = [ 96 | 'uri' => $uri, 97 | 'authorization' => $authorization, 98 | 'method' => $method, 99 | 'date' => $date, 100 | 'uploadDir' => \Yii::$app->params['storage']['directory'], 101 | ]; 102 | $service = new UploadService($config); 103 | $service->doDelete(); 104 | return ['status' => 200]; 105 | } catch (\Throwable $e) { 106 | $code = $e->getCode(); 107 | if (array_key_exists($code, Response::$httpStatuses)) { 108 | \Yii::$app->getResponse()->setStatusCode($code); 109 | } else { 110 | \Yii::$app->getResponse()->setStatusCode(500); 111 | } 112 | 113 | return $e->__toString(); 114 | } 115 | } 116 | 117 | /** 118 | * @return string 119 | */ 120 | public function actionGet() 121 | { 122 | $uri = str_replace('/v1', '', $_SERVER['REQUEST_URI']); 123 | $uri = parse_url($uri, PHP_URL_PATH); 124 | file_put_contents('/data/logs/lusc.txt', $uri, FILE_APPEND); 125 | $uri = urldecode($uri); 126 | try { 127 | $service = new UploadService([ 128 | 'uri' => $uri, 129 | 'uploadDir' => \Yii::$app->params['storage']['directory'], 130 | ]); 131 | $file = $service->doGet(); 132 | \Yii::$app->getResponse()->xSendFile($file); 133 | } catch (\Throwable $e) { 134 | \Yii::$app->getResponse()->setStatusCode($e->getCode()); 135 | return $e->getMessage(); 136 | } 137 | } 138 | /** 139 | * @return string 140 | */ 141 | public function actionHead() 142 | { 143 | $uri = str_replace('/v1', '', $_SERVER['REQUEST_URI']); 144 | $uri = parse_url($uri, PHP_URL_PATH); 145 | try { 146 | $service = new UploadService([ 147 | 'uri' => $uri, 148 | 'uploadDir' => \Yii::$app->params['storage']['directory'], 149 | ]); 150 | $file = $service->doGet(); 151 | $finfo = new \finfo(FILEINFO_MIME_TYPE); 152 | $mimeType = $finfo->file($file); 153 | clearstatcache(true, $file); 154 | $info = stat($file); 155 | $headers = \Yii::$app->getResponse()->getHeaders(); 156 | $headers->add('x-mime-type', $mimeType); 157 | $headers->add('x-create-time', $info['ctime']); 158 | $headers->add('x-size', $info['size']); 159 | } catch (\Throwable $e) { 160 | \Yii::$app->getResponse()->setStatusCode($e->getCode()); 161 | return $e->getMessage(); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /examples/yii2/storage/service/UploadService.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/7 6 | * Time: 11:12 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | 13 | namespace storage\service; 14 | 15 | use BriarBear\Exception\InvalidCallException; 16 | use BriarBear\Exception\InvalidParamException; 17 | use BriarBear\Helpers\FileHelper; 18 | use yii\base\ErrorException; 19 | use yii\base\Object; 20 | 21 | class UploadService extends Object 22 | { 23 | public $method; 24 | public $date; 25 | public $authorization; 26 | public $uri; 27 | public $contentMD5; 28 | public $bucket; 29 | public $operator; 30 | public $password; 31 | public $putTempFile; 32 | public $uploadDir = '/data/glusterfs'; 33 | public $authCallbackUrl; 34 | public $authParamName; 35 | 36 | public function doUpload() 37 | { 38 | $this->initBucket()->checkDate()->checkSign(); 39 | $target = rtrim($this->uploadDir, '/') . '/' . ltrim($this->uri, '/'); 40 | // if (is_file($target)) { 41 | // throw new InvalidCallException('File already exist', 400); 42 | // } 43 | $dir = dirname($target); 44 | if (!is_dir($dir)) { 45 | FileHelper::createDirectory($dir); 46 | } 47 | switch ($this->method) { 48 | case 'PUT': 49 | $ret = rename($this->putTempFile, $target); 50 | if ($ret === false) { 51 | throw new ErrorException('Save file failed', 500); 52 | } 53 | break; 54 | case 'POST': 55 | if (empty($_FILES) || !isset($_FILES['file']) || count($_FILES) > 1) { 56 | throw new InvalidCallException('Upload file needed, Only one file allowed each request', 400); 57 | } 58 | $ret = rename($_FILES['file']['tmp_name'], $target); 59 | if ($ret === false) { 60 | throw new ErrorException('Save file failed', 500); 61 | } 62 | break; 63 | default: 64 | throw new InvalidCallException('Method not allowed', 405); 65 | break; 66 | } 67 | return true; 68 | } 69 | public function doDelete() 70 | { 71 | $this->initBucket()->checkDate()->checkSign(); 72 | $target = rtrim($this->uploadDir, '/') . '/' . ltrim($this->uri, '/'); 73 | if (!is_file($target)) { 74 | throw new InvalidCallException('File not exist', 404); 75 | } 76 | unlink($target); 77 | return true; 78 | } 79 | public function doGet() 80 | { 81 | $this->initBucket(); 82 | if ($this->checkAuth() === false) { 83 | throw new \yii\base\InvalidCallException('Forbidden', 403); 84 | } 85 | $file = rtrim($this->uploadDir, '/') . '/' . ltrim($this->uri, '/'); 86 | if (!is_file($file)) { 87 | throw new ErrorException('File Not Exist', 404); 88 | } 89 | return $file; 90 | } 91 | protected function checkAuth() 92 | { 93 | if ($this->authParamName && $this->authCallbackUrl) { 94 | $auth = $_GET[$this->authParamName] ?? ''; 95 | if ($auth) { 96 | $postData = [ 97 | $this->authParamName => $auth, 98 | 'uri' => $this->uri, 99 | ]; 100 | $data = http_build_query($postData); 101 | $opts = array( 102 | 'http' => array( 103 | 'method' => 'POST', 104 | 'timeout' => 1, 105 | 'header' => "Content-type: application/x-www-form-urlencoded\r\n" . 106 | "Content-length:" . strlen($data) . "\r\n" . 107 | "\r\n", 108 | 'content' => $data, 109 | ), 110 | ); 111 | $context = stream_context_create($opts); 112 | $ret = file_get_contents($this->authCallbackUrl, false, $context); 113 | $ret = json_decode($ret, true); 114 | if (isset($ret['error']['returnCode']) && $ret['error']['returnCode'] == 0) { 115 | return true; 116 | } else { 117 | return false; 118 | } 119 | } 120 | } 121 | return true; 122 | } 123 | protected function checkSign() 124 | { 125 | if (!$this->authorization) { 126 | throw new InvalidParamException('Authorization header required', 400); 127 | } 128 | $arr = explode(':', $this->authorization); 129 | if (count($arr) !== 2) { 130 | throw new InvalidParamException('Authorization invalid'); 131 | } 132 | $str = trim($arr[0]); 133 | 134 | $operator = trim(substr($str, strrpos($str, ' '))); 135 | if ($operator != $this->operator) { 136 | throw new InvalidParamException('Operator invalid', 400); 137 | } 138 | $sign = $arr[1]; 139 | if ($sign != $this->getSign()) { 140 | throw new InvalidParamException('Sign invalid', 400); 141 | } 142 | } 143 | protected function getSign(): String 144 | { 145 | $data = array( 146 | $this->method, 147 | $this->uri, 148 | $this->date, 149 | ); 150 | if ($this->contentMD5) { 151 | $data[] = $this->contentMD5; 152 | } 153 | file_put_contents('/data/logs/lusc.txt', json_encode([ 154 | 'str' => implode('&', $data), 155 | 'password' => $this->password, 156 | ]) . "\r\n", FILE_APPEND); 157 | return base64_encode(hash_hmac('sha1', implode('&', $data), $this->password, true)); 158 | } 159 | protected function checkDate() 160 | { 161 | $date = date_parse_from_format('D, d M Y H:i:s T', $this->date); 162 | if (!empty($date['errors'])) { 163 | throw new InvalidParamException('Date incorrect', 400); 164 | } 165 | return $this; 166 | } 167 | protected function initBucket() 168 | { 169 | $this->uri = ltrim($this->uri, '/'); 170 | $this->bucket = substr($this->uri, 0, strpos($this->uri, '/')); 171 | $buckets = \Yii::$app->params['buckets']; 172 | if (!isset($buckets[$this->bucket])) { 173 | throw new InvalidParamException('Bucket not exist', 400); 174 | } 175 | $bucket = $buckets[$this->bucket]; 176 | $this->operator = $bucket['operator']; 177 | $this->password = md5($bucket['password']); 178 | $this->authParamName = $bucket['authParamName'] ?? ''; 179 | $this->authCallbackUrl = $bucket['authCallbackUrl'] ?? ''; 180 | return $this; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /examples/yii2/storage/web/start.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/6 6 | * Time: 15:51 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | declare (strict_types = 1); 13 | define('YII_ENABLE_ERROR_HANDLER', false); 14 | define('WEB_PATH', __DIR__); 15 | define('ROOT_PATH', realpath(__DIR__ . '/../../../../')); // modify it 16 | define('VENDOR_PATH', ROOT_PATH); // modify it 17 | // 配置分离,从OP管理的配置中获取配置 18 | define('JDB_CONF_FILE', '/data/conf/qiye/server.ini'); 19 | define('START_CRONTAB', true); 20 | error_reporting(E_ALL); 21 | // composer autoload 22 | require VENDOR_PATH . '/autoload.php'; 23 | 24 | class Start extends \yiiswoole\Start 25 | { 26 | /** 27 | * The files contains Yii2 config 28 | * @var array 29 | */ 30 | public $configFiles = [ 31 | ROOT_PATH . '/storage/config/main.php', 32 | ROOT_PATH . '/storage/config/main-{{env}}.php', 33 | ]; 34 | /** 35 | * Array params config files. Yii::$app->params[''key'] 36 | * @var array 37 | */ 38 | public $paramsConfigFiles = [ 39 | ROOT_PATH . '/storage/config/params.php', 40 | ROOT_PATH . '/storage/config/params-{{env}}.php', 41 | ]; 42 | /** 43 | * Yii2 bootstrap file to register namespaces. 44 | * @var string 45 | */ 46 | public $yiiBootstrapFile = ROOT_PATH . '/storage/config/bootstrap.php'; 47 | } 48 | 49 | (new Start())->run(); 50 | // 51 | //class Start extends \yiiswoole\Start 52 | //{ 53 | //} 54 | // 55 | //(new Start())->run($config); 56 | -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | defaultRoute; 23 | } 24 | $cacheKey = md5($this->controllerNamespace) . $route; 25 | if (!isset(static::$controllerInstances[$cacheKey])) { 26 | $controller = parent::createController($route); // TODO: Change the autogenerated stub 27 | if (false !== $controller) { 28 | static::$controllerInstances[$cacheKey] = $controller; 29 | } 30 | } 31 | if (isset(static::$controllerInstances[$cacheKey])) { 32 | return [ 33 | clone static::$controllerInstances[$cacheKey][0], 34 | static::$controllerInstances[$cacheKey][1], 35 | ]; 36 | } else { 37 | return false; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Start.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | declare (strict_types = 1); 13 | 14 | namespace yiiswoole; 15 | 16 | use yii\base\ExitException; 17 | use yii\base\InvalidRouteException; 18 | use yii\web\NotFoundHttpException; 19 | 20 | defined('YII_ENABLE_ERROR_HANDLER') || define('YII_ENABLE_ERROR_HANDLER', false); 21 | defined('WEB_PATH') || define('WEB_PATH', __DIR__); 22 | defined('ROOT_PATH') || define('ROOT_PATH', realpath(__DIR__ . '/../../')); 23 | defined('VENDOR_PATH') || define('VENDOR_PATH', ROOT_PATH . '/vendor'); 24 | // 配置分离,从OP管理的配置中获取配置 25 | defined('JDB_CONF_FILE') || define('JDB_CONF_FILE', '/data/conf/qiye/server.ini'); 26 | error_reporting(E_ALL); 27 | 28 | class Start 29 | { 30 | public $yiiConfig = []; 31 | public $serverConfig = []; 32 | public $env = 'local'; 33 | public $logTrackId = ''; 34 | /** 35 | * The files contains Yii2 config 36 | * @var array 37 | */ 38 | public $configFiles = []; 39 | /** 40 | * Array params config files. Yii::$app->params[''key'] 41 | * @var array 42 | */ 43 | public $paramsConfigFiles = []; 44 | /** 45 | * Yii2 bootstrap file to register namespaces. 46 | * @var string 47 | */ 48 | public $yiiBootstrapFile = ''; 49 | /** 50 | * @var \yiiswoole\web\Application 51 | */ 52 | public static $app = null; 53 | /** 54 | * @var Start 55 | * 56 | */ 57 | public static $instance = null; 58 | public function initEnv() 59 | { 60 | switch ($this->env) { 61 | case 'beta': // beta 62 | define('YII_DEBUG', false); // 关闭debug模式 63 | define('YII_ENV', 'beta'); 64 | define('TRACE_LEVEL', 0); 65 | break; 66 | case 'prod': // 生产 67 | define('YII_DEBUG', false); // 关闭debug模式 68 | define('YII_ENV', 'prod'); 69 | define('TRACE_LEVEL', 0); 70 | break; 71 | case 'yace': // 压测 72 | define('YII_DEBUG', false); // 关闭debug模式 73 | define('YII_ENV', 'yace'); 74 | define('TRACE_LEVEL', 0); 75 | break; 76 | case 'dev': 77 | // 开发环境 78 | define('YII_DEBUG', true); 79 | define('YII_ENV', 'dev'); 80 | define('TRACE_LEVEL', 3); 81 | break; 82 | case 'test': 83 | // 开发环境 84 | define('YII_DEBUG', true); 85 | define('YII_ENV', 'test'); 86 | define('TRACE_LEVEL', 3); 87 | break; 88 | default: 89 | // 默认本地环境 90 | define('YII_DEBUG', true); 91 | define('YII_ENV', 'dev'); 92 | define('TRACE_LEVEL', 3); 93 | break; 94 | } 95 | } 96 | public function initConfig() 97 | { 98 | if (!file_exists(JDB_CONF_FILE)) { 99 | die("缺少配置分离文件"); 100 | } 101 | $arrServerConf = $this->parseIniFileMulti(JDB_CONF_FILE, true, INI_SCANNER_TYPED); 102 | $this->env = $arrServerConf['env']['env']; 103 | $this->initEnv(); 104 | // 初使化 105 | $config = ['params' => []]; 106 | // 加载公共配置 数据库,组件,redis等 107 | foreach ($this->configFiles as $file) { 108 | $file = str_replace('{{env}}', $this->env, $file); 109 | if (is_file($file)) { 110 | $config = \BriarBear\Helpers\ArrayHelper::merge($config, require $file); 111 | } 112 | } 113 | 114 | // 加载全局配置 Yii::$app->params[$key] 请求第三方项目的接口等 115 | foreach ($this->paramsConfigFiles as $file) { 116 | $file = str_replace('{{env}}', $this->env, $file); 117 | if (is_file($file)) { 118 | $config['params'] = \BriarBear\Helpers\ArrayHelper::merge($config['params'], require $file); 119 | } 120 | } 121 | // 第三方系统接口地址 122 | if (!empty($config['params'])) { 123 | foreach ($config['params'] as $key => $value) { 124 | if (isset($arrServerConf[$key]) && !empty($arrServerConf[$key])) { 125 | if (is_array($arrServerConf[$key])) { 126 | $config['params'][$key] = \BriarBear\Helpers\ArrayHelper::merge($value, $arrServerConf[$key]); 127 | } else { 128 | $config['params'][$key] = $arrServerConf[$key]; 129 | } 130 | } 131 | } 132 | } 133 | // 数据库,codis等组件配置 134 | if (!empty($config['components'])) { 135 | foreach ($config['components'] as $key => $value) { 136 | if (isset($arrServerConf[$key]) && !empty($arrServerConf[$key])) { 137 | $config['components'][$key] = \BriarBear\Helpers\ArrayHelper::merge($value, $arrServerConf[$key]); 138 | } 139 | } 140 | } 141 | unset($key, $value); 142 | unset($arrServerConf); 143 | $this->serverConfig = [ 144 | 'logger' => $config['params']['logger'] ?? [], 145 | 'server' => $config['params']['server'] ?? [], 146 | ]; 147 | $this->serverConfig['server']['callback'] = [ 148 | 'httpRequest' => [$this, 'httpRequest'], 149 | 'tcpReceive' => [$this, 'tcpRequest'], 150 | 'workerStart' => [$this, 'workStart'], 151 | 'task' => [$this, 'task'], 152 | ]; 153 | unset($config['params']['logger'], $config['params']['server']); 154 | $this->yiiConfig = $config; 155 | unset($config); 156 | return $this; 157 | } 158 | public function run($config = null) 159 | { 160 | $server = new \BriarBear\BriarBear(); 161 | if ($config === null) { 162 | $this->initConfig(); 163 | } else { 164 | $this->processConfigSection($config); 165 | $this->serverConfig = [ 166 | 'logger' => $config['params']['logger'] ?? [], 167 | 'server' => $config['params']['server'] ?? [], 168 | ]; 169 | $this->serverConfig['server']['callback'] = [ 170 | 'httpRequest' => [$this, 'httpRequest'], 171 | 'tcpReceive' => [$this, 'tcpRequest'], 172 | 'workerStart' => [$this, 'workStart'], 173 | 'task' => [$this, 'task'], 174 | ]; 175 | unset($config['params']['logger'], $config['params']['server']); 176 | $this->yiiConfig = $config; 177 | unset($config); 178 | } 179 | 180 | $this->loadYii(); 181 | static::$app = new \yiiswoole\web\Application($this->yiiConfig); 182 | // init all yii components 183 | foreach ($this->yiiConfig['components'] as $id => $_config) { 184 | if (in_array($id, ['user', 'session'])) { 185 | continue; 186 | } 187 | static::$app->get($id); 188 | } 189 | 190 | $this->logTrackId = uniqid('', true) . '00'; 191 | static::$app->getRequest()->setUrl(null); 192 | static::$app->getRequest()->setPathInfo(null); 193 | static::$app->getResponse()->clear(); 194 | if (static::$app->has('session', true)) { 195 | static::$app->getSession()->close(); 196 | static::$app->getSession()->clear(); 197 | } 198 | if (static::$app->has('db', true)) { 199 | static::$app->getDb()->close(); 200 | } 201 | if (static::$app->has('redis', true)) { 202 | static::$app->redis->close(); 203 | } 204 | static::$instance = $this; 205 | 206 | $server->run($this->serverConfig); 207 | } 208 | 209 | /** 210 | * 211 | */ 212 | public function loadYii() 213 | { 214 | // 加载Yii核心 215 | require_once rtrim(VENDOR_PATH, '/') . '/yiisoft/yii2/Yii.php'; // Yii核心类 216 | if ($this->yiiBootstrapFile) { 217 | require $this->yiiBootstrapFile; 218 | } 219 | \Yii::$container = new \yiiswoole\di\Container(); 220 | // require all classes, speed the request 221 | $classMap = require __DIR__ . '/classes.php'; 222 | \Yii::$classMap = array_unique(array_merge(\Yii::$classMap, $classMap)); 223 | \BriarBear\Autoload::getInstance()->addClassMap(\Yii::$classMap); 224 | foreach (\Yii::$classMap as $class => $path) { 225 | if (class_exists($class) === false && trait_exists($class) === false && interface_exists($class) === false) { 226 | \Yii::autoload($class); 227 | } 228 | } 229 | // swoole fix 230 | $_SERVER['PHP_SELF'] = '/' . basename($_SERVER['PHP_SELF']); 231 | $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF']; 232 | $_SERVER['SCRIPT_FILENAME'] = rtrim(WEB_PATH, '/') . $_SERVER['PHP_SELF']; 233 | } 234 | 235 | /** 236 | * @param \BriarBear\Server $server 237 | * @param \Swoole\Server $sw 238 | * @param $fd 239 | * @param \BriarBear\Request $request 240 | * @return \BriarBear\Response\HttpResponse 241 | */ 242 | public function httpRequest(\BriarBear\Server $server, \Swoole\Server $sw, $fd, \BriarBear\Request $request) 243 | { 244 | /** 245 | * @var \yiiswoole\web\Application $app 246 | */ 247 | $app = clone static::$app; 248 | // do some init jobs before call yii 249 | $this->beforeRequest($app); 250 | $this->logTrackId = $_SERVER['HTTP_JDB_HEADER_RID'] ?? md5(uniqid('', true) . gethostname()); 251 | /** 252 | * @var \yiiswoole\web\Request $app->getRequest() 253 | */ 254 | $app->getRequest()->setBriabearRequest($request); 255 | /** 256 | * @var \yiiswoole\web\Response $app->getResponse() 257 | */ 258 | $app->getResponse()->setRequestType(\BriarBear\Server::REQUEST_TYPE_HTTP); 259 | \Yii::$app = $app; 260 | $app::setInstance($app); 261 | try { 262 | $response = $app->run(); 263 | 264 | $this->afterRequest($app); 265 | 266 | return $response; 267 | } catch (\Throwable $exception) { 268 | if ($exception instanceof NotFoundHttpException || $exception instanceof InvalidRouteException || $exception instanceof ExitException) { 269 | try { 270 | return \Yii::$app->getErrorHandler()->handleHttpException($exception); 271 | } catch (\Throwable $ex) { 272 | return \Yii::$app->getErrorHandler()->handleException($ex); 273 | } 274 | } 275 | return \Yii::$app->getErrorHandler()->handleException($exception); 276 | } 277 | } 278 | protected function beforeRequest(\yiiswoole\web\Application $app) 279 | { 280 | $app->set('request', clone static::$app->getRequest()); 281 | $app->set('response', clone static::$app->getResponse()); 282 | if ($app->has('session', true)) { 283 | $app->set('session', clone static::$app->getSession()); 284 | } 285 | if ($app->has('user', true)) { 286 | $app->set('user', clone static::$app->getUser()); 287 | } 288 | } 289 | 290 | /** 291 | * @param \yiiswoole\web\Application $app 292 | */ 293 | protected function afterRequest(\yiiswoole\web\Application $app) 294 | { 295 | \Yii::getLogger()->flush(); 296 | \Yii::getLogger()->flush(true); 297 | 298 | $app->set('request', null); 299 | $app->set('response', null); 300 | if ($app->has('session', true)) { 301 | $app->getSession()->close(); 302 | $app->getSession()->clear(); 303 | $app->set('session', null); 304 | } 305 | if ($app->has('user', true)) { 306 | $app->set('user', null); 307 | } 308 | 309 | $app::setInstance(null); 310 | \Yii::$app = null; 311 | 312 | unset($app); 313 | } 314 | 315 | /** 316 | * @param \BriarBear\Server $server 317 | * @param \Swoole\Server $sw 318 | * @param $fd 319 | * @param \BriarBear\Request $request 320 | * @return \BriarBear\Response\TcpResponse 321 | */ 322 | public function tcpRequest(\BriarBear\Server $server, \Swoole\Server $sw, $fd, \BriarBear\Request $request) 323 | { 324 | $this->logTrackId = uniqid('', true) . $fd; 325 | 326 | $app = clone static::$app; 327 | // do some init jobs before call yii 328 | $this->beforeRequest($app); 329 | 330 | $app->getRequest()->setBriabearRequest($request); 331 | $app->getResponse()->setRequestType(\BriarBear\Server::REQUEST_TYPE_TCP); 332 | \Yii::$app = $app; 333 | $app::setInstance($app); 334 | 335 | try { 336 | $response = $app->run(); 337 | 338 | $this->afterRequest($app); 339 | 340 | return $response; 341 | } catch (\Throwable $exception) { 342 | if ($exception instanceof NotFoundHttpException || $exception instanceof InvalidRouteException || $exception instanceof ExitException) { 343 | throw $exception; 344 | } 345 | return \Yii::$app->getErrorHandler()->handleException($exception); 346 | } 347 | } 348 | public function workStart() 349 | { 350 | 351 | } 352 | 353 | /** 354 | * 374 | * 375 | * will result in: 376 | * Array 382 | * ( 383 | * [foo] => bar 384 | * [foo.with.dots] => 1 385 | * ) 386 | * [array] => Array 387 | * ( 388 | * [foo] => Array 389 | * ( 390 | * [0] => 1 391 | * [1] => 2 392 | * ) 393 | * ) 394 | * [dictionary] => Array 395 | * ( 396 | * [foo] => Array 397 | * ( 398 | * [debug] => 399 | * [path] => /some/path 400 | * ) 401 | * ) 402 | * [multi] => Array 403 | * ( 404 | * [foo] => Array 405 | * ( 406 | * [data] => Array 407 | * ( 408 | * [config] => Array 409 | * ( 410 | * [debug] => 1 411 | * ) 412 | * [password] => 123456 413 | * ) 414 | * ) 415 | * ) 416 | * ) 417 | * ?> 418 | * @param $file 419 | * @param bool $processSections 420 | * @param int $scannerMode 421 | * @return array|mixed 422 | */ 423 | protected function parseIniFileMulti(string $file, bool $processSections = false, int $scannerMode = INI_SCANNER_NORMAL) 424 | { 425 | // load ini file the normal way 426 | $data = parse_ini_file($file, $processSections, $scannerMode); 427 | if (!$processSections) { 428 | $data = array($data); 429 | } 430 | $this->processConfigSection($data); 431 | if (!$processSections) { 432 | $data = $data[0]; 433 | } 434 | return $data; 435 | } 436 | protected function processConfigSection(&$data) 437 | { 438 | 439 | $explodeStr = '.'; 440 | $escapeChar = "'"; 441 | foreach ($data as $sectionKey => $section) { 442 | // loop inside the section 443 | if (!is_array($section)) { 444 | continue; 445 | } 446 | foreach ($section as $key => $value) { 447 | if (is_string($key) && strpos($key, $explodeStr)) { 448 | if (substr($key, 0, 1) !== $escapeChar) { 449 | // key has a dot. Explode on it, then parse each subkeys 450 | // and set value at the right place thanks to references 451 | $subKeys = explode($explodeStr, $key); 452 | $subs = &$data[$sectionKey]; 453 | foreach ($subKeys as $subKey) { 454 | if (!isset($subs[$subKey])) { 455 | $subs[$subKey] = []; 456 | } 457 | $subs = &$subs[$subKey]; 458 | } 459 | // set the value at the right place 460 | $subs = $value; 461 | // unset the dotted key, we don't need it anymore 462 | unset($data[$sectionKey][$key]); 463 | } 464 | // we have escaped the key, so we keep dots as they are 465 | else { 466 | $newKey = trim($key, $escapeChar); 467 | $data[$sectionKey][$newKey] = $value; 468 | unset($data[$sectionKey][$key]); 469 | } 470 | } 471 | } 472 | } 473 | } 474 | 475 | /** 476 | * @param \BriarBear\Server $bs 477 | * @param \Swoole\Server $ss 478 | * @param int $workerId 479 | * @param int $taskId 480 | * @param $taskParams 481 | */ 482 | public function task(\BriarBear\Server $bs, \Swoole\Server $ss, int $workerId, int $taskId, $taskParams) 483 | { 484 | $this->logTrackId = uniqid('', true) . $taskId; 485 | $class = $taskParams['class']; 486 | $method = $taskParams['method'] ?? null; 487 | $params = $taskParams['params'] ?? null; 488 | try { 489 | if ($method === null) { 490 | new $class($params); 491 | } else { 492 | (new $class)->$method($params); 493 | } 494 | } catch (\Throwable $exception) { 495 | $this->addLog('exec_task_fail, exception:'.$exception->__toString(), 'error', $taskParams); 496 | } 497 | \Yii::getLogger()->flush(); 498 | \Yii::getLogger()->flush(true); 499 | } 500 | /** 501 | * common method to add a log 502 | * @param string $message log message 503 | * @param string $security log level 504 | * @param array $context params to log 505 | * @param string $category log category 506 | * @return bool 507 | */ 508 | public function addLog($message, string $security, array $context = array(), string $category = 'default') 509 | { 510 | // check category 511 | if ($category == 'default' && \Yii::$app->controller) { 512 | // if client request not exist controller or action, then exception 513 | try { 514 | $category = \Yii::$app->controller->id . '-' . \Yii::$app->controller->action->id; 515 | } catch (\Throwable $e) { 516 | 517 | } 518 | } 519 | 520 | $info = [ 521 | '@timestamp' => date('Y-m-d H:i:s'), 522 | '@message' => $message, 523 | 'context' => $context, 524 | 'level' => $security, 525 | 'category' => $category, 526 | 'traceId' => $this->logTrackId, 527 | ]; 528 | $category = 'activity-' . $category; 529 | 530 | switch ($security) { 531 | case 'info': 532 | case 'error': 533 | case 'warning': 534 | \Yii::$security($info, $category); 535 | break; 536 | default: 537 | \Yii::trace($info, $category); 538 | break; 539 | } 540 | 541 | return true; 542 | } 543 | } 544 | -------------------------------------------------------------------------------- /src/classes.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * Yii core class map. 14 | * 15 | * This file contains all classes your want load before server start. 16 | * Cache all classes can get more performance by reduce the file io. 17 | * 18 | */ 19 | 20 | return [ 21 | 'BriarBear\Request' => BRIARBEAR_PATH . 'Request.php', 22 | 'BriarBear\Response\HttpResponse' => BRIARBEAR_PATH . 'Response/HttpResponse.php', 23 | 'BriarBear\Response\TcpResponse' => BRIARBEAR_PATH . 'Response/TcpResponse.php', 24 | 'BriarBear\Buffer' => BRIARBEAR_PATH . 'Buffer.php', 25 | 'BriarBear\Exception\InvalidCallException' => BRIARBEAR_PATH . 'Exception/InvalidCallException.php', 26 | 'BriarBear\Exception\InvalidConfigException' => BRIARBEAR_PATH . 'Exception/InvalidConfigException.php', 27 | 'BriarBear\Exception\InvalidParamException' => BRIARBEAR_PATH . 'Exception/InvalidParamException.php', 28 | 'BriarBear\Exception\UnknownClassException' => BRIARBEAR_PATH . 'Exception/UnknownClassException.php', 29 | 'BriarBear\Exception\UnknownMethodException' => BRIARBEAR_PATH . 'Exception/UnknownMethodException.php', 30 | 'BriarBear\Exception\UnknownPropertyException' => BRIARBEAR_PATH . 'Exception/UnknownPropertyException.php', 31 | 'BriarBear\Log\FileLogger' => BRIARBEAR_PATH . 'Log/FileLogger.php', 32 | 'yiiswoole\web\Controller' => VENDOR_PATH . '/briarbear/yii2/src/web/Controller.php', 33 | 'yiiswoole\web\ErrorHandler' => VENDOR_PATH . '/briarbear/yii2/src/web/ErrorHandler.php', 34 | 'yiiswoole\web\Request' => VENDOR_PATH . '/briarbear/yii2/src/web/Request.php', 35 | 'yiiswoole\web\Response' => VENDOR_PATH . '/briarbear/yii2/src/web/Response.php', 36 | 'yiiswoole\web\Session' => VENDOR_PATH . '/briarbear/yii2/src/web/Session.php', 37 | 'yiiswoole\web\User' => VENDOR_PATH . '/briarbear/yii2/src/web/User.php', 38 | 'yiiswoole\db\Command' => VENDOR_PATH . '/briarbear/yii2/src/db/Command.php', 39 | 'yiiswoole\db\Connection' => VENDOR_PATH . '/briarbear/yii2/src/db/Connection.php', 40 | 'yiiswoole\redis\Connection' => VENDOR_PATH . '/briarbear/yii2/src/redis/Connection.php', 41 | 'yiiswoole\redis\Session' => VENDOR_PATH . '/briarbear/yii2/src/redis/Session.php', 42 | 'yiilog\EmailTarget' => VENDOR_PATH . '/briarbear/yii2-log/src/EmailTarget.php', 43 | 'yiilog\RedisTarget' => VENDOR_PATH . '/briarbear/yii2-log/src/RedisTarget.php', 44 | 'yiilog\LogstashFileTarget' => VENDOR_PATH . '/briarbear/yii2-log/src/LogstashFileTarget.php', 45 | 'yii\redis\Connection' => VENDOR_PATH . '/yiisoft/yii2-redis/Connection.php', 46 | 'yii\redis\Cache' => VENDOR_PATH . '/yiisoft/yii2-redis/Cache.php', 47 | 'yii\redis\Session' => VENDOR_PATH . '/yiisoft/yii2-redis/Session.php', 48 | 'yii\redis\ActiveQuery' => VENDOR_PATH . '/yiisoft/yii2-redis/ActiveQuery.php', 49 | 'yii\redis\ActiveRecord' => VENDOR_PATH . '/yiisoft/yii2-redis/ActiveRecord.php', 50 | 'yii\redis\LuaScriptBuilder' => VENDOR_PATH . '/yiisoft/yii2-redis/LuaScriptBuilder.php', 51 | 'yii\httpclient\Client' => VENDOR_PATH . '/yiisoft/yii2-httpclient/Client.php', 52 | 'yii\httpclient\CurlTransport' => VENDOR_PATH . '/yiisoft/yii2-httpclient/CurlTransport.php', 53 | 'yii\httpclient\FormatterInterface' => VENDOR_PATH . '/yiisoft/yii2-httpclient/FormatterInterface.php', 54 | 'yii\httpclient\JsonFormatter' => VENDOR_PATH . '/yiisoft/yii2-httpclient/JsonFormatter.php', 55 | 'yii\httpclient\JsonParser' => VENDOR_PATH . '/yiisoft/yii2-httpclient/JsonParser.php', 56 | 'yii\httpclient\Message' => VENDOR_PATH . '/yiisoft/yii2-httpclient/Message.php', 57 | 'yii\httpclient\ParserInterface' => VENDOR_PATH . '/yiisoft/yii2-httpclient/ParserInterface.php', 58 | 'yii\httpclient\Request' => VENDOR_PATH . '/yiisoft/yii2-httpclient/Request.php', 59 | 'yii\httpclient\Response' => VENDOR_PATH . '/yiisoft/yii2-httpclient/Response.php', 60 | 'yii\httpclient\StreamTransport' => VENDOR_PATH . '/yiisoft/yii2-httpclient/StreamTransport.php', 61 | 'yii\httpclient\Transport' => VENDOR_PATH . '/yiisoft/yii2-httpclient/Transport.php', 62 | 'yii\httpclient\UrlEncodedFormatter' => VENDOR_PATH . '/yiisoft/yii2-httpclient/UrlEncodedFormatter.php', 63 | 'yii\httpclient\UrlEncodedParser' => VENDOR_PATH . '/yiisoft/yii2-httpclient/UrlEncodedParser.php', 64 | 'yii\httpclient\XmlFormatter' => VENDOR_PATH . '/yiisoft/yii2-httpclient/XmlFormatter.php', 65 | 'yii\httpclient\XmlParser' => VENDOR_PATH . '/yiisoft/yii2-httpclient/XmlParser.php', 66 | ]; 67 | -------------------------------------------------------------------------------- /src/db/Command.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | namespace yiiswoole\db; 19 | 20 | use Yii; 21 | use yii\base\Exception; 22 | use yii\db\DataReader; 23 | 24 | class Command extends \yii\db\Command 25 | { 26 | /** 27 | * @var array pending parameters to be bound to the current PDO statement. 28 | */ 29 | protected $_pendingParams = []; 30 | /** 31 | * @var string the SQL statement that this command represents 32 | */ 33 | private $_sql; 34 | /** 35 | * @var string name of the table, which schema, should be refreshed after command execution. 36 | */ 37 | private $_refreshTableName; 38 | protected $errorCount = 0; 39 | public $maxErrorTimes = 2; 40 | /** 41 | * Returns the SQL statement for this command. 42 | * @return string the SQL statement to be executed 43 | */ 44 | public function getSql() 45 | { 46 | return $this->_sql; 47 | } 48 | 49 | /** 50 | * Specifies the SQL statement to be executed. 51 | * The previous SQL execution (if any) will be cancelled, and [[params]] will be cleared as well. 52 | * @param string $sql the SQL statement to be set. 53 | * @return $this this command instance 54 | */ 55 | public function setSql($sql) 56 | { 57 | if ($sql !== $this->_sql) { 58 | $this->cancel(); 59 | $this->_sql = $this->db->quoteSql($sql); 60 | $this->_pendingParams = []; 61 | $this->params = []; 62 | $this->_refreshTableName = null; 63 | } 64 | 65 | return $this; 66 | } 67 | /** 68 | * Specifies the SQL statement to be executed. The SQL statement will not be modified in any way. 69 | * The previous SQL (if any) will be discarded, and [[params]] will be cleared as well. See [[reset()]] 70 | * for details. 71 | * 72 | * @param string $sql the SQL statement to be set. 73 | * @return $this this command instance 74 | * @since 2.0.13 75 | * @see reset() 76 | * @see cancel() 77 | */ 78 | public function setRawSql($sql) 79 | { 80 | if ($sql !== $this->_sql) { 81 | $this->cancel(); 82 | $this->reset(); 83 | $this->_sql = $sql; 84 | } 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * Returns the raw SQL by inserting parameter values into the corresponding placeholders in [[sql]]. 91 | * Note that the return value of this method should mainly be used for logging purpose. 92 | * It is likely that this method returns an invalid SQL due to improper replacement of parameter placeholders. 93 | * @return string the raw SQL with parameter values inserted into the corresponding placeholders in [[sql]]. 94 | */ 95 | public function getRawSql() 96 | { 97 | if (empty($this->params)) { 98 | return $this->_sql; 99 | } 100 | $params = []; 101 | foreach ($this->params as $name => $value) { 102 | if (is_string($name) && strncmp(':', $name, 1)) { 103 | $name = ':' . $name; 104 | } 105 | if (is_string($value)) { 106 | $params[$name] = $this->db->quoteValue($value); 107 | } elseif (is_bool($value)) { 108 | $params[$name] = ($value ? 'TRUE' : 'FALSE'); 109 | } elseif ($value === null) { 110 | $params[$name] = 'NULL'; 111 | } elseif (!is_object($value) && !is_resource($value)) { 112 | $params[$name] = $value; 113 | } 114 | } 115 | if (!isset($params[1])) { 116 | return strtr($this->_sql, $params); 117 | } 118 | $sql = ''; 119 | foreach (explode('?', $this->_sql) as $i => $part) { 120 | $sql .= (isset($params[$i]) ? $params[$i] : '') . $part; 121 | } 122 | 123 | return $sql; 124 | } 125 | /** 126 | * Binds pending parameters that were registered via [[bindValue()]] and [[bindValues()]]. 127 | * Note that this method requires an active [[pdoStatement]]. 128 | */ 129 | protected function bindPendingParams() 130 | { 131 | foreach ($this->_pendingParams as $name => $value) { 132 | $this->pdoStatement->bindValue($name, $value[0], $value[1]); 133 | } 134 | $this->_pendingParams = []; 135 | } 136 | 137 | /** 138 | * Binds a value to a parameter. 139 | * @param string|integer $name Parameter identifier. For a prepared statement 140 | * using named placeholders, this will be a parameter name of 141 | * the form `:name`. For a prepared statement using question mark 142 | * placeholders, this will be the 1-indexed position of the parameter. 143 | * @param mixed $value The value to bind to the parameter 144 | * @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value. 145 | * @return $this the current command being executed 146 | * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php 147 | */ 148 | public function bindValue($name, $value, $dataType = null) 149 | { 150 | if ($dataType === null) { 151 | $dataType = $this->db->getSchema()->getPdoType($value); 152 | } 153 | $this->_pendingParams[$name] = [$value, $dataType]; 154 | $this->params[$name] = $value; 155 | 156 | return $this; 157 | } 158 | 159 | /** 160 | * Binds a list of values to the corresponding parameters. 161 | * This is similar to [[bindValue()]] except that it binds multiple values at a time. 162 | * Note that the SQL data type of each value is determined by its PHP type. 163 | * @param array $values the values to be bound. This must be given in terms of an associative 164 | * array with array keys being the parameter names, and array values the corresponding parameter values, 165 | * e.g. `[':name' => 'John', ':age' => 25]`. By default, the PDO type of each value is determined 166 | * by its PHP type. You may explicitly specify the PDO type by using an array: `[value, type]`, 167 | * e.g. `[':name' => 'John', ':profile' => [$profile, \PDO::PARAM_LOB]]`. 168 | * @return $this the current command being executed 169 | */ 170 | public function bindValues($values) 171 | { 172 | if (empty($values)) { 173 | return $this; 174 | } 175 | 176 | $schema = $this->db->getSchema(); 177 | foreach ($values as $name => $value) { 178 | if (is_array($value)) { 179 | $this->_pendingParams[$name] = $value; 180 | $this->params[$name] = $value[0]; 181 | } else { 182 | $type = $schema->getPdoType($value); 183 | $this->_pendingParams[$name] = [$value, $type]; 184 | $this->params[$name] = $value; 185 | } 186 | } 187 | 188 | return $this; 189 | } 190 | /** 191 | * 检查指定的异常是否为可以重连的错误类型 192 | * 193 | * @param \Exception $exception 194 | * @return bool 195 | */ 196 | public function isConnectionError($exception) 197 | { 198 | if ($exception instanceof \PDOException) { 199 | $errorInfo = $this->pdoStatement->errorInfo(); 200 | if ($errorInfo[1] == 70100 || $errorInfo[1] == 2006 || $errorInfo[1] == 2013) { 201 | return true; 202 | } 203 | } 204 | $message = $exception->getMessage(); 205 | if (strpos($message, 'Error while sending QUERY packet.') !== false) { 206 | return true; 207 | } 208 | // Error reading result set's header 209 | if (strpos($message, 'Error reading result set\'s header') !== false) { 210 | return true; 211 | } 212 | // MySQL server has gone away 213 | if (strpos($message, 'MySQL server has gone away') !== false) { 214 | return true; 215 | } 216 | return false; 217 | } 218 | 219 | public function prepare($forRead = null) 220 | { 221 | if ($this->pdoStatement) { 222 | $this->bindPendingParams(); 223 | return false; 224 | } 225 | 226 | $sql = $this->getSql(); 227 | 228 | if ($this->db->getTransaction()) { 229 | // master is in a transaction. use the same connection. 230 | $forRead = false; 231 | } 232 | if ($forRead || $forRead === null && $this->db->getSchema()->isReadQuery($sql)) { 233 | $pdo = $this->db->getSlavePdo(); 234 | } else { 235 | $pdo = $this->db->getMasterPdo(); 236 | } 237 | 238 | try { 239 | $this->pdoStatement = $pdo->prepare($sql); 240 | $this->bindPendingParams(); 241 | return true; 242 | } catch (\Throwable $e) { 243 | if ($this->isConnectionError($e) && $this->errorCount < $this->maxErrorTimes) { 244 | $this->cancel(); 245 | $this->db->close(); 246 | $this->db->open(); 247 | $this->errorCount++; 248 | return $this->prepare($forRead); 249 | } 250 | 251 | $message = $e->getMessage() . "\nFailed to prepare SQL: $sql"; 252 | $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; 253 | throw new Exception($message, $errorInfo, (int) $e->getCode(), $e); 254 | } 255 | } 256 | public function queryInternal($method, $fetchMode = null, $reconnect = 0) 257 | { 258 | $rawSql = $this->getRawSql(); 259 | $oldMethod = $method; 260 | $oldFetchMode = $fetchMode; 261 | Yii::info($rawSql, 'yii\db\Command::query'); 262 | 263 | if ($method !== '') { 264 | $info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency); 265 | if (is_array($info)) { 266 | /* @var $cache \yii\caching\Cache */ 267 | $cache = $info[0]; 268 | $cacheKey = [ 269 | __CLASS__, 270 | $method, 271 | $fetchMode, 272 | $this->db->dsn, 273 | $this->db->username, 274 | $rawSql, 275 | ]; 276 | $result = $cache->get($cacheKey); 277 | if (is_array($result) && isset($result[0])) { 278 | Yii::trace('Query result served from cache', 'yii\db\Command::query'); 279 | return $result[0]; 280 | } 281 | } 282 | } 283 | $bakPendingParams = $this->_pendingParams; 284 | $this->prepare(true); 285 | 286 | $token = $rawSql; 287 | try { 288 | YII_DEBUG && Yii::beginProfile($token, 'yii\db\Command::query'); 289 | // @link https://bugs.php.net/bug.php?id=74401 290 | $this->pdoStatement->execute(); 291 | 292 | if ($method === '') { 293 | $result = new DataReader($this); 294 | } else { 295 | if ($fetchMode === null) { 296 | $fetchMode = $this->fetchMode; 297 | } 298 | $result = call_user_func_array([$this->pdoStatement, $method], (array) $fetchMode); 299 | $this->pdoStatement->closeCursor(); 300 | } 301 | 302 | YII_DEBUG && Yii::endProfile($token, 'yii\db\Command::query'); 303 | } catch (\Throwable $e) { 304 | if ($this->isConnectionError($e) && $this->errorCount < $this->maxErrorTimes) { 305 | $this->cancel(); 306 | $this->db->close(); 307 | $this->db->open(); 308 | $this->_pendingParams = $bakPendingParams; 309 | $this->errorCount++; 310 | return $this->queryInternal($oldMethod, $oldFetchMode); 311 | } 312 | 313 | YII_DEBUG && Yii::endProfile($token, 'yii\db\Command::query'); 314 | throw $this->db->getSchema()->convertException($e, $rawSql); 315 | } 316 | 317 | if (isset($cache, $cacheKey, $info)) { 318 | $cache->set($cacheKey, [$result], $info[1], $info[2]); 319 | Yii::trace('Saved query result in cache', 'yii\db\Command::query'); 320 | } 321 | 322 | return $result; 323 | } 324 | 325 | public function execute() 326 | { 327 | $sql = $this->getSql(); 328 | 329 | $rawSql = $this->getRawSql(); 330 | 331 | Yii::info($rawSql, __METHOD__); 332 | 333 | if ($sql == '') { 334 | return 0; 335 | } 336 | $bakPendingParams = $this->_pendingParams; 337 | $this->prepare(false); 338 | 339 | $token = $rawSql; 340 | try { 341 | YII_DEBUG && Yii::beginProfile($token, __METHOD__); 342 | // @link https://bugs.php.net/bug.php?id=74401 343 | $this->pdoStatement->execute(); 344 | 345 | $n = $this->pdoStatement->rowCount(); 346 | 347 | YII_DEBUG && Yii::endProfile($token, __METHOD__); 348 | 349 | $this->refreshTableSchema(); 350 | 351 | return $n; 352 | } catch (\Throwable $e) { 353 | if ($this->isConnectionError($e) && $this->errorCount < $this->maxErrorTimes) { 354 | $this->cancel(); 355 | $this->db->close(); 356 | $this->db->open(); 357 | $this->_pendingParams = $bakPendingParams; 358 | $this->errorCount++; 359 | return $this->execute(); 360 | } 361 | 362 | YII_DEBUG && Yii::endProfile($token, __METHOD__); 363 | throw $this->db->getSchema()->convertException($e, $rawSql); 364 | } 365 | } 366 | 367 | /** 368 | * Marks a specified table schema to be refreshed after command execution. 369 | * @param string $name name of the table, which schema should be refreshed. 370 | * @return $this this command instance 371 | * @since 2.0.6 372 | */ 373 | protected function requireTableSchemaRefresh($name) 374 | { 375 | $this->_refreshTableName = $name; 376 | return $this; 377 | } 378 | 379 | /** 380 | * Refreshes table schema, which was marked by [[requireTableSchemaRefresh()]] 381 | * @since 2.0.6 382 | */ 383 | protected function refreshTableSchema() 384 | { 385 | if ($this->_refreshTableName !== null) { 386 | $this->db->getSchema()->refreshTableSchema($this->_refreshTableName); 387 | } 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/db/Connection.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\db; 20 | 21 | class Connection extends \yii\db\Connection 22 | { 23 | /** 24 | * @var string the class used to create new database [[Command]] objects. If you want to extend the [[Command]] class, 25 | * you may configure this property to use your extended version of the class. 26 | * @see createCommand 27 | * @since 2.0.7 28 | */ 29 | public $commandClass = 'yiiswoole\db\Command'; 30 | 31 | protected $errorCount = 0; 32 | public $maxErrorTimes = 2; 33 | 34 | /** 35 | * Starts a transaction. 36 | * @param string|null $isolationLevel The isolation level to use for this transaction. 37 | * See [[Transaction::begin()]] for details. 38 | * @return \yii\db\Transaction the transaction initiated 39 | */ 40 | public function beginTransaction($isolationLevel = null) 41 | { 42 | try { 43 | return parent::beginTransaction($isolationLevel); 44 | } catch (\Throwable $exception) { 45 | if ($this->isConnectionError($exception) && $this->errorCount < $this->maxErrorTimes) { 46 | $this->close(); 47 | $this->open(); 48 | $this->errorCount++; 49 | return $this->beginTransaction($isolationLevel); 50 | } 51 | $this->errorCount = 0; 52 | throw $exception; 53 | } 54 | } 55 | /** 56 | * 检查指定的异常是否为可以重连的错误类型 57 | * 58 | * @param \Exception $exception 59 | * @return bool 60 | */ 61 | public function isConnectionError($exception) 62 | { 63 | if ($exception instanceof \PDOException) { 64 | $errorCode = $exception->getCode(); 65 | if ($errorCode == 70100 || $errorCode == 2006 || $errorCode == 2013) { 66 | return true; 67 | } 68 | } 69 | $message = $exception->getMessage(); 70 | if (strpos($message, 'Error while sending QUERY packet.') !== false) { 71 | return true; 72 | } 73 | // Error reading result set's header 74 | if (strpos($message, 'Error reading result set\'s header') !== false) { 75 | return true; 76 | } 77 | // MySQL server has gone away 78 | if (strpos($message, 'MySQL server has gone away') !== false) { 79 | return true; 80 | } 81 | return false; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/di/Container.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\di; 20 | 21 | use yii\base\Object; 22 | use yii\di\NotInstantiableException; 23 | 24 | class Container extends \yii\di\Container 25 | { 26 | /** 27 | * @var array 类的别名 28 | */ 29 | public static $classAlias = [ 30 | 'yii\db\Command' => 'yiiswoole\db\Command', 31 | 'yii\db\Connection' => 'yiiswoole\db\Connection', 32 | 'yii\web\Request' => 'yiiswoole\web\Request', 33 | 'yii\web\Response' => 'yiiswoole\web\Response', 34 | 'yii\web\Session' => 'yiiswoole\web\Session', 35 | 'yii\redis\Session' => 'yiiswoole\redis\Session', 36 | 'yii\redis\Connection' => 'yiiswoole\redis\Connection', 37 | 'yii\web\User' => 'yiiswoole\web\User', 38 | 'yii\web\ErrorHandler' => 'yiiswoole\web\ErrorHandler', 39 | 'yii\web\Controller' => 'yiiswoole\web\Controller', 40 | ]; 41 | /** 42 | * @var array 需要持久化的类 43 | */ 44 | public static $persistClasses = [ 45 | 'yiiswoole\db\Command', 46 | 'yiiswoole\db\Connection', 47 | 'yiiswoole\web\Request', 48 | 'yiiswoole\web\Response', 49 | 'yiiswoole\web\Session', 50 | 'yiiswoole\redis\Session', 51 | 'yiiswoole\redis\Connection', 52 | 'yiiswoole\web\User', 53 | 'yiiswoole\web\Controller', 54 | 'yii\base\ActionFilter', 55 | 'yii\base\ModelEvent', 56 | 'yii\base\Security', 57 | 'yii\base\Theme', 58 | 'yii\base\ViewEvent', 59 | 'yii\behaviors\AttributeBehavior', 60 | 'yii\behaviors\AttributeTypecastBehavior', 61 | 'yii\behaviors\BlameableBehavior', 62 | 'yii\behaviors\SluggableBehavior', 63 | 'yii\behaviors\TimestampBehavior', 64 | 'yii\bootstrap\Alert', 65 | 'yii\bootstrap\BootstrapAsset', 66 | 'yii\bootstrap\BootstrapPluginAsset', 67 | 'yii\bootstrap\BootstrapThemeAsset', 68 | 'yii\bootstrap\Button', 69 | 'yii\bootstrap\ButtonDropdown', 70 | 'yii\bootstrap\ButtonGroup', 71 | 'yii\bootstrap\Carousel', 72 | 'yii\bootstrap\Collapse', 73 | 'yii\bootstrap\Dropdown', 74 | 'yii\bootstrap\InputWidget', 75 | 'yii\bootstrap\Modal', 76 | 'yii\bootstrap\Nav', 77 | 'yii\bootstrap\NavBar', 78 | 'yii\bootstrap\Progress', 79 | 'yii\bootstrap\Tabs', 80 | 'yii\bootstrap\ToggleButtonGroup', 81 | 'yii\bootstrap\Widget', 82 | 'yii\caching\ApcCache', 83 | 'yii\caching\ArrayCache', 84 | 'yii\caching\DbCache', 85 | 'yii\caching\DummyCache', 86 | 'yii\caching\FileCache', 87 | 'yii\caching\MemCache', 88 | 'yii\caching\MemCacheServer', 89 | 'yii\caching\WinCache', 90 | 'yii\caching\XCache', 91 | 'yii\caching\ZendDataCache', 92 | 'yii\captcha\Captcha', 93 | 'yii\captcha\CaptchaAsset', 94 | 'yii\captcha\CaptchaValidator', 95 | 'yii\data\ActiveDataProvider', 96 | 'yii\data\ArrayDataProvider', 97 | 'yii\data\Pagination', 98 | 'yii\data\Sort', 99 | 'yii\data\SqlDataProvider', 100 | 'yii\db\ActiveQuery', 101 | 'yii\db\ColumnSchema', 102 | 'yii\db\Command', 103 | 'yii\db\Migration', 104 | 'yii\db\mysql\Schema', 105 | 'yii\db\Query', 106 | 'yii\db\TableSchema', 107 | 'yii\db\Transaction', 108 | 'yii\debug\components\search\Filter', 109 | 'yii\debug\components\search\matchers\GreaterThan', 110 | 'yii\debug\components\search\matchers\LowerThan', 111 | 'yii\debug\components\search\matchers\SameAs', 112 | 'yii\debug\models\search\Db', 113 | 'yii\debug\models\search\Debug', 114 | 'yii\debug\models\search\Log', 115 | 'yii\debug\models\search\Mail', 116 | 'yii\debug\models\search\Profile', 117 | 'yii\debug\panels\AssetPanel', 118 | 'yii\debug\panels\DbPanel', 119 | 'yii\debug\panels\LogPanel', 120 | 'yii\debug\panels\MailPanel', 121 | 'yii\debug\panels\ProfilingPanel', 122 | 'yii\filters\AccessControl', 123 | 'yii\filters\AccessRule', 124 | 'yii\filters\auth\CompositeAuth', 125 | 'yii\filters\auth\HttpBasicAuth', 126 | 'yii\filters\auth\HttpBearerAuth', 127 | 'yii\filters\auth\QueryParamAuth', 128 | 'yii\filters\ContentNegotiator', 129 | 'yii\filters\Cors', 130 | 'yii\filters\HttpCache', 131 | 'yii\filters\PageCache', 132 | 'yii\filters\RateLimiter', 133 | 'yii\filters\VerbFilter', 134 | 'yii\grid\ActionColumn', 135 | 'yii\grid\CheckboxColumn', 136 | 'yii\grid\DataColumn', 137 | 'yii\grid\GridView', 138 | 'yii\grid\GridViewAsset', 139 | 'yii\grid\SerialColumn', 140 | 'yii\i18n\Formatter', 141 | 'yii\i18n\DbMessageSource', 142 | 'yii\i18n\Formatter', 143 | 'yii\i18n\GettextMessageSource', 144 | 'yii\i18n\GettextMoFile', 145 | 'yii\i18n\GettextPoFile', 146 | 'yii\i18n\I18N', 147 | 'yii\i18n\MessageFormatter', 148 | 'yii\i18n\MessageSource', 149 | 'yii\i18n\PhpMessageSource', 150 | 'yii\jui\Accordion', 151 | 'yii\jui\AutoComplete', 152 | 'yii\jui\DatePicker', 153 | 'yii\jui\DatePickerLanguageAsset', 154 | 'yii\jui\Dialog', 155 | 'yii\jui\Draggable', 156 | 'yii\jui\Droppable', 157 | 'yii\jui\InputWidget', 158 | 'yii\jui\JuiAsset', 159 | 'yii\jui\Menu', 160 | 'yii\jui\ProgressBar', 161 | 'yii\jui\Resizable', 162 | 'yii\jui\Selectable', 163 | 'yii\jui\Slider', 164 | 'yii\jui\SliderInput', 165 | 'yii\jui\Sortable', 166 | 'yii\jui\Spinner', 167 | 'yii\jui\Tabs', 168 | 'yii\log\DbTarget', 169 | 'yii\log\EmailTarget', 170 | 'yii\log\FileTarget', 171 | 'yii\log\SyslogTarget', 172 | 'yii\mail\MailEvent', 173 | 'yii\rbac\Assignment', 174 | 'yii\rbac\Item', 175 | 'yii\rbac\Permission', 176 | 'yii\rbac\Role', 177 | 'yii\redis\Cache', 178 | 'yii\redis\Connection', 179 | 'yii\redis\LuaScriptBuilder', 180 | 'yii\redis\Session', 181 | 'yii\rest\Serializer', 182 | 'yii\rest\UrlRule', 183 | 'yii\test\ActiveFixture', 184 | 'yii\test\ArrayFixture', 185 | 'yii\test\InitDbFixture', 186 | 'yii\validators\BooleanValidator', 187 | 'yii\validators\CompareValidator', 188 | 'yii\validators\DateValidator', 189 | 'yii\validators\DefaultValueValidator', 190 | 'yii\validators\EachValidator', 191 | 'yii\validators\EmailValidator', 192 | 'yii\validators\ExistValidator', 193 | 'yii\validators\FileValidator', 194 | 'yii\validators\FilterValidator', 195 | 'yii\validators\ImageValidator', 196 | 'yii\validators\InlineValidator', 197 | 'yii\validators\IpValidator', 198 | 'yii\validators\NumberValidator', 199 | 'yii\validators\RangeValidator', 200 | 'yii\validators\RegularExpressionValidator', 201 | 'yii\validators\RequiredValidator', 202 | 'yii\validators\SafeValidator', 203 | 'yii\validators\StringValidator', 204 | 'yii\validators\UniqueValidator', 205 | 'yii\validators\UrlValidator', 206 | 'yii\validators\ValidationAsset', 207 | 'yii\web\AssetConverter', 208 | 'yii\web\Cookie', 209 | 'yii\web\GroupUrlRule', 210 | 'yii\web\HeaderCollection', 211 | 'yii\web\HtmlResponseFormatter', 212 | 'yii\web\JqueryAsset', 213 | 'yii\web\JsonParser', 214 | 'yii\web\JsonResponseFormatter', 215 | 'yii\web\Link', 216 | 'yii\web\MultipartFormDataParser', 217 | 'yii\web\UrlManager', 218 | 'yii\web\UrlNormalizer', 219 | 'yii\web\UrlRule', 220 | 'yii\web\UserEvent', 221 | 'yii\web\XmlResponseFormatter', 222 | 'yii\web\YiiAsset', 223 | 'yii\widgets\ActiveField', 224 | 'yii\widgets\ActiveForm', 225 | 'yii\widgets\ActiveFormAsset', 226 | 'yii\widgets\Block', 227 | 'yii\widgets\Breadcrumbs', 228 | 'yii\widgets\ContentDecorator', 229 | 'yii\widgets\DetailView', 230 | 'yii\widgets\FragmentCache', 231 | 'yii\widgets\InputWidget', 232 | 'yii\widgets\LinkPager', 233 | 'yii\widgets\LinkSorter', 234 | 'yii\widgets\ListView', 235 | 'yii\widgets\MaskedInput', 236 | 'yii\widgets\MaskedInputAsset', 237 | 'yii\widgets\Menu', 238 | 'yii\widgets\Pjax', 239 | 'yii\widgets\PjaxAsset', 240 | 'yii\widgets\Spaceless', 241 | ]; 242 | /** 243 | * @var array 持久化的类实例 244 | */ 245 | public static $persistInstances = []; 246 | /** 247 | * 在最终构造类时, 尝试检查类的别名 248 | * 249 | * @inheritdoc 250 | */ 251 | protected function build($class, $params, $config) 252 | { 253 | // 检查类的别名 避免使用者出错,默认使用这些类时,覆盖为重写的类 254 | if (isset(static::$classAlias[$class])) { 255 | $class = static::$classAlias[$class]; 256 | } 257 | // 如果允许持久化 258 | if ($class && in_array($class, static::$persistClasses)) { 259 | /* @var $reflection \ReflectionClass */ 260 | list($reflection, $dependencies) = $this->getDependencies($class); 261 | 262 | foreach ($params as $index => $param) { 263 | $dependencies[$index] = $param; 264 | } 265 | $dependencies = $this->resolveDependencies($dependencies, $reflection); 266 | if (!$reflection->isInstantiable()) { 267 | throw new NotInstantiableException($reflection->name); 268 | } 269 | if (!isset(static::$persistInstances[$class])) { 270 | static::$persistInstances[$class] = $reflection->newInstanceWithoutConstructor(); 271 | } 272 | $object = clone static::$persistInstances[$class]; 273 | // 如果有params参数的话, 则交给构造方法去执行 274 | // 如果类没有构造函数,确传了参数会报异常 275 | if (!empty($dependencies)) { 276 | if ($reflection->implementsInterface('yii\base\Configurable')) { 277 | // set $config as the last parameter (existing one will be overwritten) 278 | $dependencies[count($dependencies) - 1] = $config; 279 | $reflection->getConstructor()->invokeArgs($object, $dependencies); 280 | } else { 281 | $reflection->getConstructor()->invokeArgs($object, $dependencies); 282 | foreach ($config as $name => $value) { 283 | $object->$name = $value; 284 | } 285 | return $object; 286 | } 287 | 288 | } else { 289 | if ($object instanceof Object) { 290 | $dependencies[0] = $config; 291 | $reflection->getConstructor()->invokeArgs($object, $dependencies); 292 | } else { 293 | 294 | // 执行一些配置信息 295 | foreach ($config as $name => $value) { 296 | $object->$name = $value; 297 | } 298 | } 299 | 300 | return $object; 301 | } 302 | } 303 | 304 | return parent::build($class, $params, $config); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/redis/Connection.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\redis; 20 | 21 | use yii\base\Exception; 22 | 23 | class Connection extends \yii\redis\Connection 24 | { 25 | /** 26 | * @var resource redis socket connection 27 | */ 28 | private $_socket; 29 | /** 30 | * Returns a value indicating whether the DB connection is established. 31 | * @return boolean whether the DB connection is established 32 | */ 33 | public function getIsActive() 34 | { 35 | return $this->_socket !== null; 36 | } 37 | 38 | /** 39 | * Establishes a DB connection. 40 | * It does nothing if a DB connection has already been established. 41 | * @throws Exception if connection fails 42 | */ 43 | public function open() 44 | { 45 | if ($this->_socket !== null) { 46 | return; 47 | } 48 | $connection = ($this->unixSocket ?: $this->hostname . ':' . $this->port) . ', database=' . $this->database; 49 | \Yii::trace('Opening redis DB connection: ' . $connection, __METHOD__); 50 | $this->_socket = @stream_socket_client( 51 | $this->unixSocket ? 'unix://' . $this->unixSocket : 'tcp://' . $this->hostname . ':' . $this->port, 52 | $errorNumber, 53 | $errorDescription, 54 | $this->connectionTimeout ? $this->connectionTimeout : ini_get("default_socket_timeout") 55 | ); 56 | if ($this->_socket) { 57 | if ($this->dataTimeout !== null) { 58 | stream_set_timeout($this->_socket, $timeout = (int) $this->dataTimeout, (int) (($this->dataTimeout - $timeout) * 1000000)); 59 | } 60 | if ($this->password !== null) { 61 | $this->executeCommand('AUTH', [$this->password]); 62 | } 63 | $this->executeCommand('SELECT', [$this->database]); 64 | $this->initConnection(); 65 | } else { 66 | \Yii::error("Failed to open redis DB connection ($connection): $errorNumber - $errorDescription", __CLASS__); 67 | $message = YII_DEBUG ? "Failed to open redis DB connection ($connection): $errorNumber - $errorDescription" : 'Failed to open DB connection.'; 68 | throw new \Exception($message, (int) $errorNumber); 69 | } 70 | } 71 | /** 72 | * Executes a redis command. 73 | * For a list of available commands and their parameters see http://redis.io/commands. 74 | * 75 | * @param string $name the name of the command 76 | * @param array $params list of parameters for the command 77 | * @return array|bool|null|string Dependent on the executed command this method 78 | * will return different data types: 79 | * 80 | * - `true` for commands that return "status reply" with the message `'OK'` or `'PONG'`. 81 | * - `string` for commands that return "status reply" that does not have the message `OK` (since version 2.0.1). 82 | * - `string` for commands that return "integer reply" 83 | * as the value is in the range of a signed 64 bit integer. 84 | * - `string` or `null` for commands that return "bulk reply". 85 | * - `array` for commands that return "Multi-bulk replies". 86 | * 87 | * See [redis protocol description](http://redis.io/topics/protocol) 88 | * for details on the mentioned reply types. 89 | * @trows Exception for commands that return [error reply](http://redis.io/topics/protocol#error-reply). 90 | */ 91 | public function executeCommand($name, $params = [], $reconnect = 0) 92 | { 93 | $this->open(); 94 | // backup the params for try again when execute fail 95 | $oldName = $name; 96 | $oldParams = $params; 97 | array_unshift($params, $name); 98 | $command = '*' . count($params) . "\r\n"; 99 | foreach ($params as $arg) { 100 | $command .= '$' . mb_strlen($arg, '8bit') . "\r\n" . $arg . "\r\n"; 101 | } 102 | 103 | \Yii::trace("Executing Redis Command: {$name}", __METHOD__); 104 | $length = @fwrite($this->_socket, $command); 105 | if ($length === false || $length < mb_strlen($command)) { 106 | if ($reconnect == 0) { 107 | $this->forceClose(); 108 | return $this->executeCommand($oldName, $oldParams, ++$reconnect); 109 | } else { 110 | throw new Exception('Try to send command to redis server fail. Maybe redis server has gone away.'); 111 | } 112 | } 113 | 114 | return $this->parseResponse(implode(' ', $params), $oldName, $oldParams); 115 | } 116 | /** 117 | * Closes the currently active DB connection. 118 | * It does nothing if the connection is already closed. 119 | */ 120 | public function forceClose() 121 | { 122 | if ($this->_socket !== null) { 123 | try { 124 | $connection = ($this->unixSocket ?: $this->hostname . ':' . $this->port) . ', database=' . $this->database; 125 | \Yii::trace('Closing DB connection: ' . $connection, __METHOD__); 126 | stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR); 127 | } catch (\Exception $e) { 128 | 129 | } 130 | $this->_socket = null; 131 | } 132 | } 133 | /** 134 | * @param string $command 135 | * @return mixed 136 | * @throws Exception on error 137 | */ 138 | private function parseResponse($command, $name = '', $params = [], $reconnect = 0) 139 | { 140 | if (($line = fgets($this->_socket)) === false) { 141 | if ($reconnect == 0) { 142 | $this->forceClose(); 143 | return $this->executeCommand($name, $params, ++$reconnect); 144 | } else { 145 | throw new Exception("Failed to read from socket.\nRedis command was: " . $command); 146 | } 147 | } 148 | $type = $line[0]; 149 | $line = mb_substr($line, 1, -2, '8bit'); 150 | switch ($type) { 151 | case '+': // Status reply 152 | if ($line === 'OK' || $line === 'PONG') { 153 | return true; 154 | } else { 155 | return $line; 156 | } 157 | case '-': // Error reply 158 | throw new Exception("Redis error: " . $line . "\nRedis command was: " . $command); 159 | case ':': // Integer reply 160 | // no cast to int as it is in the range of a signed 64 bit integer 161 | return $line; 162 | case '$': // Bulk replies 163 | if ($line == '-1') { 164 | return null; 165 | } 166 | $length = $line + 2; 167 | $data = ''; 168 | while ($length > 0) { 169 | if (($block = fread($this->_socket, $length)) === false) { 170 | throw new Exception("Failed to read from socket.\nRedis command was: " . $command); 171 | } 172 | $data .= $block; 173 | $length -= mb_strlen($block, '8bit'); 174 | } 175 | 176 | return mb_substr($data, 0, -2, '8bit'); 177 | case '*': // Multi-bulk replies 178 | $count = (int) $line; 179 | $data = []; 180 | for ($i = 0; $i < $count; $i++) { 181 | $data[] = $this->parseResponse($command, $name, $params); 182 | } 183 | 184 | return $data; 185 | default: 186 | throw new Exception('Received illegal data from redis: ' . $line . "\nRedis command was: " . $command); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/redis/Session.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\redis; 20 | 21 | use yiiswoole\traits\SessionTrait; 22 | 23 | class Session extends \yii\redis\Session 24 | { 25 | use SessionTrait; 26 | } 27 | -------------------------------------------------------------------------------- /src/traits/SessionTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\traits; 20 | 21 | trait SessionTrait 22 | { 23 | private $_hasSessionId = null; 24 | /** 25 | * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function 26 | * Array may have the following possible keys: 'lifetime', 'path', 'domain', 'secure', 'httponly' 27 | * @see http://www.php.net/manual/en/function.session-set-cookie-params.php 28 | */ 29 | private $_cookieParams = ['httponly' => true]; 30 | 31 | public function clear() 32 | { 33 | $this->setHasSessionId(null); 34 | $this->setCookieParams(['httponly' => true]); 35 | } 36 | public function open() 37 | { 38 | $ret = parent::open(); 39 | if ($this->getIsActive() && $this->getUseCookies() === true && $this->getHasSessionId() === false) { 40 | $data = $this->getCookieParams(); 41 | extract($data); 42 | if (isset($lifetime, $path, $domain, $secure, $httponly)) { 43 | $cookies = \Yii::$app->getResponse()->cookies; 44 | $cookies->add(new \yii\web\Cookie([ 45 | 'name' => $this->getName(), 46 | 'value' => $this->getId(), 47 | 'domain' => $domain, 48 | 'path' => $path, 49 | 'expire' => $lifetime ? time() + $lifetime : 0, 50 | 'secure' => $secure, 51 | 'httpOnly' => $httponly, 52 | ])); 53 | } else { 54 | $cookies = \Yii::$app->getResponse()->cookies; 55 | $cookies->add(new \yii\web\Cookie([ 56 | 'name' => $this->getName(), 57 | 'value' => $this->getId(), 58 | ])); 59 | } 60 | } 61 | return $ret; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/web/Application.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\web; 20 | 21 | use yii\base\ExitException; 22 | 23 | class Application extends \yii\web\Application 24 | { 25 | protected static $controllerInstances = []; 26 | public function run() 27 | { 28 | try { 29 | 30 | $this->state = self::STATE_BEFORE_REQUEST; 31 | $this->trigger(self::EVENT_BEFORE_REQUEST); 32 | 33 | $this->state = self::STATE_HANDLING_REQUEST; 34 | /** 35 | * @var Response $response 36 | */ 37 | $response = $this->handleRequest($this->getRequest()); 38 | 39 | $this->state = self::STATE_AFTER_REQUEST; 40 | $this->trigger(self::EVENT_AFTER_REQUEST); 41 | 42 | $this->state = self::STATE_SENDING_RESPONSE; 43 | $result = $response->send(); 44 | 45 | $this->state = self::STATE_END; 46 | 47 | return $result; 48 | 49 | } catch (ExitException $e) { 50 | return $this->end($e->statusCode, isset($response) ? $response : null); 51 | } 52 | } 53 | /** 54 | * Terminates the application. 55 | * This method replaces the `exit()` function by ensuring the application life cycle is completed 56 | * before terminating the application. 57 | * @param int $status the exit status (value 0 means normal exit while other values mean abnormal exit). 58 | * @param Response $response the response to be sent. If not set, the default application [[response]] component will be used. 59 | * @throws ExitException if the application is in testing mode 60 | */ 61 | public function end($status = 0, $response = null) 62 | { 63 | if ($this->state === self::STATE_BEFORE_REQUEST || $this->state === self::STATE_HANDLING_REQUEST) { 64 | $this->state = self::STATE_AFTER_REQUEST; 65 | $this->trigger(self::EVENT_AFTER_REQUEST); 66 | } 67 | 68 | if ($this->state !== self::STATE_SENDING_RESPONSE && $this->state !== self::STATE_END) { 69 | $this->state = self::STATE_END; 70 | $response = $response ?: $this->getResponse(); 71 | return $response->send(); 72 | } 73 | } 74 | 75 | /** 76 | * @param string $route 77 | * @return array|bool 78 | */ 79 | public function createController($route) 80 | { 81 | if ($route === '') { 82 | $route = $this->defaultRoute; 83 | } 84 | if (!isset(static::$controllerInstances[$route])) { 85 | $controller = parent::createController($route); // TODO: Change the autogenerated stub 86 | if (false !== $controller) { 87 | static::$controllerInstances[$route] = $controller; 88 | } 89 | } 90 | if (isset(static::$controllerInstances[$route])) { 91 | return [ 92 | clone static::$controllerInstances[$route][0], 93 | static::$controllerInstances[$route][1], 94 | ]; 95 | } else { 96 | return false; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/web/Controller.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | namespace yiiswoole\web; 19 | 20 | use yii\base\InlineAction; 21 | 22 | class Controller extends \yii\web\Controller 23 | { 24 | protected static $actionInstances = []; 25 | public function createAction($id) 26 | { 27 | if ($id === '') { 28 | $id = $this->defaultAction; 29 | } 30 | // cache action instance for performance 31 | $key = md5(get_class($this)) . $id; 32 | if (isset(static::$actionInstances[$key])) { 33 | $action = clone static::$actionInstances[$key]; 34 | $action->controller = $this; 35 | return $action; 36 | } 37 | 38 | $action = null; 39 | $actionMap = $this->actions(); 40 | // 重写正则加入i参数,URL支持驼峰,大小写 41 | if (isset($actionMap[$id])) { 42 | $action = \Yii::createObject($actionMap[$id], [$id, null]); 43 | } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) { 44 | $methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))); 45 | if (method_exists($this, $methodName)) { 46 | $method = new \ReflectionMethod($this, $methodName); 47 | if ($method->isPublic() && $method->getName() === $methodName) { 48 | $action = new InlineAction($id, null, $methodName); 49 | } 50 | } 51 | } 52 | if ($action) { 53 | static::$actionInstances[$key] = $action; 54 | $action = clone static::$actionInstances[$key]; 55 | $action->controller = $this; 56 | } 57 | return $action; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/web/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\web; 20 | 21 | use Yii; 22 | use yii\base\ErrorException; 23 | use yii\base\ExitException; 24 | use yii\web\HttpException; 25 | 26 | class ErrorHandler extends \yii\web\ErrorHandler 27 | { 28 | /** 29 | * @var string Used to reserve memory for fatal error handler. 30 | */ 31 | private $_memoryReserve; 32 | 33 | /** 34 | * Register this error handler 35 | */ 36 | public function register() 37 | { 38 | ini_set('display_errors', false); 39 | 40 | set_error_handler([$this, 'handleError']); 41 | 42 | if ($this->memoryReserveSize > 0) { 43 | $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize); 44 | } 45 | register_shutdown_function([$this, 'handleFatalError']); 46 | } 47 | 48 | /** 49 | * Unregisters this error handler by restoring the PHP error and exception handlers. 50 | */ 51 | public function unregister() 52 | { 53 | restore_error_handler(); 54 | } 55 | 56 | /** 57 | * Handles uncaught PHP exceptions. 58 | * 59 | * This method is implemented as a PHP exception handler. 60 | * 61 | * @param \Exception $exception the exception that is not caught 62 | */ 63 | public function handleException($exception) 64 | { 65 | if ($exception instanceof ExitException) { 66 | return; 67 | } 68 | 69 | $this->exception = $exception; 70 | 71 | // disable error capturing to avoid recursive errors while handling exceptions 72 | $this->unregister(); 73 | 74 | // set preventive HTTP status code to 500 in case error handling somehow fails and headers are sent 75 | // HTTP exceptions will override this value in renderException() 76 | Yii::$app->getResponse()->setStatusCode(500); 77 | 78 | try { 79 | $this->logException($exception); 80 | if ($this->discardExistingOutput) { 81 | $this->clearOutput(); 82 | } 83 | $response = $this->renderException($exception); 84 | \Yii::getLogger()->flush(true); 85 | return $response; 86 | } catch (\Exception $e) { 87 | // an other exception could be thrown while displaying the exception 88 | $this->handleFallbackExceptionMessage($e, $exception); 89 | } catch (\Throwable $e) { 90 | // additional check for \Throwable introduced in PHP 7 91 | $this->handleFallbackExceptionMessage($e, $exception); 92 | } 93 | 94 | $this->exception = null; 95 | } 96 | /** 97 | * Handles uncaught PHP exceptions. 98 | * 99 | * This method is implemented as a PHP exception handler. 100 | * 101 | * @param \Exception $exception the exception that is not caught 102 | */ 103 | public function handleHttpException($exception) 104 | { 105 | $this->exception = $exception; 106 | 107 | // disable error capturing to avoid recursive errors while handling exceptions 108 | $this->unregister(); 109 | 110 | // set preventive HTTP status code to 500 in case error handling somehow fails and headers are sent 111 | // HTTP exceptions will override this value in renderException() 112 | Yii::$app->getResponse()->setStatusCode(500); 113 | 114 | try { 115 | if ($this->discardExistingOutput) { 116 | $this->clearOutput(); 117 | } 118 | $response = $this->renderException($exception); 119 | \Yii::getLogger()->flush(true); 120 | return $response; 121 | } catch (\Exception $e) { 122 | // an other exception could be thrown while displaying the exception 123 | $this->handleFallbackExceptionMessage($e, $exception); 124 | } catch (\Throwable $e) { 125 | // additional check for \Throwable introduced in PHP 7 126 | $this->handleFallbackExceptionMessage($e, $exception); 127 | } 128 | 129 | $this->exception = null; 130 | } 131 | 132 | /** 133 | * Handles exception thrown during exception processing in [[handleException()]]. 134 | * @param \Exception|\Throwable $exception Exception that was thrown during main exception processing. 135 | * @param \Exception $previousException Main exception processed in [[handleException()]]. 136 | * @since 2.0.11 137 | */ 138 | protected function handleFallbackExceptionMessage($exception, $previousException) 139 | { 140 | $msg = "An Error occurred while handling another error:\n"; 141 | $msg .= (string) $exception; 142 | $msg .= "\nPrevious exception:\n"; 143 | $msg .= (string) $previousException; 144 | if (YII_DEBUG) { 145 | if (PHP_SAPI === 'cli') { 146 | echo $msg . "\n"; 147 | } else { 148 | echo '
' . htmlspecialchars($msg, ENT_QUOTES, Yii::$app->charset) . '
'; 149 | } 150 | } else { 151 | echo 'An internal server error occurred.'; 152 | } 153 | $msg .= "\n\$_SERVER = " . json_encode($_SERVER); 154 | error_log($msg); 155 | } 156 | /** 157 | * Handles PHP execution errors such as warnings and notices. 158 | * 159 | * This method is used as a PHP error handler. It will simply raise an [[ErrorException]]. 160 | * 161 | * @param int $code the level of the error raised. 162 | * @param string $message the error message. 163 | * @param string $file the filename that the error was raised in. 164 | * @param int $line the line number the error was raised at. 165 | * @return bool whether the normal error handler continues. 166 | * 167 | * @throws ErrorException 168 | */ 169 | public function handleError($code, $message, $file, $line) 170 | { 171 | if (error_reporting() & $code) { 172 | $exception = new ErrorException($message, $code, $code, $file, $line); 173 | 174 | // in case error appeared in __toString method we can't throw any exception 175 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); 176 | array_shift($trace); 177 | foreach ($trace as $frame) { 178 | if ($frame['function'] === '__toString') { 179 | return $this->handleException($exception); 180 | } 181 | } 182 | 183 | throw $exception; 184 | } 185 | return false; 186 | } 187 | 188 | /** 189 | * Handles fatal PHP errors 190 | */ 191 | public function handleFatalError() 192 | { 193 | unset($this->_memoryReserve); 194 | 195 | $error = error_get_last(); 196 | 197 | if (ErrorException::isFatalError($error)) { 198 | 199 | $exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']); 200 | 201 | $this->exception = $exception; 202 | 203 | $this->logException($exception); 204 | 205 | if ($this->discardExistingOutput) { 206 | $this->clearOutput(); 207 | } 208 | $this->renderException($exception); 209 | 210 | // need to explicitly flush logs because exit() next will terminate the app immediately 211 | Yii::getLogger()->flush(true); 212 | } 213 | } 214 | /** 215 | * Renders the exception. 216 | * @param \Exception $exception the exception to be rendered. 217 | */ 218 | protected function renderException($exception) 219 | { 220 | if (Yii::$app->has('response')) { 221 | $response = Yii::$app->getResponse(); 222 | // reset parameters of response to avoid interference with partially created response data 223 | // in case the error occurred while sending the response. 224 | $response->isSent = false; 225 | $response->stream = null; 226 | $response->data = null; 227 | $response->content = null; 228 | } else { 229 | $response = new Response(); 230 | } 231 | 232 | $useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException); 233 | 234 | if ($useErrorView && $this->errorAction !== null) { 235 | $result = Yii::$app->runAction($this->errorAction); 236 | if ($result instanceof Response) { 237 | $response = $result; 238 | } else { 239 | $response->data = $result; 240 | } 241 | } elseif ($response->format === Response::FORMAT_HTML) { 242 | if (YII_ENV_TEST || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') { 243 | // AJAX request 244 | $response->data = '
' . $this->htmlEncode(static::convertExceptionToString($exception)) . '
'; 245 | } else { 246 | // if there is an error during error rendering it's useful to 247 | // display PHP error in debug mode instead of a blank screen 248 | if (YII_DEBUG) { 249 | ini_set('display_errors', 1); 250 | } 251 | $file = $useErrorView ? $this->errorView : $this->exceptionView; 252 | $response->data = $this->renderFile($file, [ 253 | 'exception' => $exception, 254 | ]); 255 | } 256 | } elseif ($response->format === Response::FORMAT_RAW) { 257 | $response->data = static::convertExceptionToString($exception); 258 | } else { 259 | $response->data = $this->convertExceptionToArray($exception); 260 | } 261 | 262 | if ($exception instanceof HttpException) { 263 | $response->setStatusCode($exception->statusCode); 264 | } else { 265 | $response->setStatusCode(500); 266 | } 267 | 268 | return $response->send(); 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/web/Request.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\web; 20 | 21 | use yii\web\HeaderCollection; 22 | 23 | class Request extends \yii\web\Request 24 | { 25 | /** 26 | * @var \BriarBear\Request 27 | */ 28 | public $request = null; 29 | /** 30 | * @var array the headers in this collection (indexed by the header names) 31 | */ 32 | private $_headers = null; 33 | 34 | /** 35 | * @param \BriarBear\Request $request 36 | */ 37 | public function setBriabearRequest(\BriarBear\Request $request) 38 | { 39 | $this->request = $request; 40 | } 41 | /** 42 | * Returns the header collection. 43 | * The header collection contains incoming HTTP headers. 44 | * @return HeaderCollection the header collection 45 | */ 46 | public function getHeaders() 47 | { 48 | if ($this->_headers === null) { 49 | $this->_headers = new HeaderCollection; 50 | if ($this->request) { 51 | $headers = $this->request->getHead(); 52 | } else { 53 | foreach ($_SERVER as $name => $value) { 54 | if (strncmp($name, 'HTTP_', 5) === 0) { 55 | $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))); 56 | $this->_headers->add($name, $value); 57 | } 58 | } 59 | 60 | return $this->_headers; 61 | } 62 | foreach ($headers as $name => $value) { 63 | $this->_headers->add($name, $value); 64 | } 65 | } 66 | 67 | return $this->_headers; 68 | } 69 | private $_rawBody; 70 | 71 | /** 72 | * Returns the raw HTTP request body. 73 | * @return string the request body 74 | */ 75 | public function getRawBody() 76 | { 77 | if ($this->_rawBody === null) { 78 | $this->_rawBody = $this->request->getRawContent(); 79 | } 80 | 81 | return $this->_rawBody; 82 | } 83 | 84 | /** 85 | * Sets the raw HTTP request body, this method is mainly used by test scripts to simulate raw HTTP requests. 86 | * @param string $rawBody the request body 87 | */ 88 | public function setRawBody($rawBody) 89 | { 90 | $this->_rawBody = $rawBody; 91 | } 92 | /** 93 | * Returns the user IP address. 94 | * @return string|null user IP address, null if not available 95 | */ 96 | public function getUserIP() 97 | { 98 | $ip=false; 99 | if(!empty($_SERVER["HTTP_CLIENT_IP"])){ 100 | $ip = $_SERVER["HTTP_CLIENT_IP"]; 101 | } 102 | if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { 103 | $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; 104 | } 105 | return ($ip ? $ip : ($_SERVER['REMOTE_ADDR'] ?? 'unknown')); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/web/Response.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\web; 20 | 21 | use BriarBear\Response\HttpResponse; 22 | use BriarBear\Response\TcpResponse; 23 | use BriarBear\Server; 24 | use Yii; 25 | use yii\base\InvalidConfigException; 26 | 27 | class Response extends \yii\web\Response 28 | { 29 | public $requestType = null; 30 | /** 31 | * @var HttpResponse|TcpResponse 32 | */ 33 | public $response = null; 34 | 35 | /** 36 | * @return string 37 | */ 38 | public function getRequestType() 39 | { 40 | return $this->requestType; 41 | } 42 | 43 | /** 44 | * Server::REQUEST_TYPE_HTTP 45 | * Server::REQUEST_TYPE_TCP 46 | * Server::REQUEST_TYPE_WEBSOCKET 47 | * @param string $requestType 48 | */ 49 | public function setRequestType($requestType) 50 | { 51 | $this->requestType = $requestType; 52 | } 53 | /** 54 | * Sends the response to the client. 55 | * @return mixed 56 | * 57 | */ 58 | public function send() 59 | { 60 | if ($this->isSent) { 61 | return $this->response; 62 | } 63 | 64 | $this->trigger(self::EVENT_BEFORE_SEND); 65 | 66 | switch ($this->requestType) { 67 | case Server::REQUEST_TYPE_HTTP: 68 | $this->prepare(); 69 | $this->response = \BriarBear\Response\Response::getInstance('HTTP'); 70 | $this->trigger(self::EVENT_AFTER_PREPARE); 71 | $this->sendHeaders(); 72 | break; 73 | case Server::REQUEST_TYPE_TCP: 74 | $this->response = \BriarBear\Response\Response::getInstance('TCP'); 75 | $this->content = $this->data; 76 | break; 77 | case Server::REQUEST_TYPE_WEBSOCKET: 78 | //TODO not finish for websocket 79 | $this->response = new HttpResponse(); 80 | break; 81 | } 82 | $this->sendContent(); 83 | $this->trigger(self::EVENT_AFTER_SEND); 84 | $this->isSent = true; 85 | return $this->response; 86 | } 87 | 88 | /** 89 | * Sends the response headers to the client 90 | */ 91 | protected function sendHeaders() 92 | { 93 | $headers = $this->getHeaders(); 94 | foreach ($headers as $name => $values) { 95 | $name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $name))); 96 | foreach ($values as $value) { 97 | $this->response->setHeader($name, $value); 98 | } 99 | } 100 | 101 | $statusCode = $this->getStatusCode(); 102 | $this->response->setHttpStatus($statusCode); 103 | $this->sendCookies(); 104 | } 105 | 106 | /** 107 | * Sends the cookies to the client. 108 | */ 109 | protected function sendCookies() 110 | { 111 | $cookies = $this->getCookies(); 112 | if (count($cookies) === 0) { 113 | return; 114 | } 115 | $request = Yii::$app->getRequest(); 116 | if ($request->enableCookieValidation) { 117 | if ($request->cookieValidationKey == '') { 118 | throw new InvalidConfigException(get_class($request) . '::cookieValidationKey must be configured with a secret key.'); 119 | } 120 | $validationKey = $request->cookieValidationKey; 121 | } 122 | foreach ($cookies as $cookie) { 123 | $value = $cookie->value; 124 | if ($cookie->expire != 1 && isset($validationKey)) { 125 | $value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey); 126 | } 127 | $this->response->setCookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly); 128 | } 129 | } 130 | 131 | /** 132 | * Sends the response content to the client 133 | */ 134 | protected function sendContent() 135 | { 136 | if ($this->xSendFilePath) { 137 | $this->response->sendFile($this->xSendFilePath); 138 | return; 139 | } 140 | if ($this->stream === null) { 141 | $this->response->setBody($this->content); 142 | 143 | return; 144 | } 145 | 146 | set_time_limit(0); // Reset time limit for big files 147 | $chunkSize = 8 * 1024 * 1024; // 8MB per chunk 148 | $this->response->setBody(''); 149 | if (is_array($this->stream)) { 150 | list($handle, $begin, $end) = $this->stream; 151 | fseek($handle, $begin); 152 | while (!feof($handle) && ($pos = ftell($handle)) <= $end) { 153 | if ($pos + $chunkSize > $end) { 154 | $chunkSize = $end - $pos + 1; 155 | } 156 | $this->response->addBody(fread($handle, $chunkSize)); 157 | } 158 | fclose($handle); 159 | } else { 160 | while (!feof($this->stream)) { 161 | $this->response->setBody(fread($this->stream, $chunkSize)); 162 | } 163 | fclose($this->stream); 164 | } 165 | } 166 | /** 167 | * Disable Http Range 168 | * Determines the HTTP range given in the request. 169 | * @param int $fileSize the size of the file that will be used to validate the requested HTTP range. 170 | * @return array|bool the range (begin, end), or false if the range request is invalid. 171 | */ 172 | protected function getHttpRange($fileSize) 173 | { 174 | return [0, $fileSize - 1]; 175 | 176 | } 177 | protected $xSendFilePath; 178 | public function xSendFile($filePath, $attachmentName = null, $options = []) 179 | { 180 | parent::xSendFile($filePath, $attachmentName, $options); // TODO: Change the autogenerated stub 181 | $this->xSendFilePath = $filePath; 182 | return $this; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/web/Session.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | namespace yiiswoole\web; 19 | 20 | use yiiswoole\traits\SessionTrait; 21 | 22 | class Session extends \yii\web\Session 23 | { 24 | use SessionTrait; 25 | } 26 | -------------------------------------------------------------------------------- /src/web/User.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2017/3/1 6 | * Time: 18:17 7 | * @link https://github.com/lscgzwd 8 | * @copyright Copyright (c) 2017 Lu Shun Cheng (https://github.com/lscgzwd) 9 | * @licence http://www.apache.org/licenses/LICENSE-2.0 10 | * @author Lu Shun Cheng (lscgzwd@gmail.com) 11 | */ 12 | /** 13 | * 14 | * @link http://www.yiiframework.com/ 15 | * @copyright Copyright (c) 2008 Yii Software LLC 16 | * @license http://www.yiiframework.com/license/ 17 | */ 18 | 19 | namespace yiiswoole\web; 20 | 21 | class User extends \yii\web\User 22 | { 23 | private $_identity = false; 24 | private $_access = []; 25 | public function clear() 26 | { 27 | $this->_identity = false; 28 | $this->_access = []; 29 | } 30 | } 31 | --------------------------------------------------------------------------------