/** * 创建一个新的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)); }
/** * 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 string $templateFile 模板文件名 * * @return string */ private function initBaseDir($templateFile) { $baseDir = Cml::getContainer()->make('cml_route')->getAppName(); $baseDir .= '/' . Cml::getApplicationDir('app_view_path_name') . (Config::get('html_theme') != '' ? DIRECTORY_SEPARATOR . Config::get('html_theme') : ''); $layOutRootDir = $baseDir; if ($templateFile === '') { $baseDir .= '/' . Cml::getContainer()->make('cml_route')->getControllerName() . '/'; $file = Cml::getContainer()->make('cml_route')->getActionName(); } else { $templateFile = str_replace('.', '/', $templateFile); $baseDir .= DIRECTORY_SEPARATOR . dirname($templateFile) . DIRECTORY_SEPARATOR; $file = basename($templateFile); } return ['layoutDir' => Cml::getApplicationDir('apps_path') . DIRECTORY_SEPARATOR . $layOutRootDir, 'layoutCacheRootPath' => Cml::getApplicationDir('runtime_cache_path') . DIRECTORY_SEPARATOR . $layOutRootDir . DIRECTORY_SEPARATOR, 'templateDir' => Cml::getApplicationDir('apps_path') . DIRECTORY_SEPARATOR . $baseDir, 'cacheDir' => Cml::getApplicationDir('runtime_cache_path') . DIRECTORY_SEPARATOR . $baseDir, 'file' => $file]; }
/** * 获取要执行的控制器类名及方法 * */ 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; } }
/** * 初始化环境 * */ 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(); } }
/** * 获取缓存文件名 * * @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'; }
/** * 检查对应的权限 * * @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 array $pathInfo * @param string $path */ private function findAction(&$pathInfo, &$path) { $controllerPath = $controllerName = ''; $routeAppHierarchy = Config::get('route_app_hierarchy', 1); $i = 0; $controllerSuffix = Config::get('controller_suffix'); while ($dir = array_shift($pathInfo)) { $controllerName = ucfirst($dir); $controller = Cml::getApplicationDir('apps_path') . $path . Cml::getApplicationDir('app_controller_path_name') . '/' . $controllerPath . $controllerName . $controllerSuffix . '.php'; if ($i >= $routeAppHierarchy && is_file($controller)) { self::$urlParams['controller'] = $controllerPath . $controllerName; break; } else { if ($i++ < $routeAppHierarchy) { $path .= $dir . '/'; } else { $controllerPath .= $dir . '/'; } } } empty(self::$urlParams['controller']) && (self::$urlParams['controller'] = $controllerName); //用于404的时候挂载插件用 self::$urlParams['action'] = array_shift($pathInfo); }
/** * 构造方法 * */ 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() { $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; } }
/** * 从文件查找控制器 * * @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); }
/** * 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; } }
/** * 创建一个迁移 * * @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)); }
/** * 添加调试信息 * * @param string $msg 调试消息字符串 * @param int $type 消息的类型 * * @return void */ public static function addTipInfo($msg, $type = self::TIP_INFO_TYPE_INFO) { if (Cml::$debug) { switch ($type) { case self::TIP_INFO_TYPE_INFO: self::$tipInfo[] = $msg; break; case self::TIP_INFO_TYPE_INCLUDE_LIB: self::$includeLib[] = $msg; break; case self::TIP_INFO_TYPE_SQL: self::$sql[] = $msg; break; case self::TIP_INFO_TYPE_INCLUDE_FILE: self::$includeFile[] = str_replace('\\', '/', str_replace([Cml::getApplicationDir('secure_src'), CML_PATH], ['{secure_src}', '{cmlphp_src}'], $msg)); break; } } }
/** * 解析一个静态资源的内容 * */ public static function parseResourceFile() { if (Cml::$debug) { $pathInfo = Route::getPathInfo(); array_shift($pathInfo); $resource = implode('/', $pathInfo); $appName = $file = ''; $i = 0; $routeAppHierarchy = Config::get('route_app_hierarchy', 1); while (true) { $resource = ltrim($resource, '/'); $pos = strpos($resource, '/'); $appName = ($appName == '' ? '' : $appName . DIRECTORY_SEPARATOR) . substr($resource, 0, $pos); $resource = substr($resource, $pos); $file = Cml::getApplicationDir('apps_path') . DIRECTORY_SEPARATOR . $appName . DIRECTORY_SEPARATOR . Cml::getApplicationDir('app_static_path_name') . $resource; if (is_file($file) || ++$i >= $routeAppHierarchy) { break; } } if (is_file($file)) { Response::sendContentTypeBySubFix(substr($resource, strrpos($resource, '.') + 1)); exit(file_get_contents($file)); } else { Response::sendHttpStatus(404); } } }
/** * 初始化运行环境 * * @param callable $initDi 注入依赖 */ private static function init($initDi) { define('CML_PATH', dirname(__DIR__)); //框架的路径 define('CML_CORE_PATH', CML_PATH . DIRECTORY_SEPARATOR . 'Cml'); // 系统核心类库目录 define('CML_EXTEND_PATH', CML_PATH . DIRECTORY_SEPARATOR . 'Vendor'); // 系统扩展类库目录 self::handleConfigLang(); //后面自动载入的类都会自动收集到Debug类下 spl_autoload_register('Cml\\Cml::autoloadComposerAdditional', true, true); //初始化依赖 $initDi(); //包含框架中的框架函数库文件 Cml::requireFile(CML_CORE_PATH . DIRECTORY_SEPARATOR . 'Tools' . DIRECTORY_SEPARATOR . 'functions.php'); //设置自定义捕获致命异常函数 //普通错误由Cml\Debug::catcher捕获 php默认在display_errors为On时致命错误直接输出 为off时 直接显示服务器错误或空白页,体验不好 register_shutdown_function(function () { if ($error = error_get_last()) { //获取最后一个发生的错误的信息。 包括提醒、警告、致命错误 if (in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING])) { //当捕获到的错误为致命错误时 报告 if (Plugin::hook('cml.before_fatal_error', $error) == 'jump') { return; } Cml::getContainer()->make('cml_error_or_exception')->fatalError($error); Plugin::hook('cml.after_fatal_error', $error); } } Plugin::hook('cml.before_cml_stop'); }); //捕获致命异常 //设置自定义的异常处理函数。 set_exception_handler(function ($e) { if (Plugin::hook('cml.before_throw_exception', $e) === 'resume') { return; } Cml::getContainer()->make('cml_error_or_exception')->appException($e); }); //手动抛出的异常由此函数捕获 ini_set('display_errors', 'off'); //屏蔽系统自带的错误输出 //载入插件配置文件 $pluginConfig = Cml::getApplicationDir('global_config_path') . DIRECTORY_SEPARATOR . 'plugin.php'; is_file($pluginConfig) && Cml::requireFile($pluginConfig); Plugin::hook('cml.before_set_time_zone'); //用于动态设置时区等。 date_default_timezone_set(Config::get('time_zone')); //设置时区 self::$nowTime = time(); self::$nowMicroTime = microtime(true); //全局的自定义语言包 $globalLang = Cml::getApplicationDir('global_lang_path') . DIRECTORY_SEPARATOR . Config::get('lang') . '.php'; is_file($globalLang) && Lang::set(Cml::requireFile($globalLang)); //设置调试模式 if (Cml::$debug) { Debug::start(); //记录开始运行时间\内存初始使用 //设置捕获系统异常 使用set_error_handler()后,error_reporting将会失效。所有的错误都会交给set_error_handler。 set_error_handler('\\Cml\\Debug::catcher'); Debug::addTipInfo(Lang::get('_CML_DEBUG_ADD_CLASS_TIP_', 'Cml\\Cml'), Debug::TIP_INFO_TYPE_INCLUDE_LIB); Debug::addTipInfo(Lang::get('_CML_DEBUG_ADD_CLASS_TIP_', 'Cml\\Config'), Debug::TIP_INFO_TYPE_INCLUDE_LIB); Debug::addTipInfo(Lang::get('_CML_DEBUG_ADD_CLASS_TIP_', 'Cml\\Lang'), Debug::TIP_INFO_TYPE_INCLUDE_LIB); Debug::addTipInfo(Lang::get('_CML_DEBUG_ADD_CLASS_TIP_', 'Cml\\Http\\Request'), Debug::TIP_INFO_TYPE_INCLUDE_LIB); Debug::addTipInfo(Lang::get('_CML_DEBUG_ADD_CLASS_TIP_', 'Cml\\Debug'), Debug::TIP_INFO_TYPE_INCLUDE_LIB); $runTimeClassList = null; } else { $GLOBALS['debug'] = false; //关闭debug //ini_set('error_reporting', E_ALL & ~E_NOTICE);//记录除了notice之外的错误 ini_set('log_errors', 'off'); //关闭php自带错误日志 //严重错误已经通过fatalError记录。为了防止日志过多,默认不记录致命错误以外的日志。有需要可以修改配置开启 if (Config::get('log_warn_log')) { set_error_handler('\\Cml\\Log::catcherPhpError'); } //线上模式包含runtime.php $runTimeFile = Cml::getApplicationDir('global_store_path') . DIRECTORY_SEPARATOR . '_runtime_.php'; if (!is_file($runTimeFile)) { //程序运行必须的类 $runTimeClassList = [CML_CORE_PATH . DIRECTORY_SEPARATOR . 'Controller.php', CML_CORE_PATH . DIRECTORY_SEPARATOR . 'Http' . DIRECTORY_SEPARATOR . 'Response.php', CML_CORE_PATH . DIRECTORY_SEPARATOR . 'Route.php', CML_CORE_PATH . DIRECTORY_SEPARATOR . 'Secure.php']; Config::get('session_user') && ($runTimeClassList[] = CML_CORE_PATH . DIRECTORY_SEPARATOR . 'Session.php'); $runTimeContent = '<?php'; foreach ($runTimeClassList as $file) { $runTimeContent .= str_replace(['<?php', '?>'], '', php_strip_whitespace($file)); } file_put_contents($runTimeFile, $runTimeContent, LOCK_EX); $runTimeContent = null; } Cml::requireFile($runTimeFile); } if (Request::isCli()) { RunCliCommand::runCliCommand(); } else { header('X-Powered-By:CmlPHP'); // 页面压缩输出支持 if (Config::get('output_encode')) { $zlib = ini_get('zlib.output_compression'); if (empty($zlib)) { ///@ob_end_clean () ; //防止在启动ob_start()之前程序已经有输出(比如配置文件尾多敲了换行)会导致服务器303错误 ob_start('ob_gzhandler') || ob_start(); define('CML_OB_START', true); } else { define('CML_OB_START', false); } } } Plugin::hook('cml.before_parse_url'); //载入路由 $routeConfigFile = Cml::getApplicationDir('global_config_path') . DIRECTORY_SEPARATOR . 'route.php'; is_file($routeConfigFile) && Cml::requireFile($routeConfigFile); Cml::getContainer()->make('cml_route')->parseUrl(); //解析处理URL Plugin::hook('cml.after_parse_url'); //载入模块配置 $modulesConfig = Cml::getApplicationDir('apps_path') . '/' . Cml::getContainer()->make('cml_route')->getAppName() . '/' . Cml::getApplicationDir('app_config_path_name') . '/' . 'normal.php'; is_file($modulesConfig) && Config::set(Cml::requireFile($modulesConfig)); //载入模块语言包 $appLang = Cml::getApplicationDir('apps_path') . '/' . Cml::getContainer()->make('cml_route')->getAppName() . '/' . Cml::getApplicationDir('app_lang_path_name') . '/' . Config::get('lang') . '.php'; is_file($appLang) && Lang::set(Cml::requireFile($appLang)); }
/** * 使用的缓存配置 默认为使用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); }
/** * 快速文件数据读取和保存 针对简单类型数据 字符串、数组 * * @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; }
/** * 从文件载入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]; } }
<?php //应用入口 use Cml\Cml; //定义项目根目录 define('CML_PROJECT_PATH', dirname(__DIR__)); //载入composer自动加载文件 $loader = (require CML_PROJECT_PATH . DIRECTORY_SEPARATOR . 'vendor/autoload.php'); //配置目录组成 Cml::setApplicationDir(['secure_src' => CML_PROJECT_PATH . DIRECTORY_SEPARATOR . 'proj3e5f9e47cd31239b6fd43a772c5a75b4', 'app_config_path_name' => 'Config', 'app_lang_path_name' => 'Lang', 'app_view_path_name' => 'View', 'app_controller_path_name' => 'Controller', 'app_static_path_name' => 'Resource']); //根据上面配置的目录,配置其它目录 Cml::setApplicationDir(['apps_path' => Cml::getApplicationDir('secure_src') . '/Application', 'global_config_path' => Cml::getApplicationDir('secure_src') . '/' . Cml::getApplicationDir('app_config_path_name'), 'global_lang_path' => Cml::getApplicationDir('secure_src') . '/' . Cml::getApplicationDir('app_lang_path_name'), 'global_store_path' => Cml::getApplicationDir('secure_src') . '/Store', 'runtime_cache_path' => Cml::getApplicationDir('secure_src') . '/Store' . DIRECTORY_SEPARATOR . 'Cache', 'runtime_logs_path' => Cml::getApplicationDir('secure_src') . '/Store' . DIRECTORY_SEPARATOR . 'Logs']); $loader->setPsr4('', Cml::getApplicationDir('apps_path')); //注入服务并运行应用 //要注意的是这边只是做绑定并没有真正实例化 Cml::runApp(function () { /*********注意********** * 框架只要求php5.4+即可。但是下面用了php5.5的语法::class, * 如果php版本不支持::class的语法直接把相应的xxx::class改成字符串即可。 * 如\Cml\ErrorOrException::class直接改成'\Cml\ErrorOrException' ***********************/ //必须绑定。系统错误及异常捕获机制 如果想使用第三方的服务只要简单封装一个服务。实现\Cml\Interfaces\ErrorOrException接口即可 Cml::getContainer()->singleton('cml_error_or_exception', \Cml\ErrorOrException::class); //Cml::getContainer()->singleton('cml_error_or_exception', \Cml\Service\Whoops::class);//Whoops封装服务使用前请安装Whoops. composer require filp/whoops //必须绑定。环境解析。自带的服务实现development/product/cli三种。可以根据需要实现更多的环境 Cml::getContainer()->singleton('cml_environment', \Cml\Service\Environment::class); //必须绑定。系统日志驱动 内置\Cml\Logger\File::class|\Cml\Logger\Redis::class两种. //自定义服务实现\Cml\Interfaces\Logger接口即可或继承\Cml\Logger\Base再按需重载 Cml::getContainer()->singleton('cml_log', \Cml\Logger\File::class); //必须绑定。路由 //框架自带的路由支持restful分格的路由、路由分组。 在未声明/未匹配到路由规则时会按url映射到文件的方式来执行相应的控制器方法。具体参考 http://doc.cmlphp.com/devintro/route/readme.html。
/** * 载入应用单独的路由 * * @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); }