Commit 48b314ba8c6a5a4dd7d09f0ea7228884c13869d1

Authored by luoyanshou
1 parent 13c2ced1

[Swoole 集成 ThinkPHP 3.2]

trunk/Swoole/Cli/Controller/IndexController.class.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: luoys
  5 + * Date: 2019/2/13
  6 + * Time: 17:29
  7 + */
  8 +
  9 +namespace Cli\Controller;
  10 +
  11 +class IndexController
  12 +{
  13 + public function index()
  14 + {
  15 + print_r(C('MODULE_DENY_LIST'));
  16 + //echo "Swoole 服务端已启动。 \n";
  17 + }
  18 +}
0 19 \ No newline at end of file
... ...
trunk/Swoole/SwooleClient.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: luoys
  5 + * Date: 2019/2/13
  6 + * Time: 11:42
  7 + */
  8 +
  9 +class SwooleClient
  10 +{
  11 + private $_client;
  12 +
  13 + public function __construct($type = SWOOLE_SOCK_TCP, $is_sync = SWOOLE_SOCK_SYNC)
  14 + {
  15 + $this->_client = new swoole_client($type, $is_sync);
  16 + }
  17 +
  18 + public function connect($host, $port = null, $timeout = null, $sock_flag = null)
  19 + {
  20 + if (!$this->_client->connect($host, $port, $timeout, $sock_flag)) {
  21 + exit("connect failed. Error: {$this->_client->errCode}\n");
  22 + }
  23 + }
  24 +
  25 + public function send($data)
  26 + {
  27 + if ($this->_client->send($data) === false)
  28 + {
  29 + exit("send data failed. Error: {$this->_client->errCode} \n");
  30 + }
  31 + }
  32 +
  33 + public function sendfile($filename, $offset, $length)
  34 + {
  35 + if ($this->_client->sendfile($filename, $offset, $length) === false) {
  36 + exit("{$filename} is not exist. \n");
  37 + }
  38 + }
  39 +
  40 + public function sendto($ip, $port, $data)
  41 + {
  42 + $this->_client->sendto($ip, $port, $data);
  43 + }
  44 +
  45 + public function recv($size = 65535, $flags = 0)
  46 + {
  47 + if (($recv = $this->_client->recv($size, $flags)) === false) {
  48 + exit("receive data failed. Error: {$this->_client->errCode} \n");
  49 + }
  50 + return $recv;
  51 + }
  52 +
  53 +}
  54 +
  55 +$client = new SwooleClient();
  56 +$client->connect('127.0.0.1', 9501);
  57 +$client->send('11223');
  58 +
  59 +$recv = $client->recv();
  60 +echo $recv;
0 61 \ No newline at end of file
... ...
trunk/Swoole/SwooleServer.php 0 → 100644
  1 +<?php
  2 +
  3 +/**
  4 + * Swoole服务端
  5 + */
  6 +class SwooleServer
  7 +{
  8 +
  9 + private $_serv = null;
  10 + private $_setting = array();
  11 +
  12 + public function __construct($host = '0.0.0.0', $port = 9501)
  13 + {
  14 + $this->_setting = array(
  15 + 'host' => $host,
  16 + 'port' => $port,
  17 + 'env' => 'dev', //环境 dev|test|prod
  18 + 'process_name' => SWOOLE_TASK_NAME_PRE, //swoole 进程名称
  19 + 'worker_num' => 4, //一般设置为服务器CPU数的1-4倍
  20 + 'task_worker_num' => 1, //task进程的数量
  21 + 'task_ipc_mode' => 3, //使用消息队列通信,并设置为争抢模式
  22 + 'task_max_request' => 10000, //task进程的最大任务数
  23 + 'daemonize' => 0, //以守护进程执行
  24 + 'max_request' => 10000,
  25 + 'dispatch_mode' => 2,
  26 + 'log_file' => SWOOLE_PATH . DIRECTORY_SEPARATOR . 'App' . DIRECTORY_SEPARATOR . 'Runtime' . DIRECTORY_SEPARATOR . 'Logs' . DIRECTORY_SEPARATOR . 'Swoole' . date('Ymd') . '.log', //日志
  27 + );
  28 + }
  29 +
  30 + /**
  31 + * 运行swoole服务
  32 + */
  33 + public function run()
  34 + {
  35 + $this->_serv = new swoole_server($this->_setting['host'], $this->_setting['port']);
  36 + $this->_serv->set(array(
  37 + 'worker_num' => $this->_setting['worker_num'],
  38 + 'task_worker_num' => $this->_setting['task_worker_num'],
  39 + 'task_ipc_mode ' => $this->_setting['task_ipc_mode'],'task_max_request' => $this->_setting['task_max_request'],
  40 + 'daemonize' => $this->_setting['daemonize'],
  41 + 'max_request' => $this->_setting['max_request'],
  42 + 'dispatch_mode' => $this->_setting['dispatch_mode'],
  43 + //'log_file' => $this->_setting['log_file']
  44 + ));
  45 + $this->_serv->on('Start', array($this, 'onStart'));
  46 + $this->_serv->on('Connect', array($this, 'onConnect'));
  47 + $this->_serv->on('WorkerStart', array($this, 'onWorkerStart'));
  48 + $this->_serv->on('ManagerStart', array($this, 'onManagerStart'));
  49 + $this->_serv->on('WorkerStop', array($this, 'onWorkerStop'));
  50 + $this->_serv->on('Receive', array($this, 'onReceive'));
  51 + $this->_serv->on('Task', array($this, 'onTask'));
  52 + $this->_serv->on('Finish', array($this, 'onFinish'));
  53 + $this->_serv->on('Shutdown', array($this, 'onShutdown'));
  54 + $this->_serv->on('Close', array($this, 'onClose'));
  55 +
  56 + $this->_serv->start();
  57 + }
  58 +
  59 + /**
  60 + * 设置swoole进程名称
  61 + * @param string $name swoole进程名称
  62 + */
  63 + private function setProcessName($name)
  64 + {
  65 + if (function_exists('cli_set_process_title')) {
  66 + cli_set_process_title($name);
  67 + } else {
  68 + if (function_exists('swoole_set_process_name')) {
  69 + swoole_set_process_name($name);
  70 + } else {
  71 + trigger_error(__METHOD__ . " failed. require cli_set_process_title or swoole_set_process_name.");
  72 + }
  73 + }
  74 + }
  75 +
  76 + /**
  77 + * Server启动在主进程的主线程回调此函数
  78 + * @param $serv
  79 + */
  80 + public function onStart($serv)
  81 + {
  82 + if (!$this->_setting['daemonize']) {
  83 + echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server master worker start\n";
  84 + }
  85 + $this->setProcessName($this->_setting['process_name'] . '-master');
  86 + //记录进程id,脚本实现自动重启
  87 + $pid = "{$serv->master_pid}\n{$serv->manager_pid}";
  88 + file_put_contents(SWOOLE_TASK_PID_PATH, $pid);
  89 + }
  90 +
  91 + /**
  92 + * worker start 加载业务脚本常驻内存
  93 + * @param $server
  94 + * @param $workerId
  95 + */
  96 + public function onWorkerStart($serv, $workerId)
  97 + {
  98 + if ($workerId >= $this->_setting['worker_num']) {
  99 + $this->setProcessName($this->_setting['process_name'] . '-task');
  100 + } else {
  101 + $this->setProcessName($this->_setting['process_name'] . '-event');
  102 + }
  103 + // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
  104 + define('APP_DEBUG', True);
  105 + // 定义应用目录
  106 + define('APP_PATH', SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR);
  107 + // 定义应用模式
  108 + define('APP_MODE', 'cli');
  109 +
  110 + // 绑定默认模块
  111 + define('BIND_MODULE', 'Cli');
  112 + // 绑定默认控制器
  113 + define('BIND_CONTROLLER', 'Index');
  114 + // 引入ThinkPHP入口文件
  115 + require_once SWOOLE_PATH . DIRECTORY_SEPARATOR . 'ThinkPHP' . DIRECTORY_SEPARATOR . 'ThinkPHP.php';
  116 + }
  117 +
  118 + /**
  119 + * 监听连接进入事件
  120 + * @param $serv
  121 + * @param $fd
  122 + */
  123 + public function onConnect($serv, $fd)
  124 + {
  125 + if (!$this->_setting['daemonize']) {
  126 + echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server connect[" . $fd . "]\n";
  127 + }
  128 + }
  129 +
  130 + /**
  131 + * worker 进程停止
  132 + * @param $server
  133 + * @param $workerId
  134 + */
  135 + public function onWorkerStop($serv, $workerId)
  136 + {
  137 + if (!$this->_setting['daemonize']) {
  138 + echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server[{$serv->setting['process_name']} worker:{$workerId} shutdown\n";
  139 + }
  140 + }
  141 +
  142 + /**
  143 + * 当管理进程启动时调用
  144 + * @param $serv
  145 + */
  146 + public function onManagerStart($serv)
  147 + {
  148 + if (!$this->_setting['daemonize']) {
  149 + echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server manager worker start\n";
  150 + }
  151 + $this->setProcessName($this->_setting['process_name'] . '-manager');
  152 + }
  153 +
  154 + /**
  155 + * 此事件在Server结束时发生
  156 + */
  157 + public function onShutdown($serv)
  158 + {
  159 + if (file_exists(SWOOLE_TASK_PID_PATH)) {
  160 + unlink(SWOOLE_TASK_PID_PATH);
  161 + }
  162 + if (!$this->_setting['daemonize']) {
  163 + echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server shutdown\n";
  164 + }
  165 + }
  166 +
  167 + /**
  168 + * 监听数据发送事件
  169 + * @param $serv
  170 + * @param $fd
  171 + * @param $from_id
  172 + * @param $data
  173 + */
  174 + public function onReceive($serv, $fd, $from_id, $data)
  175 + {
  176 + if (!$this->_setting['daemonize']) {
  177 + echo "Get Message From Client {$fd}:{$data}\n\n";
  178 + }
  179 + $result = json_decode($data, true);
  180 + switch ($result['action']) {
  181 + case 'reload': //重启
  182 + $serv->reload();
  183 + break;
  184 + case 'close': //关闭
  185 + $serv->shutdown();
  186 + break;
  187 + case 'status': //状态
  188 + $serv->send($fd, json_encode($serv->stats()));
  189 + break;
  190 + default:
  191 + $serv->task($data);
  192 + break;
  193 + }
  194 + }
  195 +
  196 + /**
  197 + * 监听连接Task事件
  198 + * @param $serv
  199 + * @param $task_id
  200 + * @param $from_id
  201 + * @param $data
  202 + */
  203 + public function onTask($serv, $task_id, $from_id, $data)
  204 + {
  205 + $result = json_decode($data, true);
  206 + //用TP处理各种逻辑
  207 + $serv->finish($data);
  208 + }
  209 +
  210 + /**
  211 + * 监听连接Finish事件
  212 + * @param $serv
  213 + * @param $task_id
  214 + * @param $data
  215 + */
  216 + public function onFinish($serv, $task_id, $data)
  217 + {
  218 + if (!$this->_setting['daemonize']) {
  219 + echo "Task {$task_id} finish\n\n";
  220 + echo "Result: {$data}\n\n";
  221 + }
  222 + }
  223 +
  224 + /**
  225 + * 监听连接关闭事件
  226 + * @param $serv
  227 + * @param $fd
  228 + */
  229 + public function onClose($serv, $fd)
  230 + {
  231 + if (!$this->_setting['daemonize']) {
  232 + echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server close[" . $fd . "]\n";
  233 + }
  234 + }
  235 +}
... ...
trunk/Swoole/tmp/swoole-task.pid 0 → 100644
  1 +29653
  2 +29662
0 3 \ No newline at end of file
... ...
trunk/Swoole/相关命令.txt 0 → 100644
  1 +1、服务启动
  2 + #启动服务,不指定绑定端口和ip,则使用默认配置
  3 + php swoole.php start
  4 + #启动服务 指定ip 和 port
  5 + php swoole.php -h127.0.0.1 -p9501 start
  6 + #启动服务 守护进程模式
  7 + php swoole.php -h127.0.0.1 -p9501 -d start
  8 + #启动服务 非守护进程模式
  9 + php swoole.php -h127.0.0.1 -p9501 -D start
  10 + #启动服务 指定进程名称(显示进程名为 swooleServ-9510-[master|manager|event|task]
  11 + php swoole.php -h127.0.0.1 -p9501 -n 9501 start
  12 +
  13 +
  14 +2、强制服务停止
  15 + php swoole.php stop
  16 + php swoole.php -p9501 stop
  17 + php swoole.php -h127.0.0.1 -p9501 stop
  18 +
  19 +
  20 +3、关闭服务
  21 + php swoole.php close
  22 + php swoole.php -p9501 close
  23 + php swoole.php -h127.0.0.1 -p9501 close
  24 +
  25 +
  26 +4、强制服务重启
  27 + php swoole.php restart
  28 + php swoole.php -p9501 restart
  29 + php swoole.php -h127.0.0.1 -p9501 restart
  30 +
  31 +
  32 +5、平滑服务重启
  33 + php swoole.php reload
  34 + php swoole.php -p9501 reload
  35 + php swoole.php -h127.0.0.1 -p9501 reload
  36 +
  37 +
  38 +6、服务状态
  39 + php swoole.php status
  40 + php swoole.php -h127.0.0.1 -p9501 status
  41 +
  42 +
  43 +7、swoole-task所有启动实例进程列表(一台服务器swoole-task可以有多个端口绑定的实例)
  44 + php swoole.php list
  45 +
  46 +
  47 +
  48 +QQ:506723561
0 49 \ No newline at end of file
... ...
trunk/ThinkPHP/Mode/Cli/App.class.php 0 → 100644
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace Think;
  12 +/**
  13 + * ThinkPHP CLI模式应用程序类 执行应用过程管理
  14 + */
  15 +class App {
  16 +
  17 + /**
  18 + * 应用程序初始化
  19 + * @access public
  20 + * @return void
  21 + */
  22 + static public function init() {
  23 + // 加载动态应用公共文件和配置
  24 + load_ext_file(COMMON_PATH);
  25 +
  26 + // 日志目录转换为绝对路径 默认情况下存储到公共模块下面
  27 + C('LOG_PATH', realpath(LOG_PATH).'/Common/');
  28 +
  29 + // 定义当前请求的系统常量
  30 + define('NOW_TIME', $_SERVER['REQUEST_TIME']);
  31 + define('REQUEST_METHOD',$_SERVER['REQUEST_METHOD']);
  32 + define('IS_GET', REQUEST_METHOD =='GET' ? true : false);
  33 + define('IS_POST', REQUEST_METHOD =='POST' ? true : false);
  34 + define('IS_PUT', REQUEST_METHOD =='PUT' ? true : false);
  35 + define('IS_DELETE', REQUEST_METHOD =='DELETE' ? true : false);
  36 +
  37 + // URL调度
  38 + Dispatcher::dispatch();
  39 +
  40 + if(C('REQUEST_VARS_FILTER')){
  41 + // 全局安全过滤
  42 + array_walk_recursive($_GET, 'think_filter');
  43 + array_walk_recursive($_POST, 'think_filter');
  44 + array_walk_recursive($_REQUEST, 'think_filter');
  45 + }
  46 +
  47 + // URL调度结束标签
  48 + Hook::listen('url_dispatch');
  49 +
  50 + define('IS_AJAX', ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || !empty($_POST[C('VAR_AJAX_SUBMIT')]) || !empty($_GET[C('VAR_AJAX_SUBMIT')])) ? true : false);
  51 +
  52 + // TMPL_EXCEPTION_FILE 改为绝对地址
  53 + C('TMPL_EXCEPTION_FILE',realpath(C('TMPL_EXCEPTION_FILE')));
  54 + return ;
  55 + }
  56 +
  57 + /**
  58 + * 执行应用程序
  59 + * @access public
  60 + * @return void
  61 + */
  62 + static public function exec() {
  63 +
  64 + if(!preg_match('/^[A-Za-z](\/|\w)*$/',CONTROLLER_NAME)){ // 安全检测
  65 + $module = false;
  66 + }elseif(C('ACTION_BIND_CLASS')){
  67 + // 操作绑定到类:模块\Controller\控制器\操作
  68 + $layer = C('DEFAULT_C_LAYER');
  69 + if(is_dir(MODULE_PATH.$layer.'/'.CONTROLLER_NAME)){
  70 + $namespace = MODULE_NAME.'\\'.$layer.'\\'.CONTROLLER_NAME.'\\';
  71 + }else{
  72 + // 空控制器
  73 + $namespace = MODULE_NAME.'\\'.$layer.'\\_empty\\';
  74 + }
  75 + $actionName = strtolower(ACTION_NAME);
  76 + if(class_exists($namespace.$actionName)){
  77 + $class = $namespace.$actionName;
  78 + }elseif(class_exists($namespace.'_empty')){
  79 + // 空操作
  80 + $class = $namespace.'_empty';
  81 + }else{
  82 + E(L('_ERROR_ACTION_').':'.ACTION_NAME);
  83 + }
  84 + $module = new $class;
  85 + // 操作绑定到类后 固定执行run入口
  86 + $action = 'run';
  87 + }else{
  88 + //创建控制器实例
  89 + $module = controller(CONTROLLER_NAME,CONTROLLER_PATH);
  90 + }
  91 +
  92 + if(!$module) {
  93 + if('4e5e5d7364f443e28fbf0d3ae744a59a' == CONTROLLER_NAME) {
  94 + header("Content-type:image/png");
  95 + exit(base64_decode(App::logo()));
  96 + }
  97 +
  98 + // 是否定义Empty控制器
  99 + $module = A('Empty');
  100 + if(!$module){
  101 + E(L('_CONTROLLER_NOT_EXIST_').':'.CONTROLLER_NAME);
  102 + }
  103 + }
  104 +
  105 + // 获取当前操作名 支持动态路由
  106 + if(!isset($action)){
  107 + $action = ACTION_NAME.C('ACTION_SUFFIX');
  108 + }
  109 + try{
  110 + self::invokeAction($module,$action);
  111 + } catch (\ReflectionException $e) {
  112 + // 方法调用发生异常后 引导到__call方法处理
  113 + $method = new \ReflectionMethod($module,'__call');
  114 + $method->invokeArgs($module,array($action,''));
  115 + }
  116 + return ;
  117 + }
  118 + public static function invokeAction($module,$action){
  119 + if(!preg_match('/^[A-Za-z](\w)*$/',$action)){
  120 + // 非法操作
  121 + throw new \ReflectionException();
  122 + }
  123 + //执行当前操作
  124 + $method = new \ReflectionMethod($module, $action);
  125 + if($method->isPublic() && !$method->isStatic()) {
  126 + $class = new \ReflectionClass($module);
  127 + // 前置操作
  128 + if($class->hasMethod('_before_'.$action)) {
  129 + $before = $class->getMethod('_before_'.$action);
  130 + if($before->isPublic()) {
  131 + $before->invoke($module);
  132 + }
  133 + }
  134 + // URL参数绑定检测
  135 + if($method->getNumberOfParameters()>0 && C('URL_PARAMS_BIND')){
  136 + switch($_SERVER['REQUEST_METHOD']) {
  137 + case 'POST':
  138 + $vars = array_merge($_GET,$_POST);
  139 + break;
  140 + case 'PUT':
  141 + parse_str(file_get_contents('php://input'), $vars);
  142 + break;
  143 + default:
  144 + $vars = $_GET;
  145 + }
  146 + $params = $method->getParameters();
  147 + $paramsBindType = C('URL_PARAMS_BIND_TYPE');
  148 + foreach ($params as $param){
  149 + $name = $param->getName();
  150 + if( 1 == $paramsBindType && !empty($vars) ){
  151 + $args[] = array_shift($vars);
  152 + }elseif( 0 == $paramsBindType && isset($vars[$name])){
  153 + $args[] = $vars[$name];
  154 + }elseif($param->isDefaultValueAvailable()){
  155 + $args[] = $param->getDefaultValue();
  156 + }else{
  157 + E(L('_PARAM_ERROR_').':'.$name);
  158 + }
  159 + }
  160 + // 开启绑定参数过滤机制
  161 + if(C('URL_PARAMS_SAFE')){
  162 + $filters = C('URL_PARAMS_FILTER')?:C('DEFAULT_FILTER');
  163 + if($filters) {
  164 + $filters = explode(',',$filters);
  165 + foreach($filters as $filter){
  166 + $args = array_map_recursive($filter,$args); // 参数过滤
  167 + }
  168 + }
  169 + }
  170 + array_walk_recursive($args,'think_filter');
  171 + $method->invokeArgs($module,$args);
  172 + }else{
  173 + $method->invoke($module);
  174 + }
  175 + // 后置操作
  176 + if($class->hasMethod('_after_'.$action)) {
  177 + $after = $class->getMethod('_after_'.$action);
  178 + if($after->isPublic()) {
  179 + $after->invoke($module);
  180 + }
  181 + }
  182 + }else{
  183 + // 操作方法不是Public 抛出异常
  184 + throw new \ReflectionException();
  185 + }
  186 + }
  187 + /**
  188 + * 运行应用实例 入口文件使用的快捷方法
  189 + * @access public
  190 + * @return void
  191 + */
  192 + static public function run() {
  193 + // 应用初始化标签
  194 + Hook::listen('app_init');
  195 + App::init();
  196 + // 应用开始标签
  197 + Hook::listen('app_begin');
  198 + // Session初始化
  199 + if(!IS_CLI){
  200 + session(C('SESSION_OPTIONS'));
  201 + }
  202 + // 记录应用初始化时间
  203 + G('initTime');
  204 + App::exec();
  205 + // 应用结束标签
  206 + Hook::listen('app_end');
  207 + return ;
  208 + }
  209 +
  210 + static public function logo(){
  211 + return 'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjVERDVENkZGQjkyNDExRTE5REY3RDQ5RTQ2RTRDQUJCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjVERDVENzAwQjkyNDExRTE5REY3RDQ5RTQ2RTRDQUJCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NURENUQ2RkRCOTI0MTFFMTlERjdENDlFNDZFNENBQkIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NURENUQ2RkVCOTI0MTFFMTlERjdENDlFNDZFNENBQkIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5fx6IRAAAMCElEQVR42sxae3BU1Rk/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';
  212 + }
  213 +}
