/** * 使用的缓存配置 默认为使用default_cache配置的参数 * * @param bool|array $conf * * @throws PhpExtendNotInstall */ public function __construct($conf = false) { if (!function_exists('apc_cache_info')) { throw new PhpExtendNotInstall(Lang::get('_CACHE_EXTENT_NOT_INSTALL_', 'Apc')); } $this->conf = $conf ? $conf : Config::get('default_cache'); }
/** * 自定义异常处理 * * @param mixed $e 异常对象 */ public function appException(&$e) { $error = []; $exceptionClass = new \ReflectionClass($e); $error['exception'] = '\\' . $exceptionClass->name; $error['message'] = $e->getMessage(); $trace = $e->getTrace(); foreach ($trace as $key => $val) { $error['files'][$key] = $val; } if (substr($e->getFile(), -20) !== '\\Tools\\functions.php' || $e->getLine() !== 90) { array_unshift($error['files'], ['file' => $e->getFile(), 'line' => $e->getLine(), 'type' => 'throw']); } if (!Cml::$debug) { //正式环境 只显示‘系统错误’并将错误信息记录到日志 Log::emergency($error['message'], [$error['files'][0]]); $error = []; $error['message'] = Lang::get('_CML_ERROR_'); } if (Request::isCli()) { pd($error); } else { header('HTTP/1.1 500 Internal Server Error'); View::getEngine('html')->reset()->assign('error', $error); Cml::showSystemTemplate(Config::get('html_exception')); } }
public function __construct($conf = false) { if (!function_exists('apc_cache_info')) { \Cml\throwException(Lang::get('_CACHE_EXTENT_NOT_INSTALL_', 'Apc')); } $this->conf = $conf ? $conf : Config::get('CACHE'); }
/** * 自定义异常处理 * * @param mixed $e 异常对象 */ public function appException(&$e) { if (Cml::$debug) { $run = new Run(); $run->pushHandler(Request::isCli() ? new PlainTextHandler() : new PrettyPageHandler()); $run->handleException($e); } else { $error = []; $error['message'] = $e->getMessage(); $trace = $e->getTrace(); $error['files'][0] = $trace[0]; if (substr($e->getFile(), -20) !== '\\Tools\\functions.php' || $e->getLine() !== 90) { array_unshift($error['files'], ['file' => $e->getFile(), 'line' => $e->getLine(), 'type' => 'throw']); } //正式环境 只显示‘系统错误’并将错误信息记录到日志 Log::emergency($error['message'], [$error['files'][0]]); $error = []; $error['message'] = Lang::get('_CML_ERROR_'); if (Request::isCli()) { \Cml\pd($error); } else { header('HTTP/1.1 500 Internal Server Error'); View::getEngine('html')->reset()->assign('error', $error); Cml::showSystemTemplate(Config::get('html_exception')); } } exit; }
/** * URL组装 支持不同URL模式 * eg: \Cml\Http\Response::url('Home/Blog/cate/id/1') * * @param string $url URL表达式 路径/控制器/操作/参数1/参数1值/..... * @param int $echo 是否输出 1输出 0 return * * @return string */ public static function url($url = '', $echo = 1) { $return = ''; // 解析URL empty($url) && \Cml\throwException(Lang::get('_CML_ERROR_')); //'U方法参数出错' // URL组装 $delimiter = Config::get('url_pathinfo_depr'); $url = ltrim($url, '/'); $url = implode($delimiter, explode('/', $url)); if (Config::get('url_model') == 1) { $return = $_SERVER['SCRIPT_NAME'] . '/' . $url; } elseif (Config::get('url_model') == 2) { $return = Route::$urlParams['root'] . $url; } elseif (Config::get('url_model') == 3) { $return = $_SERVER['SCRIPT_NAME'] . '?' . Config::get('var_pathinfo') . '=/' . $url; } $return .= Config::get('url_model') == 2 ? Config::get('url_html_suffix') : ''; $return = Secure::filterScript($return); if ($echo === 1) { echo $return; } else { return $return; } return ''; }
/** * @param bool $conf */ public function __construct($conf = false) { $this->conf = $conf ? $conf : Config::get('CACHE'); if (extension_loaded('Memcached')) { $this->memcache = new \Memcached(); } elseif (extension_loaded('Memcache')) { $this->memcache = new \Memcache(); } else { \Cml\throwException(Lang::get('_CACHE_EXTEND_NOT_INSTALL_', 'Memcache')); } if (!$this->memcache) { \Cml\throwException(Lang::get('_CACHE_NEW_INSTANCE_ERROR_', 'Memcache')); } if (count($this->conf['server']) > 1) { //单台 if (!$this->memcache->connect($this->conf['host'], $this->conf['port'])) { \Cml\throwException(Lang::get('_CACHE_CONNECT_FAIL_', 'Memcache', $this->conf['host'] . ':' . $this->conf['port'])); } } else { //多台 foreach ($this->conf['server'] as $val) { $this->memcache->addServer($val['host'], $val['port']); //增加服务器 } } }
/** * 使用的缓存配置 默认为使用default_cache配置的参数 * * @param bool|array $conf */ public function __construct($conf = false) { $this->conf = $conf ? $conf : Config::get('default_cache'); if (extension_loaded('Memcached')) { $this->memcache = new \Memcached('cml_memcache_pool'); $this->type = 1; } elseif (extension_loaded('Memcache')) { $this->memcache = new \Memcache(); $this->type = 2; } else { \Cml\throwException(Lang::get('_CACHE_EXTEND_NOT_INSTALL_', 'Memcached/Memcache')); } if (!$this->memcache) { \Cml\throwException(Lang::get('_CACHE_NEW_INSTANCE_ERROR_', 'Memcache')); } if ($this->type == 2) { //memcache foreach ($this->conf['server'] as $val) { if (!$this->memcache->addServer($val['host'], $val['port'])) { \Cml\throwException(Lang::get('_CACHE_CONNECT_FAIL_', 'Memcache', $this->conf['host'] . ':' . $this->conf['port'])); } } return; } if (md5(json_encode($this->conf['server'])) !== md5(json_encode($this->memcache->getServerList()))) { $this->memcache->quit(); $this->memcache->resetServerList(); $this->memcache->setOption(\Memcached::OPT_PREFIX_KEY, $this->conf['prefix']); \Memcached::HAVE_JSON && $this->memcache->setOption(\Memcached::OPT_SERIALIZER, \Memcached::SERIALIZER_JSON_ARRAY); if (!$this->memcache->addServers(array_values($this->conf['server']))) { \Cml\throwException(Lang::get('_CACHE_CONNECT_FAIL_', 'Memcache', json_encode($this->conf['server']))); } } }
/** * URL组装 支持不同URL模式 * eg: \Cml\Http\Response::url('Home/Blog/cate/id/1') * * @param string $url URL表达式 路径/控制器/操作/参数1/参数1值/..... * @param int $echo 是否输出 1输出 0 return * * @return string */ public static function url($url = '', $echo = 1) { $return = ''; // 解析URL if (empty($url)) { throw new \InvalidArgumentException(Lang::get('_NOT_ALLOW_EMPTY_', 'url')); //'U方法参数出错' } // URL组装 $delimiter = Config::get('url_pathinfo_depr'); $url = ltrim($url, '/'); $url = implode($delimiter, explode('/', $url)); if (Config::get('url_model') == 1) { $return = $_SERVER['SCRIPT_NAME'] . '/' . $url; } elseif (Config::get('url_model') == 2) { $return = Cml::getContainer()->make('cml_route')->getSubDirName() . $url; } elseif (Config::get('url_model') == 3) { $return = $_SERVER['SCRIPT_NAME'] . '?' . Config::get('var_pathinfo') . '=/' . $url; } $return .= Config::get('url_model') == 2 ? Config::get('url_html_suffix') : ''; $return = Secure::filterScript($return); if ($echo === 1) { echo $return; } else { return $return; } return ''; }
/** * 打印数据到chrome控制台 * * @param mixed $var 要打印的变量 * @param string $tag 标签 * * @return void */ function dumpUsePHPConsole($var, $tag = 'debug') { if (!Config::get('dump_use_php_console')) { throwException(Lang::get('_NOT_OPEN_', 'dump_use_php_console')); } static $connector = false; if ($connector === false) { $connector = PhpConsoleConnector::getInstance(); $connector->setPassword(Config::get('php_console_password')); } $connector->getDebugDispatcher()->dispatchDebug($var, $tag); }
/** * 运行对应的控制器 * * @param string $method 要执行的控制器方法 * * @return void */ public final function runAppController($method) { //检测csrf跨站攻击 Secure::checkCsrf(Config::get('check_csrf')); // 关闭GPC过滤 防止数据的正确性受到影响 在db层防注入 if (get_magic_quotes_gpc()) { Secure::stripslashes($_GET); Secure::stripslashes($_POST); Secure::stripslashes($_COOKIE); Secure::stripslashes($_REQUEST); //在程序中对get post cookie的改变不影响 request的值 } //session保存方式自定义 if (Config::get('session_user')) { Session::init(); } else { ini_get('session.auto_start') || session_start(); //自动开启session } header('Cache-control: ' . Config::get('http_cache_control')); // 页面缓存控制 //如果有子类中有init()方法 执行Init() eg:做权限控制 if (method_exists($this, "init")) { $this->init(); } //根据动作去找对应的方法 if (method_exists($this, $method)) { $this->{$method}(); } elseif (Cml::$debug) { Cml::montFor404Page(); throw new \BadMethodCallException(Lang::get('_ACTION_NOT_FOUND_', $method)); } else { Cml::montFor404Page(); Response::show404Page(); } }
/** * 根据指定的路径创建不存在的文件夹 * * @param string $path 路径/文件夹名称 * * @return string */ private function makePath($path) { $path = dirname($path); if (!is_dir($path) && !mkdir($path, 0700, true)) { \Cml\throwException(Lang::get('_CREATE_DIR_ERROR_') . "[{$path}]"); } return true; }
/** * table组装工厂 * * @param bool $isRead 是否为读操作 * * @return array */ private function tableFactory($isRead = true) { $table = $operator = ''; $cacheKey = []; foreach ($this->table as $key => $val) { $realTable = $this->getRealTableName($key); $cacheKey[] = $isRead ? $this->getCacheVer($realTable) : $realTable; $on = null; if (isset($this->join[$key])) { $operator = ' INNER JOIN'; $on = $this->join[$key]; } elseif (isset($this->leftJoin[$key])) { $operator = ' LEFT JOIN'; $on = $this->leftJoin[$key]; } elseif (isset($this->rightJoin[$key])) { $operator = ' RIGHT JOIN'; $on = $this->rightJoin[$key]; } else { empty($table) || ($operator = ' ,'); } if (is_null($val)) { $table .= "{$operator} {$realTable}"; } else { $table .= "{$operator} {$realTable} AS `{$val}`"; } isset($this->forceIndex[$realTable]) && ($table .= ' force index(' . $this->forceIndex[$realTable] . ') '); is_null($on) || ($table .= " ON {$on}"); } if (empty($table)) { throw new \InvalidArgumentException(Lang::get('_PARSE_SQL_ERROR_NO_TABLE_', $isRead ? 'select' : 'update/delete')); } return [$table, $cacheKey]; }
/** * 自动加载类库 * 要注意的是 使用autoload的时候 不能手动抛出异常 * 因为在自动加载静态类时手动抛出异常会导致自定义的致命错误捕获机制和自定义异常处理机制失效 * 而 new Class 时自动加载不存在文件时,手动抛出的异常可以正常捕获 * 这边即使文件不存在时没有抛出自定义异常也没关系,因为自定义的致命错误捕获机制会捕获到错误 * * @params string $className */ public static function autoloadComposerAdditional($className) { $GLOBALS['debug'] && \Cml\Debug::addTipInfo(\Cml\Lang::get('_CML_DEBUG_ADD_CLASS_TIP_', $className), 1); //在debug中显示包含的类 }
/** * 启动框架 * */ public static function runApp() { //系统初始化 self::init(); //控制器所在路径 $actionController = CML_APP_CONTROLLER_PATH . Route::$urlParams['controller'] . 'Controller.php'; $GLOBALS['debug'] && Debug::addTipInfo(Lang::get('_CML_ACTION_CONTROLLER_', $actionController)); Plugin::hook('cml.before_run_controller'); if (is_file($actionController)) { $className = Route::$urlParams['controller'] . 'Controller'; $className = (CML_IS_MULTI_MODULES ? '' : '\\Controller') . Route::$urlParams['path'] . (CML_IS_MULTI_MODULES ? 'Controller' . DIRECTORY_SEPARATOR : '') . "{$className}"; $className = str_replace('/', '\\', $className); $controller = new $className(); call_user_func(array($controller, "runAppController")); //运行 } else { self::montFor404Page(); if ($GLOBALS['debug']) { throwException(Lang::get('_CONTROLLER_NOT_FOUND_', CML_APP_CONTROLLER_PATH, Route::$urlParams['controller'], str_replace('/', '\\', Route::$urlParams['path']) . Route::$urlParams['controller'])); } else { Response::show404Page(); } } //输出Debug模式的信息 self::cmlStop(); }
/** * 启动应用 * * @param callable $initDi 注入依赖 */ public static function runApp(callable $initDi) { //系统初始化 self::init($initDi); Plugin::hook('cml.before_run_controller'); $controllerAction = Cml::getContainer()->make('cml_route')->getControllerAndAction(); if ($controllerAction) { Cml::$debug && Debug::addTipInfo(Lang::get('_CML_ACTION_CONTROLLER_', $controllerAction['class'])); $controller = new $controllerAction['class'](); call_user_func([$controller, "runAppController"], $controllerAction['action']); //运行 } else { self::montFor404Page(); if (self::$debug) { throw new ControllerNotFoundException(Lang::get('_CONTROLLER_NOT_FOUND_')); } else { Response::show404Page(); } } //输出Debug模式的信息 self::cmlStop(); }
/** * 分组路由 * * @param string $namespace 分组名 * @param callable $func 闭包 */ public function group($namespace, callable $func) { if (empty($namespace)) { throw new \InvalidArgumentException(Lang::get('_NOT_ALLOW_EMPTY_', '$namespace')); } self::$group = trim($namespace, '/'); $func(); self::$group = false; }
/** * 载入应用单独的路由 * * @param string $app 应用名称 */ public static function loadAppRoute($app = 'web') { static $loaded = []; if (isset($loaded[$app])) { return; } $appRoute = Cml::getApplicationDir('apps_path') . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . Cml::getApplicationDir('app_config_path_name') . DIRECTORY_SEPARATOR . 'route.php'; if (!is_file($appRoute)) { throw new \InvalidArgumentException(Lang::get('_NOT_FOUND_', $app . DIRECTORY_SEPARATOR . Cml::getApplicationDir('app_config_path_name') . DIRECTORY_SEPARATOR . 'route.php')); } $loaded[$app] = 1; Cml::requireFile($appRoute); }
/** * 获取多条数据 * * @return array */ public function select() { $this->sql['columns'] == '' && ($this->sql['columns'] = '*'); $columns = $this->sql['columns'] == '*' ? Config::get('db_fields_cache') ? $this->getDbFields($this->getRealTableName(key($this->table)), null, 1) : '*' : $this->sql['columns']; $table = $operator = $cacheKey = ''; foreach ($this->table as $key => $val) { $realTable = $this->getRealTableName($key); $cacheKey .= $this->getCacheVer($realTable); $on = null; if (isset($this->join[$key])) { $operator = ' INNER JOIN'; $on = $this->join[$key]; } elseif (isset($this->leftJoin[$key])) { $operator = ' LEFT JOIN'; $on = $this->leftJoin[$key]; } elseif (isset($this->rightJoin[$key])) { $operator = ' RIGHT JOIN'; $on = $this->rightJoin[$key]; } else { empty($table) || ($operator = ' ,'); } if (is_null($val)) { $table .= "{$operator} `{$realTable}`"; } else { $table .= "{$operator} `{$realTable}` AS `{$val}`"; } is_null($on) || ($table .= " ON {$on}"); } empty($table) && \Cml\throwException(Lang::get('_PARSE_SQL_ERROR_NO_TABLE_', 'select')); empty($this->sql['limit']) && ($this->sql['limit'] = "LIMIT 0, 100"); $sql = "SELECT {$columns} FROM {$table} " . $this->sql['where'] . $this->sql['groupBy'] . $this->sql['having'] . $this->sql['orderBy'] . $this->sql['limit'] . $this->union; $cacheKey = md5($sql . json_encode($this->bindParams)) . $cacheKey; $return = Model::getInstance()->cache()->get($cacheKey); if ($return === false) { $stmt = $this->prepare($sql, $this->rlink); $this->execute($stmt); $return = $stmt->fetchAll(\PDO::FETCH_ASSOC); Model::getInstance()->cache()->set($cacheKey, $return, $this->conf['cache_expire']); } else { $this->reset(); $this->bindParams = array(); } return $return; }
/** * 从文件载入Config * * @param $file */ public static function load($file, $global = true) { if (isset(static::$_content[$file])) { return static::$_content[$file]; } else { $file = CML_APP_FULL_PATH . DIRECTORY_SEPARATOR . ($global ? '' : \Cml\Config::get('application_dir') . Route::$urlParams['path']) . 'Config' . DIRECTORY_SEPARATOR . ($global ? self::$isLocal . DIRECTORY_SEPARATOR : '') . $file . '.php'; is_file($file) || throwException(Lang::get('_FILE_NOT_FOUND_', $file)); static::$_content[$file] = (require $file); return static::$_content[$file]; } }
/** * 防止csrf跨站攻击 * * @param int $type 检测类型 0不检查,1、只检查post,2、post get都检查 */ public static function checkCsrf($type = 1) { if ($type !== 0 && isset($_SERVER['HTTP_REFERER']) && !strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'])) { if ($type == 1) { if (!empty($_POST)) { Response::sendHttpStatus(403); throw new \UnexpectedValueException(Lang::get('_ILLEGAL_REQUEST_')); } } else { Response::sendHttpStatus(403); throw new \UnexpectedValueException(Lang::get('_ILLEGAL_REQUEST_')); } } }
/** * 根据key值删除数据 * * @param string $key eg: 'user-uid-$uid' * @param bool $and 多个条件之间是否为and true为and false为or * @param mixed $tablePrefix 表前缀 不传则获取配置中配置的前缀 * * @return boolean */ public function delete($key = '', $and = true, $tablePrefix = null) { is_null($tablePrefix) && ($tablePrefix = $this->tablePrefix); $tableName = $condition = ''; empty($key) || (list($tableName, $condition) = $this->parseKey($key, $and, true, true)); $tableName = empty($tableName) ? $this->getRealTableName(key($this->table)) : $tablePrefix . $tableName; if (empty($tableName)) { throw new \InvalidArgumentException(Lang::get('_PARSE_SQL_ERROR_NO_TABLE_', 'delete')); } $condition += $this->sql['where']; if (empty($condition)) { throw new \InvalidArgumentException(Lang::get('_PARSE_SQL_ERROR_NO_CONDITION_', 'delete')); } $bulk = new BulkWrite(); $bulk->delete($condition); $result = $this->runMongoBulkWrite($tableName, $bulk); Cml::$debug && $this->debugLogSql('BulkWrite DELETE', $tableName, $condition); return $result->getDeletedCount(); }
/** * 获取Lock实例 * * @param string|null $useCache * * @return \Cml\Lock\Redis | \Cml\Lock\Memcache | \Cml\Lock\File | false * @throws \Exception */ public function locker($useCache = null) { is_null($useCache) && ($useCache = Config::get('locker_use_cache', 'default_cache')); static $_instance = array(); $config = Config::get($useCache); if (isset($_instance[$useCache])) { return $_instance[$useCache]; } else { if ($config['on']) { $lock = 'Cml\\Lock\\' . $config['driver']; $_instance[$useCache] = new $lock($useCache); return $_instance[$useCache]; } else { throwException($useCache . Lang::get('_NOT_OPEN_')); return false; } } }
/** * 根据key值删除数据 * * @param string $key eg: 'user-uid-$uid' * @param bool $and 多个条件之间是否为and true为and false为or * * @return boolean */ public function delete($key = '', $and = true) { $tableName = $condition = ''; empty($key) || (list($tableName, $condition) = $this->parseKey($key, $and, true, true)); $tableName = empty($tableName) ? $this->getRealTableName(key($this->table)) : $this->tablePrefix . $tableName; empty($tableName) && \Cml\throwException(Lang::get('_PARSE_SQL_ERROR_NO_TABLE_', 'delete')); $condition += $this->sql['where']; empty($condition) && \Cml\throwException(Lang::get('_PARSE_SQL_ERROR_NO_CONDITION_', 'delete')); $bulk = new BulkWrite(); $bulk->delete($condition); $result = $this->runMongoBulkWrite($tableName, $bulk); $GLOBALS['debug'] && $this->debugLogSql('BulkWrite DELETE', $this->tablePrefix . $tableName, $condition); return $result->getDeletedCount(); }
/** * 从文件载入Config * * @param string $file * @param bool $global 是否从全局加载 * * @return array */ public static function load($file, $global = true) { if (isset(static::$_content[$file])) { return static::$_content[$file]; } else { $file = ($global ? Cml::getApplicationDir('global_config_path') : Cml::getApplicationDir('apps_path') . '/' . Cml::getContainer()->make('cml_route')->getAppName() . '/' . Cml::getApplicationDir('app_config_path_name')) . '/' . ($global ? self::$isLocal . DIRECTORY_SEPARATOR : '') . $file . '.php'; if (!is_file($file)) { throw new ConfigNotFoundException(Lang::get('_NOT_FOUND_', $file)); } static::$_content[$file] = Cml::requireFile($file); return static::$_content[$file]; } }
/** * 获取多条数据 * * @param int $offset 偏移量 * @param int $limit 返回的条数 * @param bool $useMaster 是否使用主库 默认读取从库 * * @return array */ public function select($offset = null, $limit = null, $useMaster = false) { is_null($offset) || $this->limit($offset, $limit); $this->sql['columns'] == '' && ($this->sql['columns'] = '*'); $columns = $this->sql['columns']; $table = $operator = $cacheKey = ''; foreach ($this->table as $key => $val) { $realTable = $this->getRealTableName($key); $cacheKey .= $this->getCacheVer($realTable); $on = null; if (isset($this->join[$key])) { $operator = ' INNER JOIN'; $on = $this->join[$key]; } elseif (isset($this->leftJoin[$key])) { $operator = ' LEFT JOIN'; $on = $this->leftJoin[$key]; } elseif (isset($this->rightJoin[$key])) { $operator = ' RIGHT JOIN'; $on = $this->rightJoin[$key]; } else { empty($table) || ($operator = ' ,'); } if (is_null($val)) { $table .= "{$operator} `{$realTable}`"; } else { $table .= "{$operator} `{$realTable}` AS `{$val}`"; } is_null($on) || ($table .= " ON {$on}"); } if (empty($table)) { throw new \InvalidArgumentException(Lang::get('_PARSE_SQL_ERROR_NO_TABLE_', 'select')); } empty($this->sql['limit']) && ($this->sql['limit'] = "LIMIT 0, 100"); $sql = "SELECT {$columns} FROM {$table} " . $this->sql['where'] . $this->sql['groupBy'] . $this->sql['having'] . $this->sql['orderBy'] . $this->union . $this->sql['limit']; $cacheKey = md5($sql . json_encode($this->bindParams)) . $cacheKey; $return = Model::getInstance()->cache()->get($cacheKey); if ($return === false) { $stmt = $this->prepare($sql, $useMaster ? $this->wlink : $this->rlink); $this->execute($stmt); $return = $stmt->fetchAll(\PDO::FETCH_ASSOC); Model::getInstance()->cache()->set($cacheKey, $return, $this->conf['cache_expire']); } else { if (Cml::$debug) { $this->currentSql = $sql; $this->debugLogSql(Debug::SQL_TYPE_FROM_CACHE); $this->currentSql = ''; } $this->reset(); $this->clearBindParams(); } return $return; }
/** * 清洗已经存储的所有元素 * */ public function truncate() { foreach ($this->conf['server'] as $key => $val) { if (!isset($this->redis[$key]) || !is_object($this->redis[$key])) { $instance = new \Redis(); if ($instance->pconnect($val['host'], $val['port'], 1.5)) { $this->redis[$key] = $instance; } else { throw new \RuntimeException(Lang::get('_CACHE_NEW_INSTANCE_ERROR_', 'Redis')); } } $this->redis[$key]->flushDB(); } return true; }
/** * 匹配路由 * * @param $pathinfo * * @return mixed */ public static function isRoute(&$pathinfo) { empty($pathinfo) && ($pathinfo[0] = '/'); //网站根地址 $issuccess = array(); $route = self::$rules; switch ($_SERVER['REQUEST_METHOD']) { case 'GET': $rmethod = self::REQUEST_METHOD_GET; break; case 'POST': $rmethod = self::REQUEST_METHOD_POST; break; case 'PUT': $rmethod = self::REQUEST_METHOD_PUT; break; case 'PATCH': $rmethod = self::REQUEST_METHOD_PATCH; break; case 'DELETE': $rmethod = self::REQUEST_METHOD_DELETE; break; case 'OPTIONS': $rmethod = self::REQUEST_METHOD_OPTIONS; break; default: $rmethod = self::REQUEST_METHOD_ANY; } foreach ($route as $k => $v) { $rulesmethod = substr($k, 0, 1); if ($rulesmethod != $rmethod && $rulesmethod != self::REQUEST_METHOD_ANY && $rulesmethod != self::RESTROUTE) { //此条路由不符合当前请求方式 continue; } unset($v); $singleRule = substr($k, 1); $arr = $singleRule === '/' ? array(0 => $singleRule) : explode('/', ltrim($singleRule, '/')); if ($arr[0] == $pathinfo[0]) { array_shift($arr); foreach ($arr as $key => $val) { if (isset($pathinfo[$key + 1]) && $pathinfo[$key + 1] !== '') { if (strpos($val, '\\d') && !is_numeric($pathinfo[$key + 1])) { //数字变量 $route[$k] = false; //匹配失败 break 1; } elseif (strpos($val, ':') === false && $val != $pathinfo[$key + 1]) { //字符串 $route[$k] = false; //匹配失败 break 1; } } else { $route[$k] = false; //匹配失败 break 1; } } } else { $route[$k] = false; //匹配失败 } if ($route[$k] !== false) { //匹配成功的路由 $issuccess[] = $k; } } if (empty($issuccess)) { $returnArr[0] = false; } else { //匹配到多条路由时 选择最长的一条(匹配更精确) usort($issuccess, function ($item1, $item2) { return strlen($item1) >= strlen($item2) ? 0 : 1; }); if (is_callable($route[$issuccess[0]])) { call_user_func($route[$issuccess[0]]); Cml::cmlStop(); } $route[$issuccess[0]] = trim($route[$issuccess[0]], '/'); //判断路由的正确性 count(explode('/', $route[$issuccess[0]])) >= 2 || throwException(Lang::get('_ROUTE_PARAM_ERROR_', substr($issuccess[0], 1))); $returnArr[0] = true; $successRoute = explode('/', $issuccess[0]); foreach ($successRoute as $key => $val) { $t = explode('\\d', $val); if (strpos($t[0], ':') !== false) { $_GET[ltrim($t[0], ':')] = $pathinfo[$key]; } unset($pathinfo[$key]); } if (substr($issuccess[0], 0, 1) == self::RESTROUTE) { $actions = explode('/', $route[$issuccess[0]]); $arrKey = count($actions) - 1; $actions[$arrKey] = strtolower($_SERVER['REQUEST_METHOD']) . ucfirst($actions[$arrKey]); $route[$issuccess[0]] = implode('/', $actions); } $returnArr['route'] = $route[$issuccess[0]]; } return $returnArr; }
/** *SQL语句条件组装 * *@param string $key eg: 'forum-fid-1-uid-2' *@param bool $and 多个条件之间是否为and true为and false为or *@param bool $noCondition 是否为无条件操作 set/delete/update操作的时候 condition为空是正常的不报异常 *@param bool $noTable 是否可以没有数据表 当delete/update等操作的时候已经执行了table() table为空是正常的 * *@return array eg: array('forum', "`fid` = '1' AND `uid` = '2'") */ protected function parseKey($key, $and = true, $noCondition = false, $noTable = false) { $condition = ''; $arr = explode('-', $key); $len = count($arr); for ($i = 1; $i < $len; $i += 2) { isset($arr[$i + 1]) && ($condition .= ($condition ? $and ? ' AND ' : ' OR ' : '') . "`{$arr[$i]}` = ?"); $this->bindParams[] = $arr[$i + 1]; } $table = strtolower($arr[0]); empty($table) && !$noTable && \Cml\throwException(Lang::get('_DB_PARAM_ERROR_PARSE_KEY_', $key, 'table')); empty($condition) && !$noCondition && \Cml\throwException(Lang::get('_DB_PARAM_ERROR_PARSE_KEY_', $key, 'condition')); empty($condition) || ($condition = "({$condition})"); return array($table, $condition); }
/** * 使用的缓存配置 默认为使用default_cache配置的参数 * * @param bool|array $conf * * @throws CacheConnectFailException | PhpExtendNotInstall */ public function __construct($conf = false) { $this->conf = $conf ? $conf : Config::get('default_cache'); if (extension_loaded('Memcached')) { $this->memcache = new \Memcached('cml_memcache_pool'); $this->type = 1; } elseif (extension_loaded('Memcache')) { $this->memcache = new \Memcache(); $this->type = 2; } else { throw new PhpExtendNotInstall(Lang::get('_CACHE_EXTEND_NOT_INSTALL_', 'Memcached/Memcache')); } if (!$this->memcache) { throw new PhpExtendNotInstall(Lang::get('_CACHE_NEW_INSTANCE_ERROR_', 'Memcache')); } $singleNodeDownFunction = function ($host, $port) { //这边挂掉调用此回调在几s内只会调用一次。其它情况使用memcache方法均返回flase不报错 Plugin::hook('cml.cache_server_down', ['host' => $host, 'port' => $port]); Log::emergency('memcache server down', ['downServer' => ['host' => $host, 'port' => $port]]); }; $allNodeDownFunction = function ($serverList) { Plugin::hook('cml.cache_server_down', $this->conf['server'], $serverList); //全挂 throw new CacheConnectFailException(Lang::get('_CACHE_CONNECT_FAIL_', 'Memcache', json_encode($serverList))); }; $downServer = 0; if ($this->type == 2) { //memcache foreach ($this->conf['server'] as $val) { if (!$this->memcache->addServer($val['host'], $val['port'])) { Log::emergency('memcache server down', ['downServer' => $val]); } } $this->memcache->setFailureCallback($singleNodeDownFunction); $serverList = $this->memcache->getextendedstats(); foreach ($serverList as $server => $status) { $status || $downServer++; } if (count($serverList) <= $downServer) { $allNodeDownFunction($serverList); } return; } if (md5(json_encode($this->conf['server'])) !== md5(json_encode($this->memcache->getServerList()))) { $this->memcache->quit(); $this->memcache->resetServerList(); $this->memcache->addServers(array_values($this->conf['server'])); $this->memcache->setOptions([\Memcached::OPT_PREFIX_KEY => $this->conf['prefix'], \Memcached::OPT_DISTRIBUTION => \Memcached::DISTRIBUTION_CONSISTENT, \Memcached::OPT_LIBKETAMA_COMPATIBLE => true, \Memcached::OPT_SERVER_FAILURE_LIMIT => 1, \Memcached::OPT_RETRY_TIMEOUT => 30, \Memcached::OPT_AUTO_EJECT_HOSTS => true, \Memcached::OPT_REMOVE_FAILED_SERVERS => true]); \Memcached::HAVE_JSON && $this->memcache->setOption(\Memcached::OPT_SERIALIZER, \Memcached::SERIALIZER_JSON_ARRAY); } $serverStatus = $this->memcache->getStats(); foreach ($serverStatus as $server => $status) { if ($status['pid'] < 1) { $downServer++; $server = explode(':', $server); $singleNodeDownFunction($server[0], $server[1]); } } if (count($serverStatus) <= $downServer) { $allNodeDownFunction($serverStatus); } }