/** * Bootstrap Phinx. * * @param array $args * @param array $options */ public function bootstrap(array $args, array $options = []) { if (false === class_exists('\\Phinx\\Config\\Config')) { throw new \RuntimeException('please use `composer require linhecheng/cmlphp-ext-phinx` cmd to install phinx.'); } if (!$this->getConfig()) { $this->loadConfig($options); } $this->loadManager($args, $options); // report the paths Output::writeln('using migration path ' . Colour::colour(str_replace(Cml::getApplicationDir('secure_src'), '{secure_src}', $this->getConfig()->getMigrationPath()), Colour::GREEN)); Output::writeln('using seed path ' . Colour::colour(str_replace(Cml::getApplicationDir('secure_src'), '{secure_src}', $this->getConfig()->getSeedPath()), Colour::GREEN)); $exportPath = false; if (isset($options['e'])) { $exportPath = $options['e']; } else { if (isset($options['export'])) { $exportPath = $options['export']; } } if ($exportPath) { is_dir($exportPath) || ($exportPath = $this->getConfig()->getExportPath()); is_dir($exportPath) || mkdir($exportPath, 0700, true); Output::writeln('using export path:' . Colour::colour(str_replace(Cml::getApplicationDir('secure_src'), '{secure_src}', $exportPath), Colour::GREEN)); $merge = isset($options['m']) || isset($options['merge']) ? 'merge_export_' . date('Y-m-d-H-i-s') . '.sql' : false; $this->getManager()->getEnvironment()->setExportPath($exportPath, $merge); } $this->getConfig()->echoAdapterInfo(); }
/** * 自定义异常处理 * * @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')); } }
/** * 自定义异常处理 * * @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; }
/** * 创建一个新的seeder * * @param array $args 参数 * @param array $options 选项 */ public function execute(array $args, array $options = []) { $this->bootstrap($args, $options); // get the seed path from the config $path = $this->getConfig()->getSeedPath(); if (!file_exists($path)) { $ask = new Dialog(); if ($ask->confirm(Colour::colour('Create seeds directory?', [Colour::RED, Colour::HIGHLIGHT]))) { mkdir($path, 0755, true); } } $this->verifySeedDirectory($path); $path = realpath($path); $className = $args[0]; if (!Util::isValidPhinxClassName($className)) { throw new \InvalidArgumentException(sprintf('The seed class name "%s" is invalid. Please use CamelCase format', $className)); } // Compute the file path $filePath = $path . DIRECTORY_SEPARATOR . $className . '.php'; if (is_file($filePath)) { throw new \InvalidArgumentException(sprintf('The file "%s" already exists', basename($filePath))); } // inject the class names appropriate to this seeder $contents = file_get_contents($this->getSeedTemplateFilename()); $classes = ['$useClassName' => 'Phinx\\Seed\\AbstractSeed', '$className' => $className, '$baseClassName' => 'AbstractSeed']; $contents = strtr($contents, $classes); if (false === file_put_contents($filePath, $contents)) { throw new \RuntimeException(sprintf('The file "%s" could not be written to', $path)); } Output::writeln('using seed base class ' . $classes['$useClassName']); Output::writeln('created ' . str_replace(Cml::getApplicationDir('secure_src'), '{secure_src}', $filePath)); }
/** * 从注释解析生成文档 * */ public static function parse() { $result = []; $config = Config::load('api', Cml::getApplicationDir('app_controller_path') ? true : false); foreach ($config['version'] as $version => $apiList) { isset($result[$version]) || ($result[$version] = []); foreach ($apiList as $model => $api) { $pos = strrpos($api, '\\'); $controller = substr($api, 0, $pos); $action = substr($api, $pos + 1); if (class_exists($controller) === false) { continue; } $annotationParams = self::getAnnotationParams($controller, $action); empty($annotationParams) || ($result[$version][$model] = $annotationParams); } } foreach ($result as $key => $val) { if (count($val) < 1) { unset($result[$key]); } } $systemCode = Cml::requireFile(__DIR__ . DIRECTORY_SEPARATOR . 'resource' . DIRECTORY_SEPARATOR . 'code.php'); Cml::requireFile(__DIR__ . DIRECTORY_SEPARATOR . 'resource' . DIRECTORY_SEPARATOR . 'doc.html', ['config' => $config, 'result' => $result, 'systemCode' => $systemCode]); }
/** * 创建控制器 * * @param array $args 参数 * @param array $options 选项 */ public function execute(array $args, array $options = []) { $template = isset($options['template']) ? $options['template'] : false; $template || ($template = __DIR__ . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Controller.php.dist'); $name = $args[0]; $name = explode('-', $name); if (count($name) < 2) { throw new \InvalidArgumentException(sprintf('The arg name "%s" is invalid. eg: adminbase-Blog/Category', $name)); } $namespace = trim(trim($name[0], '\\/')); $path = Cml::getApplicationDir('apps_path') . DIRECTORY_SEPARATOR . $namespace . DIRECTORY_SEPARATOR . Cml::getApplicationDir('app_controller_path_name') . DIRECTORY_SEPARATOR; $component = explode('/', trim(trim($name[1], '/'))); if (count($component) > 1) { $className = ucfirst(array_pop($component)) . Config::get('controller_suffix'); $component = implode(DIRECTORY_SEPARATOR, $component); $path .= $component . DIRECTORY_SEPARATOR; $component = '\\' . $component; } else { $className = ucfirst($component[0]) . Config::get('controller_suffix'); $component = ''; } if (!is_dir($path) && false == mkdir($path, 0700, true)) { throw new \RuntimeException(sprintf('The path "%s" could not be create', $path)); } $contents = strtr(file_get_contents($template), ['$namespace' => $namespace, '$component' => $component, '$className' => $className]); if (false === file_put_contents($path . $className . '.php', $contents)) { throw new \RuntimeException(sprintf('The file "%s" could not be written to', $path)); } Output::writeln(Colour::colour('Controller created successfully. ', Colour::GREEN)); }
/** * 静态页面-获取静态页面 * * @param string $key 静态页面标识符,可以用id代替 * * @return bool */ public function get($key) { $filename = $this->getFilename($key); if (!$filename || !is_file($filename)) { return false; } Cml::requireFile($filename); return true; }
/** * 获取配置参数不区分大小写 * * @param string $key 支持.获取多维数组 * @param string $default 不存在的时候默认值 * * @return mixed */ public static function get($key = null, $default = null) { // 无参数时获取所有 if (empty($key)) { return self::$_content; } $key = strtolower($key); return Cml::doteToArr($key, self::$_content['normal'], $default); }
/** * 运行对应的控制器 * * @return void */ public final function runAppController() { //检测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(); } //根据动作去找对应的方法 $method = Route::$urlParams['action']; if (method_exists($this, $method)) { $this->{$method}(); } elseif ($GLOBALS['debug']) { Cml::montFor404Page(); throwException(Lang::get('_ACTION_NOT_FOUND_', Route::$urlParams['action'])); } else { Cml::montFor404Page(); Response::show404Page(); } }
/** * 获取Logger实例 * * @return Base */ private static function getLogger() { return Cml::getContainer()->make('cml_log'); }
/** * 程序中并输出调试信息 * */ public static function cmlStop() { //输出Debug模式的信息 if (self::$debug) { header('Content-Type:text/html; charset=' . Config::get('default_charset')); Debug::stop(); } else { $deBugLogData = dump('', 1); if (!empty($deBugLogData)) { Config::get('dump_use_php_console') ? dumpUsePHPConsole($deBugLogData) : Cml::requireFile(CML_CORE_PATH . DIRECTORY_SEPARATOR . 'ConsoleLog.php', ['deBugLogData' => $deBugLogData]); } CML_OB_START && ob_end_flush(); } exit; }
/** * 构造方法 * */ public function __construct() { $this->logDir = Cml::getApplicationDir('runtime_logs_path') . DIRECTORY_SEPARATOR . date('Y/m/d') . DIRECTORY_SEPARATOR; is_dir($this->logDir) || mkdir($this->logDir, 0755, true); }
/** * 获取要执行的控制器类名及方法 * */ public function getControllerAndAction() { //控制器所在路径 $appName = self::getAppName(); $className = $appName . ($appName ? '/' : '') . Cml::getApplicationDir('app_controller_path_name') . '/' . self::getControllerName() . Config::get('controller_suffix'); $actionController = Cml::getApplicationDir('apps_path') . '/' . $className . '.php'; if (is_file($actionController)) { return ['class' => str_replace('/', '\\', $className), 'action' => self::getActionName()]; } else { return false; } }
/** * shell参数处理并启动守护进程 * * @param string $cmd */ public static function run($cmd) { self::$pidFile = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'DaemonProcess_.pid'; self::$log = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'DaemonProcess_.log'; self::$status = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'DaemonProcessStatus.php'; self::checkExtension(); $param = is_array($cmd) && count($cmd) == 2 ? $cmd[1] : $cmd; switch ($param) { case 'start': self::start(); break; case 'stop': posix_kill(self::getPid(), SIGINT); self::message('stop....'); break; case 'reload': posix_kill(self::getPid(), SIGUSR1); self::message('reloading....'); break; case 'status': self::getStatus(true); break; case 'addtask': if (func_num_args() < 1) { self::message('please input task name'); break; } $args = func_get_args(); $frequency = isset($args[2]) ? intval($args[2]) : 60; $frequency < 1 || ($frequency = 60); self::addTask($args[1], $frequency); break; case 'rmtask': if (func_num_args() < 1) { self::message('please input task name'); break; } $args = func_get_args(); self::rmTask($args[1]); break; default: self::message('Usage: xxx.php cml.cmd DaemonWorker::run {start|stop|status|addtask|rmtask}'); break; } }
/** * 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 ''; }
/** * 模板显示 调用内置的模板引擎显示方法, * * @param string $templateFile 指定要调用的模板文件 默认为空 由系统自动定位模板文件 * @param bool $inOtherApp 是否为载入其它应用的模板 * * @return void */ public function display($templateFile = '', $inOtherApp = false) { // 网页字符编码 header('Content-Type:text/html; charset=' . Config::get('default_charset')); echo $this->fetch($templateFile, $inOtherApp); Cml::cmlStop(); }
/** * 获取缓存文件名 * * @param string $key 缓存名 * * @return string */ private function getFileName($key) { $md5Key = md5($this->getKey($key)); $dir = Cml::getApplicationDir('runtime_cache_path') . DIRECTORY_SEPARATOR . 'LockFileCache' . DIRECTORY_SEPARATOR . substr($key, 0, strrpos($key, '/')) . DIRECTORY_SEPARATOR; $dir .= substr($md5Key, 0, 2) . DIRECTORY_SEPARATOR . substr($md5Key, 2, 2); is_dir($dir) || mkdir($dir, 0700, true); return $dir . DIRECTORY_SEPARATOR . $md5Key . '.php'; }
/** * 从文件载入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]; } }
/** * 使用的缓存配置 默认为使用default_cache配置的参数 * * @param bool|array $conf */ public function __construct($conf = false) { $this->conf = $conf ? $conf : Config::get('default_cache'); $this->conf['CACHE_PATH'] = isset($this->conf['CACHE_PATH']) ? $this->conf['CACHE_PATH'] : Cml::getApplicationDir('runtime_cache_path') . DIRECTORY_SEPARATOR . 'FileCache' . DIRECTORY_SEPARATOR; is_dir($this->conf['CACHE_PATH']) || mkdir($this->conf['CACHE_PATH'], 0700, true); }
/** * 获取要执行的控制器类名及方法 * */ public function getControllerAndAction() { $isOld = Cml::getApplicationDir('app_controller_path'); //控制器所在路径 $actionController = ($isOld ? $isOld . self::getAppName() : Cml::getApplicationDir('apps_path') . '/' . self::getAppName() . '/' . Cml::getApplicationDir('app_controller_path_name')) . '/' . self::getControllerName() . 'Controller.php'; if (is_file($actionController)) { $className = self::getControllerName() . 'Controller'; $className = ($isOld ? '\\Controller\\' : '') . self::getAppName() . ($isOld ? '/' : '/Controller' . '/') . "{$className}"; $className = str_replace('/', '\\', $className); return ['class' => $className, 'action' => self::getActionName()]; } else { return false; } }
/** * 抽象display * * @param string $templateFile 模板文件 * * @return mixed */ public function display($templateFile = '') { $options = $this->initBaseDir($templateFile); $compiler = new BladeCompiler($options['cacheDir'], $options['layoutCacheRootPath']); $compiler->directive('datetime', function ($timestamp) { return preg_replace('/(\\(\\d+\\))/', '<?php echo date("Y-m-d H:i:s", $1); ?>', $timestamp); }); $compiler->directive('hook', function ($hook) { return preg_replace('/\\((.*?)\\)/', '<?php \\Cml\\Plugin::hook("$1"); ?>', $hook); }); $compiler->directive('urldeper', function () { return '<?php echo \\Cml\\Config::get("url_model") == 3 ? "&" : "?"; ?>'; }); $compiler->directive('get', function ($key) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Http\\Input::getString("${1}");?>', $key); }); $compiler->directive('post', function ($key) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Http\\Input::postString("${1}");?>', $key); }); $compiler->directive('request', function ($key) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Http\\Input::requestString("${1}");?>', $key); }); $compiler->directive('url', function ($key) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Http\\Response::url("${1}"); ?>', $key); }); $compiler->directive('public', function () { return '<?php echo \\Cml\\Config::get("static__path", \\Cml\\Cml::getContainer()->make("cml_route")->getSubDirName()."public/");?>'; }); $compiler->directive('token', function () { return '<input type="hidden" name="CML_TOKEN" value="<?php echo \\Cml\\Secure::getToken();?>" />'; }); $compiler->directive('lang', function ($lang) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Lang::get("${1}"); ?>', $lang); }); $compiler->directive('config', function ($config) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Config::get("${1}"); ?>', $config); }); $compiler->directive('assert', function ($url) { return preg_replace('/\\((.*?)\\)/', '<?php echo \\Cml\\Tools\\StaticResource::parseResourceUrl("${1}"); ?>', $url); }); $compiler->directive('acl', function ($url) { return preg_replace('/\\((.*?)\\)/', '<?php if (\\Cml\\Vendor\\Acl::checkAcl("${1}")) : ?>', $url); }); $compiler->directive('endacl', function () { return '<?php endif; ?>'; }); foreach ($this->rule as $pattern => $func) { $compiler->directive($pattern, $func); } $finder = new FileViewFinder([$options['templateDir'], $options['layoutDir']]); $finder->addExtension(trim(Config::get('html_template_suffix'), '.')); $factory = new Factory($compiler, $finder); header('Content-Type:text/html; charset=' . Config::get('default_charset')); echo $factory->make($options['file'], $this->args)->render(); Cml::cmlStop(); }
/** * 从文件查找控制器 * * @param array $pathInfo * @param string $path */ private function findAction(&$pathInfo, &$path) { $controllerPath = $controllerName = ''; $controllerAppPath = Cml::getApplicationDir('app_controller_path'); //兼容旧版本 while ($dir = array_shift($pathInfo)) { $controllerName = ucfirst($dir); if ($controllerAppPath) { $controller = $controllerAppPath . $path; } else { $controller = Cml::getApplicationDir('apps_path') . $path . Cml::getApplicationDir('app_controller_path_name') . '/'; } $controller .= $controllerPath . $controllerName . 'Controller.php'; if ($path != '/' && is_file($controller)) { self::$urlParams['controller'] = $controllerPath . $controllerName; break; } else { if ($path == '/') { $path .= $dir . '/'; } else { $controllerPath .= $dir . '/'; } } } empty(self::$urlParams['controller']) && (self::$urlParams['controller'] = $controllerName); //用于404的时候挂载插件用 self::$urlParams['action'] = array_shift($pathInfo); }
/** *SQL语句条件组装 * *@param array $arr; 要组装的数组 *@param string $tableName 当前操作的数据表名 * *@return string */ protected function arrToCondition($arr, $tableName) { empty($tableName) && ($tableName = Cml::getContainer()->make('cml_route')->getControllerName()); /* //这个应该开发人员自己判断。框架不做额外开销 $dbFields = $this->getDbFields($tableName, $tablePrefix); foreach (array_keys($arr) as $key) { if (!isset($dbFields[$key])) unset($arr[$key]); //过滤db表中不存在的字段 } */ $s = $p = ''; $params = []; foreach ($arr as $k => $v) { if (is_array($v)) { //自增或自减 switch (key($v)) { case 'inc': $p = "`{$k}`= `{$k}`+" . abs(intval(current($v))); break; case 'dec': $p = "`{$k}`= `{$k}`-" . abs(intval(current($v))); break; case 'func': $func = strtoupper(key(current($v))); $funcParams = current(current($v)); foreach ($funcParams as $key => $val) { if (!isset($dbFields[$val])) { $funcParams[$key] = '%s'; $params[] = $val; } } $p = "`{$k}`= {$func}(" . implode($funcParams, ',') . ')'; break; default: //计算类型 $conkey = key($v); if (!isset($dbFields[$conkey])) { $conkey = $k; } if (!in_array(key(current($v)), ['+', '-', '*', '/', '%', '^', '&', '|', '<<', '>>', '~'])) { throw new \InvalidArgumentException(Lang::get('_PARSE_UPDATE_SQL_PARAMS_ERROR_')); } $p = "`{$k}`= `{$conkey}`" . key(current($v)) . abs(intval(current(current($v)))); break; } } else { $p = "`{$k}`= %s"; $params[] = $v; } $s .= (empty($s) ? '' : ',') . $p; } $this->bindParams = array_merge($params, $this->bindParams); return $s; }
/** * 匹配路由 * * @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; }
/** *输出分页 */ public function show() { if ($this->totalRows == 0) { return ''; } $nowCoolPage = ceil($this->nowPage / $this->barShowPage); $delimiter = Config::get('url_pathinfo_depr'); $params = array_merge($this->param, [$this->pageShowVarName => '__PAGE__']); $paramsString = ''; foreach ($params as $key => $val) { $paramsString == '' || ($paramsString .= '/'); $paramsString .= $key . '/' . $val; } if ($this->url) { $url = rtrim(Response::url($this->url . '/' . $paramsString, false), $delimiter); } else { $url = rtrim(Response::url(Cml::getContainer()->make('cml_route')->getFullPathNotContainSubDir() . '/' . $paramsString, false), $delimiter); } $upRow = $this->nowPage - 1; $downRow = $this->nowPage + 1; $upPage = $upRow > 0 ? '<li><a href = "' . str_replace('__PAGE__', $upRow, $url) . '">' . $this->config['prev'] . '</a></li>' : ''; $downPage = $downRow <= $this->totalPages ? '<li><a href="' . str_replace('__PAGE__', $downRow, $url) . '">' . $this->config['next'] . '</a></li>' : ''; // << < > >> if ($nowCoolPage == 1) { $theFirst = $prePage = ''; } else { $preRow = $this->nowPage - $this->barShowPage; $prePage = '<li><a href="' . str_replace('__PAGE__', $preRow, $url) . '">上' . $this->barShowPage . '页</a></li>'; $theFirst = '<li><a href="' . str_replace('__PAGE__', 1, $url) . '">' . $this->config['first'] . '</a></li>'; } if ($nowCoolPage == $this->coolPages) { $nextPage = $theEnd = ''; } else { $nextRow = $this->nowPage + $this->barShowPage; $theEndRow = $this->totalPages; $nextPage = '<li><a href="' . str_replace('__PAGE__', $nextRow, $url) . '">下' . $this->barShowPage . '页</a></li>'; $theEnd = '<li><a href="' . str_replace('__PAGE__', $theEndRow, $url) . '">' . $this->config['last'] . '</a></li>'; } //1 2 3 4 5 $linkPage = ''; for ($i = 1; $i <= $this->barShowPage; $i++) { $page = ($nowCoolPage - 1) * $this->barShowPage + $i; if ($page != $this->nowPage) { if ($page <= $this->totalPages) { $linkPage .= ' <li><a href="' . str_replace('__PAGE__', $page, $url) . '"> ' . $page . ' </a></li>'; } else { break; } } else { if ($this->totalPages != 1) { $linkPage .= ' <li class="active"><a>' . $page . '</a></li>'; } } } $pageStr = str_replace(['%header%', '%nowPage%', '%totalRow%', '%totalPage%', '%upPage%', '%downPage%', '%first%', '%prePage%', '%linkPage%', '%nextPage%', '%end%'], [$this->config['header'], $this->nowPage, $this->totalRows, $this->totalPages, $upPage, $downPage, $theFirst, $prePage, $linkPage, $nextPage, $theEnd], $this->config['theme']); return '<ul>' . $pageStr . '</ul>'; }
/** * 检查对应的权限 * * @param object|string $controller 传入控制器实例对象,用来判断当前访问的方法是不是要跳过权限检查。 * 如当前访问的方法为web/User/list则传入new \web\Controller\User()获得的实例。最常用的是在基础控制器的init方法或构造方法里传入$this。 * 传入字符串如web/User/list时会自动 new \web\Controller\User()获取实例用于判断 * * @return int 返回1是通过检查,0是不能通过检查 */ public static function checkAcl($controller) { $authInfo = self::getLoginInfo(); if (!$authInfo) { return false; } //登录超时 //当前登录用户是否为超级管理员 if (self::isSuperUser()) { return true; } $checkUrl = Cml::getContainer()->make('cml_route')->getFullPathNotContainSubDir(); $checkAction = Cml::getContainer()->make('cml_route')->getActionName(); if (is_string($controller)) { $checkUrl = trim($controller, '/\\'); $controller = str_replace('/', '\\', $checkUrl); $actionPosition = strrpos($controller, '\\'); $checkAction = substr($controller, $actionPosition + 1); $offset = $appPosition = 0; for ($i = 0; $i < Config::get('route_app_hierarchy', 1); $i++) { $appPosition = strpos($controller, '\\', $offset); $offset = $appPosition + 1; } $appPosition = $offset - 1; $subString = substr($controller, 0, $appPosition) . '\\' . Cml::getApplicationDir('app_controller_path_name') . substr($controller, $appPosition, $actionPosition - $appPosition); $controller = "\\{$subString}" . Config::get('controller_suffix'); if (class_exists($controller)) { $controller = new $controller(); } else { return false; } } $checkUrl = ltrim(str_replace('\\', '/', $checkUrl), '/'); if (is_object($controller)) { //判断是否有标识 @noacl 不检查权限 $reflection = new \ReflectionClass($controller); $methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { if ($method->name == $checkAction) { $annotation = $method->getDocComment(); if (strpos($annotation, '@noacl') !== false) { return true; } $checkUrlArray = []; if (preg_match('/@acljump([^\\n]+)/i', $annotation, $aclJump)) { if (isset($aclJump[1]) && $aclJump[1]) { $aclJump[1] = explode('|', $aclJump[1]); foreach ($aclJump[1] as $val) { trim($val) && ($checkUrlArray[] = ltrim(str_replace('\\', '/', trim($val)), '/')); } } empty($checkUrlArray) || ($checkUrl = $checkUrlArray); } } } } $acl = Model::getInstance()->db()->columns('m.id')->table(['access' => 'a'])->join(['menus' => 'm'], 'a.menuid=m.id')->lBrackets()->whereIn('a.groupid', $authInfo['groupid'])->_or()->where('a.userid', $authInfo['id'])->rBrackets(); $acl = is_array($checkUrl) ? $acl->whereIn('m.url', $checkUrl) : $acl->where('m.url', $checkUrl); $acl = $acl->select(); return count($acl) > 0; }
/** * 快速文件数据读取和保存 针对简单类型数据 字符串、数组 * * @param string $name 缓存名称 * @param mixed $value 缓存值 * @param string $path 缓存路径 * * @return mixed */ function simpleFileCache($name, $value = '', $path = null) { is_null($path) && ($path = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'Data'); static $_cache = []; $filename = $path . '/' . $name . '.php'; if ($value !== '') { if (is_null($value)) { // 删除缓存 return false !== @unlink($filename); } else { if (is_array($value)) { // 缓存数据 $dir = dirname($filename); // 目录不存在则创建 is_dir($dir) || mkdir($dir, 0700, true); $_cache[$name] = $value; return file_put_contents($filename, "<?php\treturn " . var_export($value, true) . ";?>", LOCK_EX); } else { return false; } } } if (isset($_cache[$name])) { return $_cache[$name]; } // 获取缓存数据 if (is_file($filename)) { $value = Cml::requireFile($filename); $_cache[$name] = $value; } else { $value = false; } return $value; }
/** * 创建一个迁移 * * @param array $args 参数 * @param array $options 选项 */ public function execute(array $args, array $options = []) { $className = $args[0]; $this->bootstrap($args, $options); if (!Util::isValidPhinxClassName($className)) { throw new \InvalidArgumentException(sprintf('The migration class name "%s" is invalid. Please use CamelCase format.', $className)); } // get the migration path from the config $path = $this->getConfig()->getMigrationPath(); if (!is_dir($path)) { $ask = new Dialog(); if ($ask->confirm(Colour::colour('Create migrations directory?', [Colour::RED, Colour::HIGHLIGHT]))) { mkdir($path, 0755, true); } } $this->verifyMigrationDirectory($path); $path = realpath($path); if (!Util::isUniqueMigrationClassName($className, $path)) { throw new \InvalidArgumentException(sprintf('The migration class name "%s" already exists', $className)); } // Compute the file path $fileName = Util::mapClassNameToFileName($className); $filePath = $path . DIRECTORY_SEPARATOR . $fileName; if (is_file($filePath)) { throw new \InvalidArgumentException(sprintf('The file "%s" already exists', $filePath)); } // Get the alternative template and static class options from the command line, but only allow one of them. $altTemplate = $options['template']; $creationClassName = $options['class']; if ($altTemplate && $creationClassName) { throw new \InvalidArgumentException('Cannot use --template and --class at the same time'); } // Verify the alternative template file's existence. if ($altTemplate && !is_file($altTemplate)) { throw new \InvalidArgumentException(sprintf('The alternative template file "%s" does not exist', $altTemplate)); } if ($creationClassName) { // Supplied class does not exist, is it aliased? if (!class_exists($creationClassName)) { throw new \InvalidArgumentException(sprintf('The class "%s" does not exist', $creationClassName)); } // Does the class implement the required interface? if (!is_subclass_of($creationClassName, self::CREATION_INTERFACE)) { throw new \InvalidArgumentException(sprintf('The class "%s" does not implement the required interface "%s"', $creationClassName, self::CREATION_INTERFACE)); } } // Determine the appropriate mechanism to get the template if ($creationClassName) { // Get the template from the creation class $creationClass = new $creationClassName(); $contents = $creationClass->getMigrationTemplate(); } else { // Load the alternative template if it is defined. $contents = file_get_contents($altTemplate ?: $this->getMigrationTemplateFilename()); } // inject the class names appropriate to this migration $classes = ['$useClassName' => $this->getConfig()->getMigrationBaseClassName(false), '$className' => $className, '$version' => Util::getVersionFromFileName($fileName), '$baseClassName' => $this->getConfig()->getMigrationBaseClassName(true)]; $contents = strtr($contents, $classes); if (false === file_put_contents($filePath, $contents)) { throw new \RuntimeException(sprintf('The file "%s" could not be written to', $path)); } // Do we need to do the post creation call to the creation class? if ($creationClassName) { $creationClass->postMigrationCreation($filePath, $className, $this->getConfig()->getMigrationBaseClassName()); } Output::writeln('using migration base class ' . Colour::colour($classes['$useClassName'], Colour::GREEN)); if (!empty($altTemplate)) { Output::writeln('using alternative template ' . Colour::colour($altTemplate, Colour::GREEN)); } elseif (!empty($creationClassName)) { Output::writeln('using template creation class ' . Colour::colour($creationClassName, Colour::GREEN)); } else { Output::writeln('using default template'); } Output::writeln('created ' . str_replace(Cml::getApplicationDir('secure_src'), '{secure_src}', $filePath)); }
Cml::getContainer()->singleton('cml_debug', \Cml\Debug::class); //必须绑定。命令行组件 Cml::getContainer()->singleton('cml_console', \Cml\Console\Console::class); //可选,队列服务 内置 \Cml\Queue\Redis::class.(内置的redis服务与缓存挂钩)参考 http://doc.cmlphp.com/devintro/quenue.html //自定义服务实现\Cml\Interfaces\Queue接口即可或继承\Cml\Queue\Base再按需重载 Cml::getContainer()->singleton('cml_queue', \Cml\Queue\Redis::class); //可选,锁服务 内置\Cml\Lock\File::class|\Cml\Lock\Redis::class|\Cml\Lock\Memcache::class三种. //内置的redis锁跟/memcache锁 跟缓存服务挂钩。参考 http://doc.cmlphp.com/devintro/lock.html //自定义服务实现\Cml\Interfaces\Lock接口即可或继承\Cml\Lock\Base再按需重载 Cml::getContainer()->singleton('cml_lock', \Cml\Lock\Redis::class); //可选。绑定要用到视图引擎内置以下5种 以view_为前缀,用的时候不用带前缀如使用view_html视图服务: \Cml\View::getEngine('html'); //\Cml\View::getEngine();不传类型的时候,使用的引擎可在配置文件中配置 'view_render_engine' => 'Html'默认为view_html //自定义服务实现\Cml\Interfaces\View接口即可或继承\Cml\View\Base再按需重载 Cml::getContainer()->singleton('view_html', \Cml\View\Html::class); Cml::getContainer()->singleton('view_json', \Cml\View\Json::class); Cml::getContainer()->singleton('view_blade', \Cml\Service\Blade::class); //blade模板引擎,使用前安装依赖。composer require linhecheng/cmlphp-ext-blade //Cml::getContainer()->singleton('view_excel', \Cml\View\Excel::class); //Cml::getContainer()->singleton('view_xml', \Cml\View\Xml::class); //可选,db 允许多种驱动同时使用。因同种数据库可能同时连多个.这边不使用单例绑定.内置 \Cml\Db\MySql\Pdo::class|\Cml\Db\MongoDB\MongoDB::class 两种数据库支持. //自定义数据库驱动实现\Cml\Interfaces\Db接口即可或继承\Cml\Db\Base再按需重载 Cml::getContainer()->bind('db_mysql', \Cml\Db\MySql\Pdo::class); //Cml::getContainer()->bind('db_mongodb', \Cml\Db\MongoDB\MongoDB::class); //可选,cache 允许多种驱动同时使用。如即使用memcache又使用redis.有使用数据库时至少要启用一种缓存,因同种缓存可能同时连多个.这边不使用单例绑定。 // 内置 \Cml\Cache\Redis::class|\Cml\Cache\File::class | \Cml\Cache\Memcache::class | \Cml\Cache\Apc::class 四种缓存支持. //自定义数据库驱动实现\Cml\Interfaces\Cache接口即可或继承\Cml\Cache\Base再按需重载 Cml::getContainer()->bind('cache_redis', \Cml\Cache\Redis::class); Cml::getContainer()->bind('cache_file', \Cml\Cache\File::class); //Cml::getContainer()->bind('cache_memcache', \Cml\Cache\Memcache::class); //Cml::getContainer()->bind('cache_apc', \Cml\Cache\Memcache::class); });
/** * 初始化环境 * */ private static function initEvn() { if (!self::$pidFile) { self::$pidFile = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'DaemonProcess_.pid'; self::$log = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'DaemonProcess_.log'; self::$status = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . 'DaemonProcessStatus.php'; self::checkExtension(); } }