... ...
trunk/ThinkPHP/Mode/Cli/Controller.class.php 0 → 100644
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace Think;
  12 +/**
  13 + * ThinkPHP CLI模式控制器基类 抽象类
  14 + */
  15 +abstract class Controller {
  16 +
  17 + /**
  18 + * 控制器参数
  19 + * @var config
  20 + * @access protected
  21 + */
  22 + protected $config = array();
  23 +
  24 + /**
  25 + * 架构函数 取得模板对象实例
  26 + * @access public
  27 + */
  28 + public function __construct() {
  29 + Hook::listen('action_begin',$this->config);
  30 + //控制器初始化
  31 + if(method_exists($this,'_initialize'))
  32 + $this->_initialize();
  33 + }
  34 +
  35 + /**
  36 + * 魔术方法 有不存在的操作的时候执行
  37 + * @access public
  38 + * @param string $method 方法名
  39 + * @param array $args 参数
  40 + * @return mixed
  41 + */
  42 + public function __call($method,$args) {
  43 + if( 0 === strcasecmp($method,ACTION_NAME.C('ACTION_SUFFIX'))) {
  44 + if(method_exists($this,'_empty')) {
  45 + // 如果定义了_empty操作 则调用
  46 + $this->_empty($method,$args);
  47 + }else{
  48 + E(L('_ERROR_ACTION_').':'.ACTION_NAME);
  49 + }
  50 + }else{
  51 + E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
  52 + return;
  53 + }
  54 + }
  55 +
  56 + /**
  57 + * Ajax方式返回数据到客户端
  58 + * @access protected
  59 + * @param mixed $data 要返回的数据
  60 + * @param String $type AJAX返回数据格式
  61 + * @param int $json_option 传递给json_encode的option参数
  62 + * @return void
  63 + */
  64 + protected function ajaxReturn($data,$type='',$json_option=0) {
  65 + if(empty($type)) $type = C('DEFAULT_AJAX_RETURN');
  66 + switch (strtoupper($type)){
  67 + case 'JSON' :
  68 + // 返回JSON数据格式到客户端 包含状态信息
  69 + header('Content-Type:application/json; charset=utf-8');
  70 + exit(json_encode($data,$json_option));
  71 + case 'XML' :
  72 + // 返回xml格式数据
  73 + header('Content-Type:text/xml; charset=utf-8');
  74 + exit(xml_encode($data));
  75 + case 'JSONP':
  76 + // 返回JSON数据格式到客户端 包含状态信息
  77 + header('Content-Type:application/json; charset=utf-8');
  78 + $handler = isset($_GET[C('VAR_JSONP_HANDLER')]) ? $_GET[C('VAR_JSONP_HANDLER')] : C('DEFAULT_JSONP_HANDLER');
  79 + exit($handler.'('.json_encode($data,$json_option).');');
  80 + case 'EVAL' :
  81 + // 返回可执行的js脚本
  82 + header('Content-Type:text/html; charset=utf-8');
  83 + exit($data);
  84 + default :
  85 + // 用于扩展其他返回格式数据
  86 + Hook::listen('ajax_return',$data);
  87 + }
  88 + }
  89 +
  90 + /**
  91 + * Action跳转(URL重定向) 支持指定模块和延时跳转
  92 + * @access protected
  93 + * @param string $url 跳转的URL表达式
  94 + * @param array $params 其它URL参数
  95 + * @param integer $delay 延时跳转的时间 单位为秒
  96 + * @param string $msg 跳转提示信息
  97 + * @return void
  98 + */
  99 + protected function redirect($url,$params=array(),$delay=0,$msg='') {
  100 + $url = U($url,$params);
  101 + redirect($url,$delay,$msg);
  102 + }
  103 +
  104 + /**
  105 + * 析构方法
  106 + * @access public
  107 + */
  108 + public function __destruct() {
  109 + // 执行后续操作
  110 + Hook::listen('action_end');
  111 + }
  112 +}
  113 +// 设置控制器别名 便于升级
  114 +class_alias('Think\Controller','Think\Action');
... ...
trunk/ThinkPHP/Mode/Cli/Dispatcher.class.php 0 → 100644
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace Think;
  12 +/**
  13 + * ThinkPHP内置的Dispatcher类
  14 + * 完成URL解析、路由和调度
  15 + */
  16 +class Dispatcher {
  17 +
  18 + /**
  19 + * URL映射到控制器
  20 + * @access public
  21 + * @return void
  22 + */
  23 + static public function dispatch() {
  24 + $varPath = C('VAR_PATHINFO');
  25 + $varAddon = C('VAR_ADDON');
  26 + $varModule = C('VAR_MODULE');
  27 + $varController = C('VAR_CONTROLLER');
  28 + $varAction = C('VAR_ACTION');
  29 + $urlCase = C('URL_CASE_INSENSITIVE');
  30 + if(isset($_GET[$varPath])) { // 判断URL里面是否有兼容模式参数
  31 + $_SERVER['PATH_INFO'] = $_GET[$varPath];
  32 + unset($_GET[$varPath]);
  33 + }elseif(IS_CLI){ // CLI模式下 index.php module/controller/action/params/...
  34 + $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
  35 + }
  36 +
  37 + // 开启子域名部署
  38 + if(C('APP_SUB_DOMAIN_DEPLOY')) {
  39 + $rules = C('APP_SUB_DOMAIN_RULES');
  40 + if(isset($rules[$_SERVER['HTTP_HOST']])) { // 完整域名或者IP配置
  41 + define('APP_DOMAIN',$_SERVER['HTTP_HOST']); // 当前完整域名
  42 + $rule = $rules[APP_DOMAIN];
  43 + }else{
  44 + if(strpos(C('APP_DOMAIN_SUFFIX'),'.')){ // com.cn net.cn
  45 + $domain = array_slice(explode('.', $_SERVER['HTTP_HOST']), 0, -3);
  46 + }else{
  47 + $domain = array_slice(explode('.', $_SERVER['HTTP_HOST']), 0, -2);
  48 + }
  49 + if(!empty($domain)) {
  50 + $subDomain = implode('.', $domain);
  51 + define('SUB_DOMAIN',$subDomain); // 当前完整子域名
  52 + $domain2 = array_pop($domain); // 二级域名
  53 + if($domain) { // 存在三级域名
  54 + $domain3 = array_pop($domain);
  55 + }
  56 + if(isset($rules[$subDomain])) { // 子域名
  57 + $rule = $rules[$subDomain];
  58 + }elseif(isset($rules['*.' . $domain2]) && !empty($domain3)){ // 泛三级域名
  59 + $rule = $rules['*.' . $domain2];
  60 + $panDomain = $domain3;
  61 + }elseif(isset($rules['*']) && !empty($domain2) && 'www' != $domain2 ){ // 泛二级域名
  62 + $rule = $rules['*'];
  63 + $panDomain = $domain2;
  64 + }
  65 + }
  66 + }
  67 +
  68 + if(!empty($rule)) {
  69 + // 子域名部署规则 '子域名'=>array('模块名[/控制器名]','var1=a&var2=b');
  70 + if(is_array($rule)){
  71 + list($rule,$vars) = $rule;
  72 + }
  73 + $array = explode('/',$rule);
  74 + // 模块绑定
  75 + define('BIND_MODULE',array_shift($array));
  76 + // 控制器绑定
  77 + if(!empty($array)) {
  78 + $controller = array_shift($array);
  79 + if($controller){
  80 + define('BIND_CONTROLLER',$controller);
  81 + }
  82 + }
  83 + if(isset($vars)) { // 传入参数
  84 + parse_str($vars,$parms);
  85 + if(isset($panDomain)){
  86 + $pos = array_search('*', $parms);
  87 + if(false !== $pos) {
  88 + // 泛域名作为参数
  89 + $parms[$pos] = $panDomain;
  90 + }
  91 + }
  92 + $_GET = array_merge($_GET,$parms);
  93 + }
  94 + }
  95 + }
  96 + // 分析PATHINFO信息
  97 + if(!isset($_SERVER['PATH_INFO'])) {
  98 + $types = explode(',',C('URL_PATHINFO_FETCH'));
  99 + foreach ($types as $type){
  100 + if(0===strpos($type,':')) {// 支持函数判断
  101 + $_SERVER['PATH_INFO'] = call_user_func(substr($type,1));
  102 + break;
  103 + }elseif(!empty($_SERVER[$type])) {
  104 + $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type],$_SERVER['SCRIPT_NAME']))?
  105 + substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];
  106 + break;
  107 + }
  108 + }
  109 + }
  110 +
  111 + $depr = C('URL_PATHINFO_DEPR');
  112 + define('MODULE_PATHINFO_DEPR', $depr);
  113 +
  114 + if(empty($_SERVER['PATH_INFO'])) {
  115 + $_SERVER['PATH_INFO'] = '';
  116 + define('__INFO__','');
  117 + define('__EXT__','');
  118 + }else{
  119 + define('__INFO__',trim($_SERVER['PATH_INFO'],'/'));
  120 + // URL后缀
  121 + define('__EXT__', strtolower(pathinfo($_SERVER['PATH_INFO'],PATHINFO_EXTENSION)));
  122 + $_SERVER['PATH_INFO'] = __INFO__;
  123 + if(!defined('BIND_MODULE') && (!C('URL_ROUTER_ON') || !Route::check())){
  124 + if (__INFO__ && C('MULTI_MODULE')){ // 获取模块名
  125 + $paths = explode($depr,__INFO__,2);
  126 + $allowList = C('MODULE_ALLOW_LIST'); // 允许的模块列表
  127 + $module = preg_replace('/\.' . __EXT__ . '$/i', '',$paths[0]);
  128 + if( empty($allowList) || (is_array($allowList) && in_array_case($module, $allowList))){
  129 + $_GET[$varModule] = $module;
  130 + $_SERVER['PATH_INFO'] = isset($paths[1])?$paths[1]:'';
  131 + }
  132 + }
  133 + }
  134 + }
  135 +
  136 + // URL常量
  137 + define('__SELF__',strip_tags($_SERVER[C('URL_REQUEST_URI')]));
  138 +
  139 + // 获取模块名称
  140 + define('MODULE_NAME', defined('BIND_MODULE')? BIND_MODULE : self::getModule($varModule));
  141 +
  142 + // 检测模块是否存在
  143 + if( MODULE_NAME && (defined('BIND_MODULE') || !in_array_case(MODULE_NAME,C('MODULE_DENY_LIST')) ) && is_dir(APP_PATH.MODULE_NAME)){
  144 + // 定义当前模块路径
  145 + define('MODULE_PATH', APP_PATH.MODULE_NAME.'/');
  146 + // 定义当前模块的模版缓存路径
  147 + C('CACHE_PATH',CACHE_PATH.MODULE_NAME.'/');
  148 + // 定义当前模块的日志目录
  149 + C('LOG_PATH', realpath(LOG_PATH).'/'.MODULE_NAME.'/');
  150 +
  151 + // 模块检测
  152 + Hook::listen('module_check');
  153 +
  154 + // 加载模块配置文件
  155 + if(is_file(MODULE_PATH.'Conf/config'.CONF_EXT))
  156 + C(load_config(MODULE_PATH.'Conf/config'.CONF_EXT));
  157 + // 加载应用模式对应的配置文件
  158 + if('common' != APP_MODE && is_file(MODULE_PATH.'Conf/config_'.APP_MODE.CONF_EXT))
  159 + C(load_config(MODULE_PATH.'Conf/config_'.APP_MODE.CONF_EXT));
  160 + // 当前应用状态对应的配置文件
  161 + if(APP_STATUS && is_file(MODULE_PATH.'Conf/'.APP_STATUS.CONF_EXT))
  162 + C(load_config(MODULE_PATH.'Conf/'.APP_STATUS.CONF_EXT));
  163 +
  164 + // 加载模块别名定义
  165 + if(is_file(MODULE_PATH.'Conf/alias.php'))
  166 + Think::addMap(include MODULE_PATH.'Conf/alias.php');
  167 + // 加载模块tags文件定义
  168 + if(is_file(MODULE_PATH.'Conf/tags.php'))
  169 + Hook::import(include MODULE_PATH.'Conf/tags.php');
  170 + // 加载模块函数文件
  171 + if(is_file(MODULE_PATH.'Common/function.php'))
  172 + include MODULE_PATH.'Common/function.php';
  173 +
  174 + $urlCase = C('URL_CASE_INSENSITIVE');
  175 + // 加载模块的扩展配置文件
  176 + load_ext_file(MODULE_PATH);
  177 + }else{
  178 + E(L('_MODULE_NOT_EXIST_').':'.MODULE_NAME);
  179 + }
  180 +
  181 + if(!defined('__APP__')){
  182 + $urlMode = C('URL_MODEL');
  183 + if($urlMode == URL_COMPAT ){// 兼容模式判断
  184 + define('PHP_FILE',_PHP_FILE_.'?'.$varPath.'=');
  185 + }elseif($urlMode == URL_REWRITE ) {
  186 + $url = dirname(_PHP_FILE_);
  187 + if($url == '/' || $url == '\\')
  188 + $url = '';
  189 + define('PHP_FILE',$url);
  190 + }else {
  191 + define('PHP_FILE',_PHP_FILE_);
  192 + }
  193 + // 当前应用地址
  194 + define('__APP__',strip_tags(PHP_FILE));
  195 + }
  196 + // 模块URL地址
  197 + $moduleName = defined('MODULE_ALIAS')? MODULE_ALIAS : MODULE_NAME;
  198 + define('__MODULE__',(defined('BIND_MODULE') || !C('MULTI_MODULE'))? __APP__ : __APP__.'/'.($urlCase ? strtolower($moduleName) : $moduleName));
  199 +
  200 + if('' != $_SERVER['PATH_INFO'] && (!C('URL_ROUTER_ON') || !Route::check()) ){ // 检测路由规则 如果没有则按默认规则调度URL
  201 + Hook::listen('path_info');
  202 + // 检查禁止访问的URL后缀
  203 + if(C('URL_DENY_SUFFIX') && preg_match('/\.('.trim(C('URL_DENY_SUFFIX'),'.').')$/i', $_SERVER['PATH_INFO'])){
  204 + send_http_status(404);
  205 + exit;
  206 + }
  207 +
  208 + // 去除URL后缀
  209 + $_SERVER['PATH_INFO'] = preg_replace(C('URL_HTML_SUFFIX')? '/\.('.trim(C('URL_HTML_SUFFIX'),'.').')$/i' : '/\.'.__EXT__.'$/i', '', $_SERVER['PATH_INFO']);
  210 +
  211 + $depr = C('URL_PATHINFO_DEPR');
  212 + $paths = explode($depr,trim($_SERVER['PATH_INFO'],$depr));
  213 +
  214 + if(!defined('BIND_CONTROLLER')) {// 获取控制器
  215 + if(C('CONTROLLER_LEVEL')>1){// 控制器层次
  216 + $_GET[$varController] = implode('/',array_slice($paths,0,C('CONTROLLER_LEVEL')));
  217 + $paths = array_slice($paths, C('CONTROLLER_LEVEL'));
  218 + }else{
  219 + $_GET[$varController] = array_shift($paths);
  220 + }
  221 + }
  222 + // 获取操作
  223 + if(!defined('BIND_ACTION')){
  224 + $_GET[$varAction] = array_shift($paths);
  225 + }
  226 + // 解析剩余的URL参数
  227 + $var = array();
  228 + if(C('URL_PARAMS_BIND') && 1 == C('URL_PARAMS_BIND_TYPE')){
  229 + // URL参数按顺序绑定变量
  230 + $var = $paths;
  231 + }else{
  232 + preg_replace_callback('/(\w+)\/([^\/]+)/', function($match) use(&$var){$var[$match[1]]=strip_tags($match[2]);}, implode('/',$paths));
  233 + }
  234 + $_GET = array_merge($var,$_GET);
  235 + }
  236 + // 获取控制器的命名空间(路径)
  237 + define('CONTROLLER_PATH', self::getSpace($varAddon,$urlCase));
  238 + // 获取控制器和操作名
  239 + define('CONTROLLER_NAME', defined('BIND_CONTROLLER')? BIND_CONTROLLER : self::getController($varController,$urlCase));
  240 + define('ACTION_NAME', defined('BIND_ACTION')? BIND_ACTION : self::getAction($varAction,$urlCase));
  241 +
  242 + // 当前控制器的UR地址
  243 + $controllerName = defined('CONTROLLER_ALIAS')? CONTROLLER_ALIAS : CONTROLLER_NAME;
  244 + define('__CONTROLLER__',__MODULE__.$depr.(defined('BIND_CONTROLLER')? '': ( $urlCase ? parse_name($controllerName) : $controllerName )) );
  245 +
  246 + // 当前操作的URL地址
  247 + define('__ACTION__',__CONTROLLER__.$depr.(defined('ACTION_ALIAS')?ACTION_ALIAS:ACTION_NAME));
  248 +
  249 + //保证$_REQUEST正常取值
  250 + $_REQUEST = array_merge($_POST,$_GET); // -- 加了$_COOKIE. 保证哦.. 保证个毛坑死人
  251 + }
  252 +
  253 + /**
  254 + * 获得控制器的命名空间路径 便于插件机制访问
  255 + */
  256 + static private function getSpace($var,$urlCase) {
  257 + $space = !empty($_GET[$var])?strip_tags($_GET[$var]):'';
  258 + unset($_GET[$var]);
  259 + return $space;
  260 + }
  261 +
  262 + /**
  263 + * 获得实际的控制器名称
  264 + */
  265 + static private function getController($var,$urlCase) {
  266 + $controller = (!empty($_GET[$var])? $_GET[$var]:C('DEFAULT_CONTROLLER'));
  267 + unset($_GET[$var]);
  268 + if($maps = C('URL_CONTROLLER_MAP')) {
  269 + if(isset($maps[strtolower($controller)])) {
  270 + // 记录当前别名
  271 + define('CONTROLLER_ALIAS',strtolower($controller));
  272 + // 获取实际的控制器名
  273 + return ucfirst($maps[CONTROLLER_ALIAS]);
  274 + }elseif(array_search(strtolower($controller),$maps)){
  275 + // 禁止访问原始控制器
  276 + return '';
  277 + }
  278 + }
  279 + if($urlCase) {
  280 + // URL地址不区分大小写
  281 + // 智能识别方式 user_type 识别到 UserTypeController 控制器
  282 + $controller = parse_name($controller,1);
  283 + }
  284 + return strip_tags(ucfirst($controller));
  285 + }
  286 +
  287 + /**
  288 + * 获得实际的操作名称
  289 + */
  290 + static private function getAction($var,$urlCase) {
  291 + $action = !empty($_POST[$var]) ?
  292 + $_POST[$var] :
  293 + (!empty($_GET[$var])?$_GET[$var]:C('DEFAULT_ACTION'));
  294 + unset($_POST[$var],$_GET[$var]);
  295 + if($maps = C('URL_ACTION_MAP')) {
  296 + if(isset($maps[strtolower(CONTROLLER_NAME)])) {
  297 + $maps = $maps[strtolower(CONTROLLER_NAME)];
  298 + if(isset($maps[strtolower($action)])) {
  299 + // 记录当前别名
  300 + define('ACTION_ALIAS',strtolower($action));
  301 + // 获取实际的操作名
  302 + if(is_array($maps[ACTION_ALIAS])){
  303 + parse_str($maps[ACTION_ALIAS][1],$vars);
  304 + $_GET = array_merge($_GET,$vars);
  305 + return $maps[ACTION_ALIAS][0];
  306 + }else{
  307 + return $maps[ACTION_ALIAS];
  308 + }
  309 +
  310 + }elseif(array_search(strtolower($action),$maps)){
  311 + // 禁止访问原始操作
  312 + return '';
  313 + }
  314 + }
  315 + }
  316 + return strip_tags( $urlCase? strtolower($action) : $action );
  317 + }
  318 +
  319 + /**
  320 + * 获得实际的模块名称
  321 + */
  322 + static private function getModule($var) {
  323 + $module = (!empty($_GET[$var])?$_GET[$var]:C('DEFAULT_MODULE'));
  324 + unset($_GET[$var]);
  325 + if($maps = C('URL_MODULE_MAP')) {
  326 + if(isset($maps[strtolower($module)])) {
  327 + // 记录当前别名
  328 + define('MODULE_ALIAS',strtolower($module));
  329 + // 获取实际的模块名
  330 + return ucfirst($maps[MODULE_ALIAS]);
  331 + }elseif(array_search(strtolower($module),$maps)){
  332 + // 禁止访问原始模块
  333 + return '';
  334 + }
  335 + }
  336 + return strip_tags(ucfirst($module));
  337 + }
  338 +
  339 +}
... ...
trunk/ThinkPHP/Mode/Cli/functions.php 0 → 100644
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +/**
  13 + * Think 系统函数库
  14 + */
  15 +
  16 +/**
  17 + * 获取和设置配置参数 支持批量定义
  18 + * @param string|array $name 配置变量
  19 + * @param mixed $value 配置值
  20 + * @param mixed $default 默认值
  21 + * @return mixed
  22 + */
  23 +function C($name=null, $value=null,$default=null) {
  24 + static $_config = array();
  25 + // 无参数时获取所有
  26 + if (empty($name)) {
  27 + return $_config;
  28 + }
  29 + // 优先执行设置获取或赋值
  30 + if (is_string($name)) {
  31 + if (!strpos($name, '.')) {
  32 + $name = strtoupper($name);
  33 + if (is_null($value))
  34 + return isset($_config[$name]) ? $_config[$name] : $default;
  35 + $_config[$name] = $value;
  36 + return null;
  37 + }
  38 + // 二维数组设置和获取支持
  39 + $name = explode('.', $name);
  40 + $name[0] = strtoupper($name[0]);
  41 + if (is_null($value))
  42 + return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default;
  43 + $_config[$name[0]][$name[1]] = $value;
  44 + return null;
  45 + }
  46 + // 批量设置
  47 + if (is_array($name)){
  48 + $_config = array_merge($_config, array_change_key_case($name,CASE_UPPER));
  49 + return null;
  50 + }
  51 + return null; // 避免非法参数
  52 +}
  53 +
  54 +/**
  55 + * 加载配置文件 支持格式转换 仅支持一级配置
  56 + * @param string $file 配置文件名
  57 + * @param string $parse 配置解析方法 有些格式需要用户自己解析
  58 + * @return array
  59 + */
  60 +function load_config($file,$parse=CONF_PARSE){
  61 + $ext = pathinfo($file,PATHINFO_EXTENSION);
  62 + switch($ext){
  63 + case 'php':
  64 + return include $file;
  65 + case 'ini':
  66 + return parse_ini_file($file);
  67 + case 'yaml':
  68 + return yaml_parse_file($file);
  69 + case 'xml':
  70 + return (array)simplexml_load_file($file);
  71 + case 'json':
  72 + return json_decode(file_get_contents($file), true);
  73 + default:
  74 + if(function_exists($parse)){
  75 + return $parse($file);
  76 + }else{
  77 + E(L('_NOT_SUPPORT_').':'.$ext);
  78 + }
  79 + }
  80 +}
  81 +
  82 +/**
  83 + * 解析yaml文件返回一个数组
  84 + * @param string $file 配置文件名
  85 + * @return array
  86 + */
  87 +if (!function_exists('yaml_parse_file')) {
  88 + function yaml_parse_file($file) {
  89 + vendor('spyc.Spyc');
  90 + return Spyc::YAMLLoad($file);
  91 + }
  92 +}
  93 +
  94 +/**
  95 + * 抛出异常处理
  96 + * @param string $msg 异常消息
  97 + * @param integer $code 异常代码 默认为0
  98 + * @throws Think\Exception
  99 + * @return void
  100 + */
  101 +function E($msg, $code=0) {
  102 + throw new Think\Exception($msg, $code);
  103 +}
  104 +
  105 +/**
  106 + * 记录和统计时间(微秒)和内存使用情况
  107 + * 使用方法:
  108 + * <code>
  109 + * G('begin'); // 记录开始标记位
  110 + * // ... 区间运行代码
  111 + * G('end'); // 记录结束标签位
  112 + * echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位
  113 + * echo G('begin','end','m'); // 统计区间内存使用情况
  114 + * 如果end标记位没有定义,则会自动以当前作为标记位
  115 + * 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
  116 + * </code>
  117 + * @param string $start 开始标签
  118 + * @param string $end 结束标签
  119 + * @param integer|string $dec 小数位或者m
  120 + * @return mixed
  121 + */
  122 +function G($start,$end='',$dec=4) {
  123 + static $_info = array();
  124 + static $_mem = array();
  125 + if(is_float($end)) { // 记录时间
  126 + $_info[$start] = $end;
  127 + }elseif(!empty($end)){ // 统计时间和内存使用
  128 + if(!isset($_info[$end])) $_info[$end] = microtime(TRUE);
  129 + if(MEMORY_LIMIT_ON && $dec=='m'){
  130 + if(!isset($_mem[$end])) $_mem[$end] = memory_get_usage();
  131 + return number_format(($_mem[$end]-$_mem[$start])/1024);
  132 + }else{
  133 + return number_format(($_info[$end]-$_info[$start]),$dec);
  134 + }
  135 +
  136 + }else{ // 记录时间和内存使用
  137 + $_info[$start] = microtime(TRUE);
  138 + if(MEMORY_LIMIT_ON) $_mem[$start] = memory_get_usage();
  139 + }
  140 + return null;
  141 +}
  142 +
  143 +/**
  144 + * 获取和设置语言定义(不区分大小写)
  145 + * @param string|array $name 语言变量
  146 + * @param mixed $value 语言值或者变量
  147 + * @return mixed
  148 + */
  149 +function L($name=null, $value=null) {
  150 + static $_lang = array();
  151 + // 空参数返回所有定义
  152 + if (empty($name))
  153 + return $_lang;
  154 + // 判断语言获取(或设置)
  155 + // 若不存在,直接返回全大写$name
  156 + if (is_string($name)) {
  157 + $name = strtoupper($name);
  158 + if (is_null($value)){
  159 + return isset($_lang[$name]) ? $_lang[$name] : $name;
  160 + }elseif(is_array($value)){
  161 + // 支持变量
  162 + $replace = array_keys($value);
  163 + foreach($replace as &$v){
  164 + $v = '{$'.$v.'}';
  165 + }
  166 + return str_replace($replace,$value,isset($_lang[$name]) ? $_lang[$name] : $name);
  167 + }
  168 + $_lang[$name] = $value; // 语言定义
  169 + return null;
  170 + }
  171 + // 批量定义
  172 + if (is_array($name))
  173 + $_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
  174 + return null;
  175 +}
  176 +
  177 +/**
  178 + * 添加和获取页面Trace记录
  179 + * @param string $value 变量
  180 + * @param string $label 标签
  181 + * @param string $level 日志级别
  182 + * @param boolean $record 是否记录日志
  183 + * @return void|array
  184 + */
  185 +function trace($value='[think]',$label='',$level='DEBUG',$record=false) {
  186 + return Think\Think::trace($value,$label,$level,$record);
  187 +}
  188 +
  189 +/**
  190 + * 编译文件
  191 + * @param string $filename 文件名
  192 + * @return string
  193 + */
  194 +function compile($filename) {
  195 + $content = php_strip_whitespace($filename);
  196 + $content = trim(substr($content, 5));
  197 + // 替换预编译指令
  198 + $content = preg_replace('/\/\/\[RUNTIME\](.*?)\/\/\[\/RUNTIME\]/s', '', $content);
  199 + if(0===strpos($content,'namespace')){
  200 + $content = preg_replace('/namespace\s(.*?);/','namespace \\1{',$content,1);
  201 + }else{
  202 + $content = 'namespace {'.$content;
  203 + }
  204 + if ('?>' == substr($content, -2))
  205 + $content = substr($content, 0, -2);
  206 + return $content.'}';
  207 +}
  208 +
  209 +/**
  210 + * 获取模版文件 格式 资源://模块@主题/控制器/操作
  211 + * @param string $template 模版资源地址
  212 + * @param string $layer 视图层(目录)名称
  213 + * @return string
  214 + */
  215 +function T($template='',$layer=''){
  216 +
  217 + // 解析模版资源地址
  218 + if(false === strpos($template,'://')){
  219 + $template = 'http://'.str_replace(':', '/',$template);
  220 + }
  221 + $info = parse_url($template);
  222 + $file = $info['host'].(isset($info['path'])?$info['path']:'');
  223 + $module = isset($info['user'])?$info['user'].'/':MODULE_NAME.'/';
  224 + $extend = $info['scheme'];
  225 + $layer = $layer?$layer:C('DEFAULT_V_LAYER');
  226 +
  227 + // 获取当前主题的模版路径
  228 + $auto = C('AUTOLOAD_NAMESPACE');
  229 + if($auto && isset($auto[$extend])){ // 扩展资源
  230 + $baseUrl = $auto[$extend].$module.$layer.'/';
  231 + }elseif(C('VIEW_PATH')){
  232 + // 改变模块视图目录
  233 + $baseUrl = C('VIEW_PATH');
  234 + }elseif(defined('TMPL_PATH')){
  235 + // 指定全局视图目录
  236 + $baseUrl = TMPL_PATH.$module;
  237 + }else{
  238 + $baseUrl = APP_PATH.$module.$layer.'/';
  239 + }
  240 +
  241 + // 获取主题
  242 + $theme = substr_count($file,'/')<2 ? C('DEFAULT_THEME') : '';
  243 +
  244 + // 分析模板文件规则
  245 + $depr = C('TMPL_FILE_DEPR');
  246 + if('' == $file) {
  247 + // 如果模板文件名为空 按照默认规则定位
  248 + $file = CONTROLLER_NAME . $depr . ACTION_NAME;
  249 + }elseif(false === strpos($file, '/')){
  250 + $file = CONTROLLER_NAME . $depr . $file;
  251 + }elseif('/' != $depr){
  252 + $file = substr_count($file,'/')>1 ? substr_replace($file,$depr,strrpos($file,'/'),1) : str_replace('/', $depr, $file);
  253 + }
  254 + return $baseUrl.($theme?$theme.'/':'').$file.C('TMPL_TEMPLATE_SUFFIX');
  255 +}
  256 +
  257 +/**
  258 + * 获取输入参数 支持过滤和默认值
  259 + * 使用方法:
  260 + * <code>
  261 + * I('id',0); 获取id参数 自动判断get或者post
  262 + * I('post.name','','htmlspecialchars'); 获取$_POST['name']
  263 + * I('get.'); 获取$_GET
  264 + * </code>
  265 + * @param string $name 变量的名称 支持指定类型
  266 + * @param mixed $default 不存在的时候默认值
  267 + * @param mixed $filter 参数过滤方法
  268 + * @param mixed $datas 要获取的额外数据源
  269 + * @return mixed
  270 + */
  271 +function I($name,$default='',$filter=null,$datas=null) {
  272 + static $_PUT = null;
  273 + if(strpos($name,'/')){ // 指定修饰符
  274 + list($name,$type) = explode('/',$name,2);
  275 + }elseif(C('VAR_AUTO_STRING')){ // 默认强制转换为字符串
  276 + $type = 's';
  277 + }
  278 + if(strpos($name,'.')) { // 指定参数来源
  279 + list($method,$name) = explode('.',$name,2);
  280 + }else{ // 默认为自动判断
  281 + $method = 'param';
  282 + }
  283 + switch(strtolower($method)) {
  284 + case 'get' :
  285 + $input =& $_GET;
  286 + break;
  287 + case 'post' :
  288 + $input =& $_POST;
  289 + break;
  290 + case 'put' :
  291 + if(is_null($_PUT)){
  292 + parse_str(file_get_contents('php://input'), $_PUT);
  293 + }
  294 + $input = $_PUT;
  295 + break;
  296 + case 'param' :
  297 + switch($_SERVER['REQUEST_METHOD']) {
  298 + case 'POST':
  299 + $input = $_POST;
  300 + break;
  301 + case 'PUT':
  302 + if(is_null($_PUT)){
  303 + parse_str(file_get_contents('php://input'), $_PUT);
  304 + }
  305 + $input = $_PUT;
  306 + break;
  307 + default:
  308 + $input = $_GET;
  309 + }
  310 + break;
  311 + case 'path' :
  312 + $input = array();
  313 + if(!empty($_SERVER['PATH_INFO'])){
  314 + $depr = C('URL_PATHINFO_DEPR');
  315 + $input = explode($depr,trim($_SERVER['PATH_INFO'],$depr));
  316 + }
  317 + break;
  318 + case 'request' :
  319 + $input =& $_REQUEST;
  320 + break;
  321 + case 'session' :
  322 + $input =& $_SESSION;
  323 + break;
  324 + case 'cookie' :
  325 + $input =& $_COOKIE;
  326 + break;
  327 + case 'server' :
  328 + $input =& $_SERVER;
  329 + break;
  330 + case 'globals' :
  331 + $input =& $GLOBALS;
  332 + break;
  333 + case 'data' :
  334 + $input =& $datas;
  335 + break;
  336 + default:
  337 + return null;
  338 + }
  339 + if(''==$name) { // 获取全部变量
  340 + $data = $input;
  341 + $filters = isset($filter)?$filter:C('DEFAULT_FILTER');
  342 + if($filters) {
  343 + if(is_string($filters)){
  344 + $filters = explode(',',$filters);
  345 + }
  346 + foreach($filters as $filter){
  347 + $data = array_map_recursive($filter,$data); // 参数过滤
  348 + }
  349 + }
  350 + }elseif(isset($input[$name])) { // 取值操作
  351 + $data = $input[$name];
  352 + $filters = isset($filter)?$filter:C('DEFAULT_FILTER');
  353 + if($filters) {
  354 + if(is_string($filters)){
  355 + if(0 === strpos($filters,'/')){
  356 + if(1 !== preg_match($filters,(string)$data)){
  357 + // 支持正则验证
  358 + return isset($default) ? $default : null;
  359 + }
  360 + }else{
  361 + $filters = explode(',',$filters);
  362 + }
  363 + }elseif(is_int($filters)){
  364 + $filters = array($filters);
  365 + }
  366 +
  367 + if(is_array($filters)){
  368 + foreach($filters as $filter){
  369 + if(function_exists($filter)) {
  370 + $data = is_array($data) ? array_map_recursive($filter,$data) : $filter($data); // 参数过滤
  371 + }else{
  372 + $data = filter_var($data,is_int($filter) ? $filter : filter_id($filter));
  373 + if(false === $data) {
  374 + return isset($default) ? $default : null;
  375 + }
  376 + }
  377 + }
  378 + }
  379 + }
  380 + if(!empty($type)){
  381 + switch(strtolower($type)){
  382 + case 'a': // 数组
  383 + $data = (array)$data;
  384 + break;
  385 + case 'd': // 数字
  386 + $data = (int)$data;
  387 + break;
  388 + case 'f': // 浮点
  389 + $data = (float)$data;
  390 + break;
  391 + case 'b': // 布尔
  392 + $data = (boolean)$data;
  393 + break;
  394 + case 's': // 字符串
  395 + default:
  396 + $data = (string)$data;
  397 + }
  398 + }
  399 + }else{ // 变量默认值
  400 + $data = isset($default)?$default:null;
  401 + }
  402 + is_array($data) && array_walk_recursive($data,'think_filter');
  403 + return $data;
  404 +}
  405 +
  406 +function array_map_recursive($filter, $data) {
  407 + $result = array();
  408 + foreach ($data as $key => $val) {
  409 + $result[$key] = is_array($val)
  410 + ? array_map_recursive($filter, $val)
  411 + : call_user_func($filter, $val);
  412 + }
  413 + return $result;
  414 + }
  415 +
  416 +/**
  417 + * 设置和获取统计数据
  418 + * 使用方法:
  419 + * <code>
  420 + * N('db',1); // 记录数据库操作次数
  421 + * N('read',1); // 记录读取次数
  422 + * echo N('db'); // 获取当前页面数据库的所有操作次数
  423 + * echo N('read'); // 获取当前页面读取次数
  424 + * </code>
  425 + * @param string $key 标识位置
  426 + * @param integer $step 步进值
  427 + * @param boolean $save 是否保存结果
  428 + * @return mixed
  429 + */
  430 +function N($key, $step=0,$save=false) {
  431 + static $_num = array();
  432 + if (!isset($_num[$key])) {
  433 + $_num[$key] = (false !== $save)? S('N_'.$key) : 0;
  434 + }
  435 + if (empty($step)){
  436 + return $_num[$key];
  437 + }else{
  438 + $_num[$key] = $_num[$key] + (int)$step;
  439 + }
  440 + if(false !== $save){ // 保存结果
  441 + S('N_'.$key,$_num[$key],$save);
  442 + }
  443 + return null;
  444 +}
  445 +
  446 +/**
  447 + * 字符串命名风格转换
  448 + * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  449 + * @param string $name 字符串
  450 + * @param integer $type 转换类型
  451 + * @return string
  452 + */
  453 +function parse_name($name, $type=0) {
  454 + if ($type) {
  455 + return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function($match){return strtoupper($match[1]);}, $name));
  456 + } else {
  457 + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
  458 + }
  459 +}
  460 +
  461 +/**
  462 + * 优化的require_once
  463 + * @param string $filename 文件地址
  464 + * @return boolean
  465 + */
  466 +function require_cache($filename) {
  467 + static $_importFiles = array();
  468 + if (!isset($_importFiles[$filename])) {
  469 + if (file_exists_case($filename)) {
  470 + require $filename;
  471 + $_importFiles[$filename] = true;
  472 + } else {
  473 + $_importFiles[$filename] = false;
  474 + }
  475 + }
  476 + return $_importFiles[$filename];
  477 +}
  478 +
  479 +/**
  480 + * 区分大小写的文件存在判断
  481 + * @param string $filename 文件地址
  482 + * @return boolean
  483 + */
  484 +function file_exists_case($filename) {
  485 + if (is_file($filename)) {
  486 + if (IS_WIN && APP_DEBUG) {
  487 + if (basename(realpath($filename)) != basename($filename))
  488 + return false;
  489 + }
  490 + return true;
  491 + }
  492 + return false;
  493 +}
  494 +
  495 +/**
  496 + * 导入所需的类库 同java的Import 本函数有缓存功能
  497 + * @param string $class 类库命名空间字符串
  498 + * @param string $baseUrl 起始路径
  499 + * @param string $ext 导入的文件扩展名
  500 + * @return boolean
  501 + */
  502 +function import($class, $baseUrl = '', $ext=EXT) {
  503 + static $_file = array();
  504 + $class = str_replace(array('.', '#'), array('/', '.'), $class);
  505 + if (isset($_file[$class . $baseUrl]))
  506 + return true;
  507 + else
  508 + $_file[$class . $baseUrl] = true;
  509 + $class_strut = explode('/', $class);
  510 + if (empty($baseUrl)) {
  511 + if ('@' == $class_strut[0] || MODULE_NAME == $class_strut[0]) {
  512 + //加载当前模块的类库
  513 + $baseUrl = MODULE_PATH;
  514 + $class = substr_replace($class, '', 0, strlen($class_strut[0]) + 1);
  515 + }elseif ('Common' == $class_strut[0]) {
  516 + //加载公共模块的类库
  517 + $baseUrl = COMMON_PATH;
  518 + $class = substr($class, 7);
  519 + }elseif (in_array($class_strut[0],array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$class_strut[0])) {
  520 + // 系统类库包和第三方类库包
  521 + $baseUrl = LIB_PATH;
  522 + }else { // 加载其他模块的类库
  523 + $baseUrl = APP_PATH;
  524 + }
  525 + }
  526 + if (substr($baseUrl, -1) != '/')
  527 + $baseUrl .= '/';
  528 + $classfile = $baseUrl . $class . $ext;
  529 + if (!class_exists(basename($class),false)) {
  530 + // 如果类不存在 则导入类库文件
  531 + return require_cache($classfile);
  532 + }
  533 + return null;
  534 +}
  535 +
  536 +/**
  537 + * 基于命名空间方式导入函数库
  538 + * load('@.Util.Array')
  539 + * @param string $name 函数库命名空间字符串
  540 + * @param string $baseUrl 起始路径
  541 + * @param string $ext 导入的文件扩展名
  542 + * @return void
  543 + */
  544 +function load($name, $baseUrl='', $ext='.php') {
  545 + $name = str_replace(array('.', '#'), array('/', '.'), $name);
  546 + if (empty($baseUrl)) {
  547 + if (0 === strpos($name, '@/')) {//加载当前模块函数库
  548 + $baseUrl = MODULE_PATH.'Common/';
  549 + $name = substr($name, 2);
  550 + } else { //加载其他模块函数库
  551 + $array = explode('/', $name);
  552 + $baseUrl = APP_PATH . array_shift($array).'/Common/';
  553 + $name = implode('/',$array);
  554 + }
  555 + }
  556 + if (substr($baseUrl, -1) != '/')
  557 + $baseUrl .= '/';
  558 + require_cache($baseUrl . $name . $ext);
  559 +}
  560 +
  561 +/**
  562 + * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
  563 + * @param string $class 类库
  564 + * @param string $baseUrl 基础目录
  565 + * @param string $ext 类库后缀
  566 + * @return boolean
  567 + */
  568 +function vendor($class, $baseUrl = '', $ext='.php') {
  569 + if (empty($baseUrl))
  570 + $baseUrl = VENDOR_PATH;
  571 + return import($class, $baseUrl, $ext);
  572 +}
  573 +
  574 +/**
  575 + * 实例化模型类 格式 [资源://][模块/]模型
  576 + * @param string $name 资源地址
  577 + * @param string $layer 模型层名称
  578 + * @return Think\Model
  579 + */
  580 +function D($name='',$layer='') {
  581 + if(empty($name)) return new Think\Model;
  582 + static $_model = array();
  583 + $layer = $layer? : C('DEFAULT_M_LAYER');
  584 + if(isset($_model[$name.$layer]))
  585 + return $_model[$name.$layer];
  586 + $class = parse_res_name($name,$layer);
  587 + if(class_exists($class)) {
  588 + $model = new $class(basename($name));
  589 + }elseif(false === strpos($name,'/')){
  590 + // 自动加载公共模块下面的模型
  591 + if(!C('APP_USE_NAMESPACE')){
  592 + import('Common/'.$layer.'/'.$class);
  593 + }else{
  594 + $class = '\\Common\\'.$layer.'\\'.$name.$layer;
  595 + }
  596 + $model = class_exists($class)? new $class($name) : new Think\Model($name);
  597 + }else {
  598 + Think\Log::record('D方法实例化没找到模型类'.$class,Think\Log::NOTICE);
  599 + $model = new Think\Model(basename($name));
  600 + }
  601 + $_model[$name.$layer] = $model;
  602 + return $model;
  603 +}
  604 +
  605 +/**
  606 + * 实例化一个没有模型文件的Model
  607 + * @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
  608 + * @param string $tablePrefix 表前缀
  609 + * @param mixed $connection 数据库连接信息
  610 + * @return Think\Model
  611 + */
  612 +function M($name='', $tablePrefix='',$connection='') {
  613 + static $_model = array();
  614 + if(strpos($name,':')) {
  615 + list($class,$name) = explode(':',$name);
  616 + }else{
  617 + $class = 'Think\\Model';
  618 + }
  619 + $guid = (is_array($connection)?implode('',$connection):$connection).$tablePrefix . $name . '_' . $class;
  620 + if (!isset($_model[$guid]))
  621 + $_model[$guid] = new $class($name,$tablePrefix,$connection);
  622 + return $_model[$guid];
  623 +}
  624 +
  625 +/**
  626 + * 解析资源地址并导入类库文件
  627 + * 例如 module/controller addon://module/behavior
  628 + * @param string $name 资源地址 格式:[扩展://][模块/]资源名
  629 + * @param string $layer 分层名称
  630 + * @param integer $level 控制器层次
  631 + * @return string
  632 + */
  633 +function parse_res_name($name,$layer,$level=1){
  634 + if(strpos($name,'://')) {// 指定扩展资源
  635 + list($extend,$name) = explode('://',$name);
  636 + }else{
  637 + $extend = '';
  638 + }
  639 + if(strpos($name,'/') && substr_count($name, '/')>=$level){ // 指定模块
  640 + list($module,$name) = explode('/',$name,2);
  641 + }else{
  642 + $module = defined('MODULE_NAME') ? MODULE_NAME : '' ;
  643 + }
  644 + $array = explode('/',$name);
  645 + if(!C('APP_USE_NAMESPACE')){
  646 + $class = parse_name($name, 1);
  647 + import($module.'/'.$layer.'/'.$class.$layer);
  648 + }else{
  649 + $class = $module.'\\'.$layer;
  650 + foreach($array as $name){
  651 + $class .= '\\'.parse_name($name, 1);
  652 + }
  653 + // 导入资源类库
  654 + if($extend){ // 扩展资源
  655 + $class = $extend.'\\'.$class;
  656 + }
  657 + }
  658 + return $class.$layer;
  659 +}
  660 +
  661 +/**
  662 + * 用于实例化访问控制器
  663 + * @param string $name 控制器名
  664 + * @param string $path 控制器命名空间(路径)
  665 + * @return Think\Controller|false
  666 + */
  667 +function controller($name,$path=''){
  668 + $layer = C('DEFAULT_C_LAYER');
  669 + if(!C('APP_USE_NAMESPACE')){
  670 + $class = parse_name($name, 1).$layer;
  671 + import(MODULE_NAME.'/'.$layer.'/'.$class);
  672 + }else{
  673 + $class = ( $path ? basename(ADDON_PATH).'\\'.$path : MODULE_NAME ).'\\'.$layer;
  674 + $array = explode('/',$name);
  675 + foreach($array as $name){
  676 + $class .= '\\'.parse_name($name, 1);
  677 + }
  678 + $class .= $layer;
  679 + }
  680 + if(class_exists($class)) {
  681 + return new $class();
  682 + }else {
  683 + return false;
  684 + }
  685 +}
  686 +
  687 +/**
  688 + * 实例化多层控制器 格式:[资源://][模块/]控制器
  689 + * @param string $name 资源地址
  690 + * @param string $layer 控制层名称
  691 + * @param integer $level 控制器层次
  692 + * @return Think\Controller|false
  693 + */
  694 +function A($name,$layer='',$level=0) {
  695 + static $_action = array();
  696 + $layer = $layer? : C('DEFAULT_C_LAYER');
  697 + $level = $level? : ($layer == C('DEFAULT_C_LAYER')?C('CONTROLLER_LEVEL'):1);
  698 + if(isset($_action[$name.$layer]))
  699 + return $_action[$name.$layer];
  700 +
  701 + $class = parse_res_name($name,$layer,$level);
  702 + if(class_exists($class)) {
  703 + $action = new $class();
  704 + $_action[$name.$layer] = $action;
  705 + return $action;
  706 + }else {
  707 + return false;
  708 + }
  709 +}
  710 +
  711 +
  712 +/**
  713 + * 远程调用控制器的操作方法 URL 参数格式 [资源://][模块/]控制器/操作
  714 + * @param string $url 调用地址
  715 + * @param string|array $vars 调用参数 支持字符串和数组
  716 + * @param string $layer 要调用的控制层名称
  717 + * @return mixed
  718 + */
  719 +function R($url,$vars=array(),$layer='') {
  720 + $info = pathinfo($url);
  721 + $action = $info['basename'];
  722 + $module = $info['dirname'];
  723 + $class = A($module,$layer);
  724 + if($class){
  725 + if(is_string($vars)) {
  726 + parse_str($vars,$vars);
  727 + }
  728 + return call_user_func_array(array(&$class,$action.C('ACTION_SUFFIX')),$vars);
  729 + }else{
  730 + return false;
  731 + }
  732 +}
  733 +
  734 +/**
  735 + * 处理标签扩展
  736 + * @param string $tag 标签名称
  737 + * @param mixed $params 传入参数
  738 + * @return void
  739 + */
  740 +function tag($tag, &$params=NULL) {
  741 + \Think\Hook::listen($tag,$params);
  742 +}
  743 +
  744 +/**
  745 + * 执行某个行为
  746 + * @param string $name 行为名称
  747 + * @param string $tag 标签名称(行为类无需传入)
  748 + * @param Mixed $params 传入的参数
  749 + * @return void
  750 + */
  751 +function B($name, $tag='',&$params=NULL) {
  752 + if(''==$tag){
  753 + $name .= 'Behavior';
  754 + }
  755 + return \Think\Hook::exec($name,$tag,$params);
  756 +}
  757 +
  758 +/**
  759 + * 去除代码中的空白和注释
  760 + * @param string $content 代码内容
  761 + * @return string
  762 + */
  763 +function strip_whitespace($content) {
  764 + $stripStr = '';
  765 + //分析php源码
  766 + $tokens = token_get_all($content);
  767 + $last_space = false;
  768 + for ($i = 0, $j = count($tokens); $i < $j; $i++) {
  769 + if (is_string($tokens[$i])) {
  770 + $last_space = false;
  771 + $stripStr .= $tokens[$i];
  772 + } else {
  773 + switch ($tokens[$i][0]) {
  774 + //过滤各种PHP注释
  775 + case T_COMMENT:
  776 + case T_DOC_COMMENT:
  777 + break;
  778 + //过滤空格
  779 + case T_WHITESPACE:
  780 + if (!$last_space) {
  781 + $stripStr .= ' ';
  782 + $last_space = true;
  783 + }
  784 + break;
  785 + case T_START_HEREDOC:
  786 + $stripStr .= "<<<THINK\n";
  787 + break;
  788 + case T_END_HEREDOC:
  789 + $stripStr .= "THINK;\n";
  790 + for($k = $i+1; $k < $j; $k++) {
  791 + if(is_string($tokens[$k]) && $tokens[$k] == ';') {
  792 + $i = $k;
  793 + break;
  794 + } else if($tokens[$k][0] == T_CLOSE_TAG) {
  795 + break;
  796 + }
  797 + }
  798 + break;
  799 + default:
  800 + $last_space = false;
  801 + $stripStr .= $tokens[$i][1];
  802 + }
  803 + }
  804 + }
  805 + return $stripStr;
  806 +}
  807 +
  808 +/**
  809 + * 自定义异常处理
  810 + * @param string $msg 异常消息
  811 + * @param string $type 异常类型 默认为Think\Exception
  812 + * @param integer $code 异常代码 默认为0
  813 + * @return void
  814 + */
  815 +function throw_exception($msg, $type='Think\\Exception', $code=0) {
  816 + Think\Log::record('建议使用E方法替代throw_exception',Think\Log::NOTICE);
  817 + if (class_exists($type, false))
  818 + throw new $type($msg, $code);
  819 + else
  820 + Think\Think::halt($msg); // 异常类型不存在则输出错误信息字串
  821 +}
  822 +
  823 +/**
  824 + * 浏览器友好的变量输出
  825 + * @param mixed $var 变量
  826 + * @param boolean $echo 是否输出 默认为True 如果为false 则返回输出字符串
  827 + * @param string $label 标签 默认为空
  828 + * @param boolean $strict 是否严谨 默认为true
  829 + * @return void|string
  830 + */
  831 +function dump($var, $echo=true, $label=null, $strict=true) {
  832 + $label = ($label === null) ? '' : rtrim($label) . ' ';
  833 + if (!$strict) {
  834 + if (ini_get('html_errors')) {
  835 + $output = print_r($var, true);
  836 + $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
  837 + } else {
  838 + $output = $label . print_r($var, true);
  839 + }
  840 + } else {
  841 + ob_start();
  842 + var_dump($var);
  843 + $output = ob_get_clean();
  844 + if (!extension_loaded('xdebug')) {
  845 + $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
  846 + $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
  847 + }
  848 + }
  849 + if ($echo) {
  850 + echo($output);
  851 + return null;
  852 + }else
  853 + return $output;
  854 +}
  855 +
  856 +/**
  857 + * 设置当前页面的布局
  858 + * @param string|false $layout 布局名称 为false的时候表示关闭布局
  859 + * @return void
  860 + */
  861 +function layout($layout) {
  862 + if(false !== $layout) {
  863 + // 开启布局
  864 + C('LAYOUT_ON',true);
  865 + if(is_string($layout)) { // 设置新的布局模板
  866 + C('LAYOUT_NAME',$layout);
  867 + }
  868 + }else{// 临时关闭布局
  869 + C('LAYOUT_ON',false);
  870 + }
  871 +}
  872 +
  873 +/**
  874 + * URL组装 支持不同URL模式
  875 + * @param string $url URL表达式,格式:'[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...'
  876 + * @param string|array $vars 传入的参数,支持数组和字符串
  877 + * @param string|boolean $suffix 伪静态后缀,默认为true表示获取配置值
  878 + * @param boolean $domain 是否显示域名
  879 + * @return string
  880 + */
  881 +function U($url='',$vars='',$suffix=true,$domain=false) {
  882 + // 解析URL
  883 + $info = parse_url($url);
  884 + $url = !empty($info['path'])?$info['path']:ACTION_NAME;
  885 + if(isset($info['fragment'])) { // 解析锚点
  886 + $anchor = $info['fragment'];
  887 + if(false !== strpos($anchor,'?')) { // 解析参数
  888 + list($anchor,$info['query']) = explode('?',$anchor,2);
  889 + }
  890 + if(false !== strpos($anchor,'@')) { // 解析域名
  891 + list($anchor,$host) = explode('@',$anchor, 2);
  892 + }
  893 + }elseif(false !== strpos($url,'@')) { // 解析域名
  894 + list($url,$host) = explode('@',$info['path'], 2);
  895 + }
  896 + // 解析子域名
  897 + if(isset($host)) {
  898 + $domain = $host.(strpos($host,'.')?'':strstr($_SERVER['HTTP_HOST'],'.'));
  899 + }elseif($domain===true){
  900 + $domain = $_SERVER['HTTP_HOST'];
  901 + if(C('APP_SUB_DOMAIN_DEPLOY') ) { // 开启子域名部署
  902 + $domain = $domain=='localhost'?'localhost':'www'.strstr($_SERVER['HTTP_HOST'],'.');
  903 + // '子域名'=>array('模块[/控制器]');
  904 + foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) {
  905 + $rule = is_array($rule)?$rule[0]:$rule;
  906 + if(false === strpos($key,'*') && 0=== strpos($url,$rule)) {
  907 + $domain = $key.strstr($domain,'.'); // 生成对应子域名
  908 + $url = substr_replace($url,'',0,strlen($rule));
  909 + break;
  910 + }
  911 + }
  912 + }
  913 + }
  914 +
  915 + // 解析参数
  916 + if(is_string($vars)) { // aaa=1&bbb=2 转换成数组
  917 + parse_str($vars,$vars);
  918 + }elseif(!is_array($vars)){
  919 + $vars = array();
  920 + }
  921 + if(isset($info['query'])) { // 解析地址里面参数 合并到vars
  922 + parse_str($info['query'],$params);
  923 + $vars = array_merge($params,$vars);
  924 + }
  925 +
  926 + // URL组装
  927 + $depr = C('URL_PATHINFO_DEPR');
  928 + $urlCase = C('URL_CASE_INSENSITIVE');
  929 + if($url) {
  930 + if(0=== strpos($url,'/')) {// 定义路由
  931 + $route = true;
  932 + $url = substr($url,1);
  933 + if('/' != $depr) {
  934 + $url = str_replace('/',$depr,$url);
  935 + }
  936 + }else{
  937 + if('/' != $depr) { // 安全替换
  938 + $url = str_replace('/',$depr,$url);
  939 + }
  940 + // 解析模块、控制器和操作
  941 + $url = trim($url,$depr);
  942 + $path = explode($depr,$url);
  943 + $var = array();
  944 + $varModule = C('VAR_MODULE');
  945 + $varController = C('VAR_CONTROLLER');
  946 + $varAction = C('VAR_ACTION');
  947 + $var[$varAction] = !empty($path)?array_pop($path):ACTION_NAME;
  948 + $var[$varController] = !empty($path)?array_pop($path):CONTROLLER_NAME;
  949 + if($maps = C('URL_ACTION_MAP')) {
  950 + if(isset($maps[strtolower($var[$varController])])) {
  951 + $maps = $maps[strtolower($var[$varController])];
  952 + if($action = array_search(strtolower($var[$varAction]),$maps)){
  953 + $var[$varAction] = $action;
  954 + }
  955 + }
  956 + }
  957 + if($maps = C('URL_CONTROLLER_MAP')) {
  958 + if($controller = array_search(strtolower($var[$varController]),$maps)){
  959 + $var[$varController] = $controller;
  960 + }
  961 + }
  962 + if($urlCase) {
  963 + $var[$varController] = parse_name($var[$varController]);
  964 + }
  965 + $module = '';
  966 +
  967 + if(!empty($path)) {
  968 + $var[$varModule] = implode($depr,$path);
  969 + }else{
  970 + if(C('MULTI_MODULE')) {
  971 + if(MODULE_NAME != C('DEFAULT_MODULE') || !C('MODULE_ALLOW_LIST')){
  972 + $var[$varModule]= MODULE_NAME;
  973 + }
  974 + }
  975 + }
  976 + if($maps = C('URL_MODULE_MAP')) {
  977 + if($_module = array_search(strtolower($var[$varModule]),$maps)){
  978 + $var[$varModule] = $_module;
  979 + }
  980 + }
  981 + if(isset($var[$varModule])){
  982 + $module = $var[$varModule];
  983 + unset($var[$varModule]);
  984 + }
  985 +
  986 + }
  987 + }
  988 +
  989 + if(C('URL_MODEL') == 0) { // 普通模式URL转换
  990 + $url = __APP__.'?'.C('VAR_MODULE')."={$module}&".http_build_query(array_reverse($var));
  991 + if($urlCase){
  992 + $url = strtolower($url);
  993 + }
  994 + if(!empty($vars)) {
  995 + $vars = http_build_query($vars);
  996 + $url .= '&'.$vars;
  997 + }
  998 + }else{ // PATHINFO模式或者兼容URL模式
  999 + if(isset($route)) {
  1000 + $url = __APP__.'/'.rtrim($url,$depr);
  1001 + }else{
  1002 + $module = (defined('BIND_MODULE') && BIND_MODULE==$module )? '' : $module;
  1003 + $url = __APP__.'/'.($module?$module.MODULE_PATHINFO_DEPR:'').implode($depr,array_reverse($var));
  1004 + }
  1005 + if($urlCase){
  1006 + $url = strtolower($url);
  1007 + }
  1008 + if(!empty($vars)) { // 添加参数
  1009 + foreach ($vars as $var => $val){
  1010 + if('' !== trim($val)) $url .= $depr . $var . $depr . urlencode($val);
  1011 + }
  1012 + }
  1013 + if($suffix) {
  1014 + $suffix = $suffix===true?C('URL_HTML_SUFFIX'):$suffix;
  1015 + if($pos = strpos($suffix, '|')){
  1016 + $suffix = substr($suffix, 0, $pos);
  1017 + }
  1018 + if($suffix && '/' != substr($url,-1)){
  1019 + $url .= '.'.ltrim($suffix,'.');
  1020 + }
  1021 + }
  1022 + }
  1023 + if(isset($anchor)){
  1024 + $url .= '#'.$anchor;
  1025 + }
  1026 + if($domain) {
  1027 + $url = (is_ssl()?'https://':'http://').$domain.$url;
  1028 + }
  1029 + return $url;
  1030 +}
  1031 +
  1032 +/**
  1033 + * 渲染输出Widget
  1034 + * @param string $name Widget名称
  1035 + * @param array $data 传入的参数
  1036 + * @return void
  1037 + */
  1038 +function W($name, $data=array()) {
  1039 + return R($name,$data,'Widget');
  1040 +}
  1041 +
  1042 +/**
  1043 + * 判断是否SSL协议
  1044 + * @return boolean
  1045 + */
  1046 +function is_ssl() {
  1047 + if(isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))){
  1048 + return true;
  1049 + }elseif(isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'] )) {
  1050 + return true;
  1051 + }
  1052 + return false;
  1053 +}
  1054 +
  1055 +/**
  1056 + * URL重定向
  1057 + * @param string $url 重定向的URL地址
  1058 + * @param integer $time 重定向的等待时间(秒)
  1059 + * @param string $msg 重定向前的提示信息
  1060 + * @return void
  1061 + */
  1062 +function redirect($url, $time=0, $msg='') {
  1063 + //多行URL地址支持
  1064 + $url = str_replace(array("\n", "\r"), '', $url);
  1065 + if (empty($msg))
  1066 + $msg = "系统将在{$time}秒之后自动跳转到{$url}!";
  1067 + if (!headers_sent()) {
  1068 + // redirect
  1069 + if (0 === $time) {
  1070 + header('Location: ' . $url);
  1071 + } else {
  1072 + header("refresh:{$time};url={$url}");
  1073 + echo($msg);
  1074 + }
  1075 + exit();
  1076 + } else {
  1077 + $str = "<meta http-equiv='Refresh' content='{$time};URL={$url}'>";
  1078 + if ($time != 0)
  1079 + $str .= $msg;
  1080 + exit($str);
  1081 + }
  1082 +}
  1083 +
  1084 +/**
  1085 + * 缓存管理
  1086 + * @param mixed $name 缓存名称,如果为数组表示进行缓存设置
  1087 + * @param mixed $value 缓存值
  1088 + * @param mixed $options 缓存参数
  1089 + * @return mixed
  1090 + */
  1091 +function S($name,$value='',$options=null) {
  1092 + static $cache = '';
  1093 + if(is_array($options)){
  1094 + // 缓存操作的同时初始化
  1095 + $type = isset($options['type'])?$options['type']:'';
  1096 + $cache = Think\Cache::getInstance($type,$options);
  1097 + }elseif(is_array($name)) { // 缓存初始化
  1098 + $type = isset($name['type'])?$name['type']:'';
  1099 + $cache = Think\Cache::getInstance($type,$name);
  1100 + return $cache;
  1101 + }elseif(empty($cache)) { // 自动初始化
  1102 + $cache = Think\Cache::getInstance();
  1103 + }
  1104 + if(''=== $value){ // 获取缓存
  1105 + return $cache->get($name);
  1106 + }elseif(is_null($value)) { // 删除缓存
  1107 + return $cache->rm($name);
  1108 + }else { // 缓存数据
  1109 + if(is_array($options)) {
  1110 + $expire = isset($options['expire'])?$options['expire']:NULL;
  1111 + }else{
  1112 + $expire = is_numeric($options)?$options:NULL;
  1113 + }
  1114 + return $cache->set($name, $value, $expire);
  1115 + }
  1116 +}
  1117 +
  1118 +/**
  1119 + * 快速文件数据读取和保存 针对简单类型数据 字符串、数组
  1120 + * @param string $name 缓存名称
  1121 + * @param mixed $value 缓存值
  1122 + * @param string $path 缓存路径
  1123 + * @return mixed
  1124 + */
  1125 +function F($name, $value='', $path=DATA_PATH) {
  1126 + static $_cache = array();
  1127 + $filename = $path . $name . '.php';
  1128 + if ('' !== $value) {
  1129 + if (is_null($value)) {
  1130 + // 删除缓存
  1131 + if(false !== strpos($name,'*')){
  1132 + return false; // TODO
  1133 + }else{
  1134 + unset($_cache[$name]);
  1135 + return Think\Storage::unlink($filename,'F');
  1136 + }
  1137 + } else {
  1138 + Think\Storage::put($filename,serialize($value),'F');
  1139 + // 缓存数据
  1140 + $_cache[$name] = $value;
  1141 + return null;
  1142 + }
  1143 + }
  1144 + // 获取缓存数据
  1145 + if (isset($_cache[$name]))
  1146 + return $_cache[$name];
  1147 + if (Think\Storage::has($filename,'F')){
  1148 + $value = unserialize(Think\Storage::read($filename,'F'));
  1149 + $_cache[$name] = $value;
  1150 + } else {
  1151 + $value = false;
  1152 + }
  1153 + return $value;
  1154 +}
  1155 +
  1156 +/**
  1157 + * 根据PHP各种类型变量生成唯一标识号
  1158 + * @param mixed $mix 变量
  1159 + * @return string
  1160 + */
  1161 +function to_guid_string($mix) {
  1162 + if (is_object($mix)) {
  1163 + return spl_object_hash($mix);
  1164 + } elseif (is_resource($mix)) {
  1165 + $mix = get_resource_type($mix) . strval($mix);
  1166 + } else {
  1167 + $mix = serialize($mix);
  1168 + }
  1169 + return md5($mix);
  1170 +}
  1171 +
  1172 +/**
  1173 + * XML编码
  1174 + * @param mixed $data 数据
  1175 + * @param string $root 根节点名
  1176 + * @param string $item 数字索引的子节点名
  1177 + * @param string $attr 根节点属性
  1178 + * @param string $id 数字索引子节点key转换的属性名
  1179 + * @param string $encoding 数据编码
  1180 + * @return string
  1181 + */
  1182 +function xml_encode($data, $root='think', $item='item', $attr='', $id='id', $encoding='utf-8') {
  1183 + if(is_array($attr)){
  1184 + $_attr = array();
  1185 + foreach ($attr as $key => $value) {
  1186 + $_attr[] = "{$key}=\"{$value}\"";
  1187 + }
  1188 + $attr = implode(' ', $_attr);
  1189 + }
  1190 + $attr = trim($attr);
  1191 + $attr = empty($attr) ? '' : " {$attr}";
  1192 + $xml = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
  1193 + $xml .= "<{$root}{$attr}>";
  1194 + $xml .= data_to_xml($data, $item, $id);
  1195 + $xml .= "</{$root}>";
  1196 + return $xml;
  1197 +}
  1198 +
  1199 +/**
  1200 + * 数据XML编码
  1201 + * @param mixed $data 数据
  1202 + * @param string $item 数字索引时的节点名称
  1203 + * @param string $id 数字索引key转换为的属性名
  1204 + * @return string
  1205 + */
  1206 +function data_to_xml($data, $item='item', $id='id') {
  1207 + $xml = $attr = '';
  1208 + foreach ($data as $key => $val) {
  1209 + if(is_numeric($key)){
  1210 + $id && $attr = " {$id}=\"{$key}\"";
  1211 + $key = $item;
  1212 + }
  1213 + $xml .= "<{$key}{$attr}>";
  1214 + $xml .= (is_array($val) || is_object($val)) ? data_to_xml($val, $item, $id) : $val;
  1215 + $xml .= "</{$key}>";
  1216 + }
  1217 + return $xml;
  1218 +}
  1219 +
  1220 +/**
  1221 + * session管理函数
  1222 + * @param string|array $name session名称 如果为数组则表示进行session设置
  1223 + * @param mixed $value session值
  1224 + * @return mixed
  1225 + */
  1226 +function session($name='',$value='') {
  1227 + $prefix = C('SESSION_PREFIX');
  1228 + if(is_array($name)) { // session初始化 在session_start 之前调用
  1229 + if(isset($name['prefix'])) C('SESSION_PREFIX',$name['prefix']);
  1230 + if(C('VAR_SESSION_ID') && isset($_REQUEST[C('VAR_SESSION_ID')])){
  1231 + session_id($_REQUEST[C('VAR_SESSION_ID')]);
  1232 + }elseif(isset($name['id'])) {
  1233 + session_id($name['id']);
  1234 + }
  1235 + if('common' == APP_MODE){ // 其它模式可能不支持
  1236 + ini_set('session.auto_start', 0);
  1237 + }
  1238 + if(isset($name['name'])) session_name($name['name']);
  1239 + if(isset($name['path'])) session_save_path($name['path']);
  1240 + if(isset($name['domain'])) ini_set('session.cookie_domain', $name['domain']);
  1241 + if(isset($name['expire'])) {
  1242 + ini_set('session.gc_maxlifetime', $name['expire']);
  1243 + ini_set('session.cookie_lifetime', $name['expire']);
  1244 + }
  1245 + if(isset($name['use_trans_sid'])) ini_set('session.use_trans_sid', $name['use_trans_sid']?1:0);
  1246 + if(isset($name['use_cookies'])) ini_set('session.use_cookies', $name['use_cookies']?1:0);
  1247 + if(isset($name['cache_limiter'])) session_cache_limiter($name['cache_limiter']);
  1248 + if(isset($name['cache_expire'])) session_cache_expire($name['cache_expire']);
  1249 + if(isset($name['type'])) C('SESSION_TYPE',$name['type']);
  1250 + if(C('SESSION_TYPE')) { // 读取session驱动
  1251 + $type = C('SESSION_TYPE');
  1252 + $class = strpos($type,'\\')? $type : 'Think\\Session\\Driver\\'. ucwords(strtolower($type));
  1253 + $hander = new $class();
  1254 + session_set_save_handler(
  1255 + array(&$hander,"open"),
  1256 + array(&$hander,"close"),
  1257 + array(&$hander,"read"),
  1258 + array(&$hander,"write"),
  1259 + array(&$hander,"destroy"),
  1260 + array(&$hander,"gc"));
  1261 + }
  1262 + // 启动session
  1263 + if(C('SESSION_AUTO_START')) session_start();
  1264 + }elseif('' === $value){
  1265 + if(''===$name){
  1266 + // 获取全部的session
  1267 + return $prefix ? $_SESSION[$prefix] : $_SESSION;
  1268 + }elseif(0===strpos($name,'[')) { // session 操作
  1269 + if('[pause]'==$name){ // 暂停session
  1270 + session_write_close();
  1271 + }elseif('[start]'==$name){ // 启动session
  1272 + session_start();
  1273 + }elseif('[destroy]'==$name){ // 销毁session
  1274 + $_SESSION = array();
  1275 + session_unset();
  1276 + session_destroy();
  1277 + }elseif('[regenerate]'==$name){ // 重新生成id
  1278 + session_regenerate_id();
  1279 + }
  1280 + }elseif(0===strpos($name,'?')){ // 检查session
  1281 + $name = substr($name,1);
  1282 + if(strpos($name,'.')){ // 支持数组
  1283 + list($name1,$name2) = explode('.',$name);
  1284 + return $prefix?isset($_SESSION[$prefix][$name1][$name2]):isset($_SESSION[$name1][$name2]);
  1285 + }else{
  1286 + return $prefix?isset($_SESSION[$prefix][$name]):isset($_SESSION[$name]);
  1287 + }
  1288 + }elseif(is_null($name)){ // 清空session
  1289 + if($prefix) {
  1290 + unset($_SESSION[$prefix]);
  1291 + }else{
  1292 + $_SESSION = array();
  1293 + }
  1294 + }elseif($prefix){ // 获取session
  1295 + if(strpos($name,'.')){
  1296 + list($name1,$name2) = explode('.',$name);
  1297 + return isset($_SESSION[$prefix][$name1][$name2])?$_SESSION[$prefix][$name1][$name2]:null;
  1298 + }else{
  1299 + return isset($_SESSION[$prefix][$name])?$_SESSION[$prefix][$name]:null;
  1300 + }
  1301 + }else{
  1302 + if(strpos($name,'.')){
  1303 + list($name1,$name2) = explode('.',$name);
  1304 + return isset($_SESSION[$name1][$name2])?$_SESSION[$name1][$name2]:null;
  1305 + }else{
  1306 + return isset($_SESSION[$name])?$_SESSION[$name]:null;
  1307 + }
  1308 + }
  1309 + }elseif(is_null($value)){ // 删除session
  1310 + if(strpos($name,'.')){
  1311 + list($name1,$name2) = explode('.',$name);
  1312 + if($prefix){
  1313 + unset($_SESSION[$prefix][$name1][$name2]);
  1314 + }else{
  1315 + unset($_SESSION[$name1][$name2]);
  1316 + }
  1317 + }else{
  1318 + if($prefix){
  1319 + unset($_SESSION[$prefix][$name]);
  1320 + }else{
  1321 + unset($_SESSION[$name]);
  1322 + }
  1323 + }
  1324 + }else{ // 设置session
  1325 + if(strpos($name,'.')){
  1326 + list($name1,$name2) = explode('.',$name);
  1327 + if($prefix){
  1328 + $_SESSION[$prefix][$name1][$name2] = $value;
  1329 + }else{
  1330 + $_SESSION[$name1][$name2] = $value;
  1331 + }
  1332 + }else{
  1333 + if($prefix){
  1334 + $_SESSION[$prefix][$name] = $value;
  1335 + }else{
  1336 + $_SESSION[$name] = $value;
  1337 + }
  1338 + }
  1339 + }
  1340 + return null;
  1341 +}
  1342 +
  1343 +/**
  1344 + * Cookie 设置、获取、删除
  1345 + * @param string $name cookie名称
  1346 + * @param mixed $value cookie值
  1347 + * @param mixed $option cookie参数
  1348 + * @return mixed
  1349 + */
  1350 +function cookie($name='', $value='', $option=null) {
  1351 + // 默认设置
  1352 + $config = array(
  1353 + 'prefix' => C('COOKIE_PREFIX'), // cookie 名称前缀
  1354 + 'expire' => C('COOKIE_EXPIRE'), // cookie 保存时间
  1355 + 'path' => C('COOKIE_PATH'), // cookie 保存路径
  1356 + 'domain' => C('COOKIE_DOMAIN'), // cookie 有效域名
  1357 + 'secure' => C('COOKIE_SECURE'), // cookie 启用安全传输
  1358 + 'httponly' => C('COOKIE_HTTPONLY'), // httponly设置
  1359 + );
  1360 + // 参数设置(会覆盖黙认设置)
  1361 + if (!is_null($option)) {
  1362 + if (is_numeric($option))
  1363 + $option = array('expire' => $option);
  1364 + elseif (is_string($option))
  1365 + parse_str($option, $option);
  1366 + $config = array_merge($config, array_change_key_case($option));
  1367 + }
  1368 + if(!empty($config['httponly'])){
  1369 + ini_set("session.cookie_httponly", 1);
  1370 + }
  1371 + // 清除指定前缀的所有cookie
  1372 + if (is_null($name)) {
  1373 + if (empty($_COOKIE))
  1374 + return null;
  1375 + // 要删除的cookie前缀,不指定则删除config设置的指定前缀
  1376 + $prefix = empty($value) ? $config['prefix'] : $value;
  1377 + if (!empty($prefix)) {// 如果前缀为空字符串将不作处理直接返回
  1378 + foreach ($_COOKIE as $key => $val) {
  1379 + if (0 === stripos($key, $prefix)) {
  1380 + setcookie($key, '', time() - 3600, $config['path'], $config['domain'],$config['secure'],$config['httponly']);
  1381 + unset($_COOKIE[$key]);
  1382 + }
  1383 + }
  1384 + }
  1385 + return null;
  1386 + }elseif('' === $name){
  1387 + // 获取全部的cookie
  1388 + return $_COOKIE;
  1389 + }
  1390 + $name = $config['prefix'] . str_replace('.', '_', $name);
  1391 + if ('' === $value) {
  1392 + if(isset($_COOKIE[$name])){
  1393 + $value = $_COOKIE[$name];
  1394 + if(0===strpos($value,'think:')){
  1395 + $value = substr($value,6);
  1396 + return array_map('urldecode',json_decode(MAGIC_QUOTES_GPC?stripslashes($value):$value,true));
  1397 + }else{
  1398 + return $value;
  1399 + }
  1400 + }else{
  1401 + return null;
  1402 + }
  1403 + } else {
  1404 + if (is_null($value)) {
  1405 + setcookie($name, '', time() - 3600, $config['path'], $config['domain'],$config['secure'],$config['httponly']);
  1406 + unset($_COOKIE[$name]); // 删除指定cookie
  1407 + } else {
  1408 + // 设置cookie
  1409 + if(is_array($value)){
  1410 + $value = 'think:'.json_encode(array_map('urlencode',$value));
  1411 + }
  1412 + $expire = !empty($config['expire']) ? time() + intval($config['expire']) : 0;
  1413 + setcookie($name, $value, $expire, $config['path'], $config['domain'],$config['secure'],$config['httponly']);
  1414 + $_COOKIE[$name] = $value;
  1415 + }
  1416 + }
  1417 + return null;
  1418 +}
  1419 +
  1420 +/**
  1421 + * 加载动态扩展文件
  1422 + * @var string $path 文件路径
  1423 + * @return void
  1424 + */
  1425 +function load_ext_file($path) {
  1426 + // 加载自定义外部文件
  1427 + if($files = C('LOAD_EXT_FILE')) {
  1428 + $files = explode(',',$files);
  1429 + foreach ($files as $file){
  1430 + $file = $path.'Common/'.$file.'.php';
  1431 + if(is_file($file)) include $file;
  1432 + }
  1433 + }
  1434 + // 加载自定义的动态配置文件
  1435 + if($configs = C('LOAD_EXT_CONFIG')) {
  1436 + if(is_string($configs)) $configs = explode(',',$configs);
  1437 + foreach ($configs as $key=>$config){
  1438 + $file = is_file($config)? $config : $path.'Conf/'.$config.CONF_EXT;
  1439 + if(is_file($file)) {
  1440 + is_numeric($key)?C(load_config($file)):C($key,load_config($file));
  1441 + }
  1442 + }
  1443 + }
  1444 +}
  1445 +
  1446 +/**
  1447 + * 获取客户端IP地址
  1448 + * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
  1449 + * @param boolean $adv 是否进行高级模式获取(有可能被伪装)
  1450 + * @return mixed
  1451 + */
  1452 +function get_client_ip($type = 0,$adv=false) {
  1453 + $type = $type ? 1 : 0;
  1454 + static $ip = NULL;
  1455 + if ($ip !== NULL) return $ip[$type];
  1456 + if($adv){
  1457 + if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  1458 + $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
  1459 + $pos = array_search('unknown',$arr);
  1460 + if(false !== $pos) unset($arr[$pos]);
  1461 + $ip = trim($arr[0]);
  1462 + }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
  1463 + $ip = $_SERVER['HTTP_CLIENT_IP'];
  1464 + }elseif (isset($_SERVER['REMOTE_ADDR'])) {
  1465 + $ip = $_SERVER['REMOTE_ADDR'];
  1466 + }
  1467 + }elseif (isset($_SERVER['REMOTE_ADDR'])) {
  1468 + $ip = $_SERVER['REMOTE_ADDR'];
  1469 + }
  1470 + // IP地址合法验证
  1471 + $long = sprintf("%u",ip2long($ip));
  1472 + $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
  1473 + return $ip[$type];
  1474 +}
  1475 +
  1476 +/**
  1477 + * 发送HTTP状态
  1478 + * @param integer $code 状态码
  1479 + * @return void
  1480 + */
  1481 +function send_http_status($code) {
  1482 + static $_status = array(
  1483 + // Informational 1xx
  1484 + 100 => 'Continue',
  1485 + 101 => 'Switching Protocols',
  1486 + // Success 2xx
  1487 + 200 => 'OK',
  1488 + 201 => 'Created',
  1489 + 202 => 'Accepted',
  1490 + 203 => 'Non-Authoritative Information',
  1491 + 204 => 'No Content',
  1492 + 205 => 'Reset Content',
  1493 + 206 => 'Partial Content',
  1494 + // Redirection 3xx
  1495 + 300 => 'Multiple Choices',
  1496 + 301 => 'Moved Permanently',
  1497 + 302 => 'Moved Temporarily ', // 1.1
  1498 + 303 => 'See Other',
  1499 + 304 => 'Not Modified',
  1500 + 305 => 'Use Proxy',
  1501 + // 306 is deprecated but reserved
  1502 + 307 => 'Temporary Redirect',
  1503 + // Client Error 4xx
  1504 + 400 => 'Bad Request',
  1505 + 401 => 'Unauthorized',
  1506 + 402 => 'Payment Required',
  1507 + 403 => 'Forbidden',
  1508 + 404 => 'Not Found',
  1509 + 405 => 'Method Not Allowed',
  1510 + 406 => 'Not Acceptable',
  1511 + 407 => 'Proxy Authentication Required',
  1512 + 408 => 'Request Timeout',
  1513 + 409 => 'Conflict',
  1514 + 410 => 'Gone',
  1515 + 411 => 'Length Required',
  1516 + 412 => 'Precondition Failed',
  1517 + 413 => 'Request Entity Too Large',
  1518 + 414 => 'Request-URI Too Long',
  1519 + 415 => 'Unsupported Media Type',
  1520 + 416 => 'Requested Range Not Satisfiable',
  1521 + 417 => 'Expectation Failed',
  1522 + // Server Error 5xx
  1523 + 500 => 'Internal Server Error',
  1524 + 501 => 'Not Implemented',
  1525 + 502 => 'Bad Gateway',
  1526 + 503 => 'Service Unavailable',
  1527 + 504 => 'Gateway Timeout',
  1528 + 505 => 'HTTP Version Not Supported',
  1529 + 509 => 'Bandwidth Limit Exceeded'
  1530 + );
  1531 + if(isset($_status[$code])) {
  1532 + header('HTTP/1.1 '.$code.' '.$_status[$code]);
  1533 + // 确保FastCGI模式下正常
  1534 + header('Status:'.$code.' '.$_status[$code]);
  1535 + }
  1536 +}
  1537 +
  1538 +function think_filter(&$value){
  1539 + // TODO 其他安全过滤
  1540 +
  1541 + // 过滤查询特殊字符
  1542 + if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){
  1543 + $value .= ' ';
  1544 + }
  1545 +}
  1546 +
  1547 +// 不区分大小写的in_array实现
  1548 +function in_array_case($value,$array){
  1549 + return in_array(strtolower($value),array_map('strtolower',$array));
  1550 +}
... ...
trunk/ThinkPHP/Mode/cli.php 0 → 100644
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +/**
  13 + * ThinkPHP CLI模式定义
  14 + */
  15 +return array(
  16 + // 配置文件
  17 + 'config' => array(
  18 + THINK_PATH.'Conf/convention.php', // 系统惯例配置
  19 + CONF_PATH.'config'.CONF_EXT, // 应用公共配置
  20 + ),
  21 +
  22 + // 别名定义
  23 + 'alias' => array(
  24 + 'Think\Exception' => CORE_PATH . 'Exception'.EXT,
  25 + 'Think\Model' => CORE_PATH . 'Model'.EXT,
  26 + 'Think\Db' => CORE_PATH . 'Db'.EXT,
  27 + 'Think\Cache' => CORE_PATH . 'Cache'.EXT,
  28 + 'Think\Cache\Driver\File' => CORE_PATH . 'Cache/Driver/File'.EXT,
  29 + 'Think\Storage' => CORE_PATH . 'Storage'.EXT,
  30 + ),
  31 +
  32 + // 函数和类文件
  33 + 'core' => array(
  34 + MODE_PATH.'Cli/functions.php',
  35 + COMMON_PATH.'Common/function.php',
  36 + MODE_PATH . 'Cli/App'.EXT,
  37 + MODE_PATH . 'Cli/Dispatcher'.EXT,
  38 + MODE_PATH . 'Cli/Controller'.EXT,
  39 + CORE_PATH . 'Behavior'.EXT,
  40 + ),
  41 + // 行为扩展定义
  42 + 'tags' => array(
  43 + ),
  44 +);
... ...
trunk/swoole.php 0 → 100644
  1 +#!/bin/env php
  2 +<?php
  3 +/**
  4 + * 默认时区定义
  5 + */
  6 +date_default_timezone_set('Asia/Shanghai');
  7 +
  8 +/**
  9 + * 设置错误报告模式
  10 + */
  11 +error_reporting(0);
  12 +
  13 +/**
  14 + * 设置默认区域
  15 + */
  16 +setlocale(LC_ALL, "zh_CN.utf-8");
  17 +
  18 +/**
  19 + * 检测 PDO_MYSQL
  20 + */
  21 +if (!extension_loaded('pdo_mysql')) {
  22 + exit('PDO_MYSQL extension is not installed' . PHP_EOL);
  23 +}
  24 +/**
  25 + * 检查exec 函数是否启用
  26 + */
  27 +if (!function_exists('exec')) {
  28 + exit('exec function is disabled' . PHP_EOL);
  29 +}
  30 +/**
  31 + * 检查命令 lsof 命令是否存在
  32 + */
  33 +exec("whereis lsof", $out);
  34 +if ($out[0] == 'lsof:') {
  35 + exit('lsof is not found' . PHP_EOL);
  36 +}
  37 +/**
  38 + * 定义项目根目录&swoole-task pid
  39 + */
  40 +define('SWOOLE_PATH', __DIR__);
  41 +define('SWOOLE_TASK_PID_PATH', SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . 'swoole-task.pid');
  42 +define('SWOOLE_TASK_NAME_PRE', 'swooleServ');
  43 +
  44 +/**
  45 + * 加载 swoole server
  46 + */
  47 +include SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR . 'SwooleServer.php';
  48 +
  49 +function portBind($port)
  50 +{
  51 + $ret = [];
  52 + $cmd = "lsof -i :{$port}|awk '$1 != \"COMMAND\" {print $1, $2, $9}'";
  53 + exec($cmd, $out);
  54 + if ($out) {
  55 + foreach ($out as $v) {
  56 + $a = explode(' ', $v);
  57 + list($ip, $p) = explode(':', $a[2]);
  58 + $ret[$a[1]] = [
  59 + 'cmd' => $a[0],
  60 + 'ip' => $ip,
  61 + 'port' => $p,
  62 + ];
  63 + }
  64 + }
  65 +
  66 + return $ret;
  67 +}
  68 +
  69 +function servStart($host, $port, $daemon, $name)
  70 +{
  71 + echo "正在启动 swoole-task 服务" . PHP_EOL;
  72 + if (!is_writable(dirname(SWOOLE_TASK_PID_PATH))) {
  73 + exit("swoole-task-pid文件需要目录的写入权限:" . dirname(SWOOLE_TASK_PID_PATH) . PHP_EOL);
  74 + }
  75 + if (file_exists(SWOOLE_TASK_PID_PATH)) {
  76 + $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
  77 + $cmd = "ps ax | awk '{ print $1 }' | grep -e \"^{$pid[0]}$\"";
  78 + exec($cmd, $out);
  79 + if (!empty($out)) {
  80 + exit("swoole-task pid文件 " . SWOOLE_TASK_PID_PATH . " 存在,swoole-task 服务器已经启动,进程pid为:{$pid[0]}" . PHP_EOL);
  81 + } else {
  82 + echo "警告:swoole-task pid文件 " . SWOOLE_TASK_PID_PATH . " 存在,可能swoole-task服务上次异常退出(非守护模式ctrl+c终止造成是最大可能)" . PHP_EOL;
  83 + unlink(SWOOLE_TASK_PID_PATH);
  84 + }
  85 + }
  86 + $bind = portBind($port);
  87 + if ($bind) {
  88 + foreach ($bind as $k => $v) {
  89 + if ($v['ip'] == '*' || $v['ip'] == $host) {
  90 + exit("端口已经被占用 {$host}:$port, 占用端口进程ID {$k}" . PHP_EOL);
  91 + }
  92 + }
  93 + }
  94 + unset($_SERVER['argv']);
  95 + $_SERVER['argc'] = 0;
  96 + echo "启动 swoole-task 服务成功" . PHP_EOL;
  97 + $server = new SwooleServer('127.0.0.1', 9501);
  98 + $server->run();
  99 + //确保服务器启动后swoole-task-pid文件必须生成
  100 + /*if (!empty(portBind($port)) && !file_exists(SWOOLE_TASK_PID_PATH)) {
  101 + exit("swoole-task pid文件生成失败( " . SWOOLE_TASK_PID_PATH . ") ,请手动关闭当前启动的swoole-task服务检查原因" . PHP_EOL);
  102 + }*/
  103 +}
  104 +
  105 +function servStop($host, $port, $isRestart = false)
  106 +{
  107 + echo "正在停止 swoole-task 服务" . PHP_EOL;
  108 + if (!file_exists(SWOOLE_TASK_PID_PATH)) {
  109 + exit('swoole-task-pid文件:' . SWOOLE_TASK_PID_PATH . '不存在' . PHP_EOL);
  110 + }
  111 + $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
  112 + $bind = portBind($port);
  113 + if (empty($bind) || !isset($bind[$pid[0]])) {
  114 + exit("指定端口占用进程不存在 port:{$port}, pid:{$pid[0]}" . PHP_EOL);
  115 + }
  116 + $cmd = "kill {$pid[0]}";
  117 + exec($cmd);
  118 + do {
  119 + $out = [];
  120 + $c = "ps ax | awk '{ print $1 }' | grep -e \"^{$pid[0]}$\"";
  121 + exec($c, $out);
  122 + if (empty($out)) {
  123 + break;
  124 + }
  125 + } while (true);
  126 + //确保停止服务后swoole-task-pid文件被删除
  127 + if (file_exists(SWOOLE_TASK_PID_PATH)) {
  128 + unlink(SWOOLE_TASK_PID_PATH);
  129 + }
  130 + $msg = "执行命令 {$cmd} 成功,端口 {$host}:{$port} 进程结束" . PHP_EOL;
  131 + if ($isRestart) {
  132 + echo $msg;
  133 + } else {
  134 + exit($msg);
  135 + }
  136 +}
  137 +
  138 +function servReload($host, $port, $isRestart = false)
  139 +{
  140 + echo "正在平滑重启 swoole-task 服务" . PHP_EOL;
  141 + try {
  142 + $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
  143 + $ret = $client->connect($host, $port);
  144 + if (empty($ret)) {
  145 + exit("{$host}:{$port} swoole-task服务不存在或者已经关闭" . PHP_EOL);
  146 + } else {
  147 + $client->send(json_encode(array('action' => 'reload')));
  148 + }
  149 + $msg = "执行命令reload成功,端口 {$host}:{$port} 进程重启" . PHP_EOL;
  150 + if ($isRestart) {
  151 + echo $msg;
  152 + } else {
  153 + exit($msg);
  154 + }
  155 + } catch (Exception $e) {
  156 + exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
  157 + }
  158 +}
  159 +
  160 +function servClose($host, $port, $isRestart = false)
  161 +{
  162 + echo "正在关闭 swoole-task 服务" . PHP_EOL;
  163 + try {
  164 + $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
  165 + $ret = $client->connect($host, $port);
  166 + if (empty($ret)) {
  167 + exit("{$host}:{$port} swoole-task服务不存在或者已经关闭" . PHP_EOL);
  168 + } else {
  169 + $client->send(json_encode(array('action' => 'close')));
  170 + }
  171 + //确保停止服务后swoole-task-pid文件被删除
  172 + if (file_exists(SWOOLE_TASK_PID_PATH)) {
  173 + unlink(SWOOLE_TASK_PID_PATH);
  174 + }
  175 + $msg = "执行命令close成功,端口 {$host}:{$port} 进程结束" . PHP_EOL;
  176 + if ($isRestart) {
  177 + echo $msg;
  178 + } else {
  179 + exit($msg);
  180 + }
  181 + } catch (\Exception $e) {
  182 + exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
  183 + }
  184 +}
  185 +
  186 +function servStatus($host, $port)
  187 +{
  188 + echo "swoole-task {$host}:{$port} 运行状态" . PHP_EOL;
  189 + $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
  190 + $bind = portBind($port);
  191 + if (empty($bind) || !isset($bind[$pid[0]])) {
  192 + exit("指定端口占用进程不存在 port:{$port}, pid:{$pid[0]}" . PHP_EOL);
  193 + }
  194 + $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
  195 + $ret = $client->connect($host, $port);
  196 + if (empty($ret)) {
  197 + exit("{$host}:{$port} swoole-task服务不存在或者已经停止" . PHP_EOL);
  198 + } else {
  199 + $client->send(json_encode(array('action' => 'status')));
  200 + $out = $client->recv();
  201 + $a = json_decode($out);
  202 + $b = array(
  203 + 'start_time' => '服务器启动的时间',
  204 + 'connection_num' => '当前连接的数量',
  205 + 'accept_count' => '接受的连接数量',
  206 + 'close_count' => '关闭的连接数量',
  207 + 'tasking_num' => '当前正在排队的任务数',
  208 + 'request_count' => '请求的连接数量',
  209 + 'worker_request_count' => 'worker连接数量',
  210 + 'task_process_num' => '任务进程数量'
  211 + );
  212 + foreach ($a as $k1 => $v1) {
  213 + if ($k1 == 'start_time') {
  214 + $v1 = date("Y-m-d H:i:s", $v1);
  215 + }
  216 + echo $b[$k1] . ":\t$v1" . PHP_EOL;
  217 + }
  218 + }
  219 + exit();
  220 +}
  221 +
  222 +function servList()
  223 +{
  224 + echo "本机运行的swoole-task服务进程" . PHP_EOL;
  225 + $cmd = "ps aux|grep " . SWOOLE_TASK_NAME_PRE . "|grep -v grep|awk '{print $1, $2, $6, $8, $9, $11}'";
  226 + exec($cmd, $out);
  227 + if (empty($out)) {
  228 + exit("没有发现正在运行的swoole-task服务" . PHP_EOL);
  229 + }
  230 + echo "USER PID RSS(kb) STAT START COMMAND" . PHP_EOL;
  231 + foreach ($out as $v) {
  232 + echo $v . PHP_EOL;
  233 + }
  234 + exit();
  235 +}
  236 +
  237 +//可执行命令
  238 +$cmds = [
  239 + 'start',
  240 + 'stop',
  241 + 'restart',
  242 + 'reload',
  243 + 'close',
  244 + 'status',
  245 + 'list',
  246 +];
  247 +$shortopts = "dDh:p:n:";
  248 +$longopts = [
  249 + 'help',
  250 + 'daemon',
  251 + 'nondaemon',
  252 + 'host:',
  253 + 'port:',
  254 + 'name:',
  255 +];
  256 +$opts = getopt($shortopts, $longopts);
  257 +
  258 +if (isset($opts['help']) || $argc < 2) {
  259 + echo <<<HELP
  260 +用法:php swoole.php 选项 ... 命令[start|stop|restart|reload|close|status|list]
  261 +管理swoole-task服务,确保系统 lsof 命令有效
  262 +如果不指定监听host或者port,使用配置参数
  263 +
  264 +参数说明
  265 + --help 显示本帮助说明
  266 + -d, --daemon 指定此参数,以守护进程模式运行,不指定则读取配置文件值
  267 + -D, --nondaemon 指定此参数,以非守护进程模式运行,不指定则读取配置文件值
  268 + -h, --host 指定监听ip,例如 php swoole.php -h127.0.0.1
  269 + -p, --port 指定监听端口port, 例如 php swoole.php -h127.0.0.1 -p9520
  270 + -n, --name 指定服务进程名称,例如 php swoole.php -ntest start, 则进程名称为SWOOLE_TASK_NAME_PRE-name
  271 +启动swoole-task 如果不指定 host和port,读取默认配置
  272 +强制关闭swoole-task 必须指定port,没有指定host,关闭的监听端口是 *:port,指定了host,关闭 host:port端口
  273 +平滑关闭swoole-task 必须指定port,没有指定host,关闭的监听端口是 *:port,指定了host,关闭 host:port端口
  274 +强制重启swoole-task 必须指定端口
  275 +平滑重启swoole-task 必须指定端口
  276 +获取swoole-task 状态,必须指定port(不指定host默认127.0.0.1), tasking_num是正在处理的任务数量(0表示没有待处理任务)
  277 +
  278 +HELP;
  279 + exit;
  280 +}
  281 +//参数检查
  282 +foreach ($opts as $k => $v) {
  283 + if (($k == 'h' || $k == 'host')) {
  284 + if (empty($v)) {
  285 + exit("参数 -h --host 必须指定值\n");
  286 + }
  287 + }
  288 + if (($k == 'p' || $k == 'port')) {
  289 + if (empty($v)) {
  290 + exit("参数 -p --port 必须指定值\n");
  291 + }
  292 + }
  293 + if (($k == 'n' || $k == 'name')) {
  294 + if (empty($v)) {
  295 + exit("参数 -n --name 必须指定值\n");
  296 + }
  297 + }
  298 +}
  299 +
  300 +//命令检查
  301 +$cmd = $argv[$argc - 1];
  302 +if (!in_array($cmd, $cmds)) {
  303 + exit("输入命令有误 : {$cmd}, 请查看帮助文档\n");
  304 +}
  305 +
  306 +//监听ip 127.0.0.1,空读取配置文件
  307 +$host = '127.0.0.1';
  308 +if (!empty($opts['h'])) {
  309 + $host = $opts['h'];
  310 + if (!filter_var($host, FILTER_VALIDATE_IP)) {
  311 + exit("输入host有误:{$host}");
  312 + }
  313 +}
  314 +if (!empty($opts['host'])) {
  315 + $host = $opts['host'];
  316 + if (!filter_var($host, FILTER_VALIDATE_IP)) {
  317 + exit("输入host有误:{$host}");
  318 + }
  319 +}
  320 +//监听端口,9501 读取配置文件
  321 +$port = 9501;
  322 +if (!empty($opts['p'])) {
  323 + $port = (int)$opts['p'];
  324 + if ($port <= 0) {
  325 + exit("输入port有误:{$port}");
  326 + }
  327 +}
  328 +if (!empty($opts['port'])) {
  329 + $port = (int)$opts['port'];
  330 + if ($port <= 0) {
  331 + exit("输入port有误:{$port}");
  332 + }
  333 +}
  334 +//进程名称 没有默认为 SWOOLE_TASK_NAME_PRE;
  335 +$name = SWOOLE_TASK_NAME_PRE;
  336 +if (!empty($opts['n'])) {
  337 + $name = $opts['n'];
  338 +}
  339 +if (!empty($opts['name'])) {
  340 + $name = $opts['n'];
  341 +}
  342 +//是否守护进程 -1 读取配置文件
  343 +$isdaemon = -1;
  344 +if (isset($opts['D']) || isset($opts['nondaemon'])) {
  345 + $isdaemon = 0;
  346 +}
  347 +if (isset($opts['d']) || isset($opts['daemon'])) {
  348 + $isdaemon = 1;
  349 +}
  350 +//启动swoole-task服务
  351 +if ($cmd == 'start') {
  352 + servStart($host, $port, $isdaemon, $name);
  353 +}
  354 +//强制停止swoole-task服务
  355 +if ($cmd == 'stop') {
  356 + if (empty($port)) {
  357 + exit("停止swoole-task服务必须指定port" . PHP_EOL);
  358 + }
  359 + servStop($host, $port);
  360 +}
  361 +//关闭swoole-task服务
  362 +if ($cmd == 'close') {
  363 + if (empty($port)) {
  364 + exit("停止swoole-task服务必须指定port" . PHP_EOL);
  365 + }
  366 + servClose($host, $port);
  367 +}
  368 +//强制重启swoole-task服务
  369 +if ($cmd == 'restart') {
  370 + if (empty($port)) {
  371 + exit("重启swoole-task服务必须指定port" . PHP_EOL);
  372 + }
  373 + echo "重启swoole-task服务" . PHP_EOL;
  374 + servStop($host, $port, true);
  375 + servStart($host, $port, $isdaemon, $name);
  376 +}
  377 +//平滑重启swoole-task服务
  378 +if ($cmd == 'reload') {
  379 + if (empty($port)) {
  380 + exit("平滑重启swoole-task服务必须指定port" . PHP_EOL);
  381 + }
  382 + echo "平滑重启swoole-task服务" . PHP_EOL;
  383 + servReload($host, $port, true);
  384 +}
  385 +//查看swoole-task服务状态
  386 +if ($cmd == 'status') {
  387 + if (empty($host)) {
  388 + $host = '127.0.0.1';
  389 + }
  390 + if (empty($port)) {
  391 + exit("查看swoole-task服务必须指定port(host不指定默认使用127.0.0.1)" . PHP_EOL);
  392 + }
  393 + servStatus($host, $port);
  394 +}
  395 +//查看swoole-task服务进程列表
  396 +if ($cmd == 'list') {
  397 + servList();
  398 +}
  399 +
  400 +
... ...