// +---------------------------------------------------------------------- namespace think; class Build { /** * 应用对象 * @var App */ protected $app; /** * 应用目录 * @var string */ protected $basePath; public function __construct(App $app) { $this->app = $app; $this->basePath = $this->app->getAppPath(); } /** * 根据传入的build资料创建目录和文件 * @access public * @param array $build build列表 * @param string $namespace 应用类库命名空间 * @param bool $suffix 类库后缀 * @return void */ public function run(array $build = [], $namespace = 'app', $suffix = false) { // 锁定 $lockfile = $this->basePath . 'build.lock'; if (is_writable($lockfile)) { return; } elseif (!touch($lockfile)) { throw new Exception('应用目录[' . $this->basePath . ']不可写,目录无法自动生成!
请手动生成项目目录~', 10006); } foreach ($build as $module => $list) { if ('__dir__' == $module) { // 创建目录列表 $this->buildDir($list); } elseif ('__file__' == $module) { // 创建文件列表 $this->buildFile($list); } else { // 创建模块 $this->module($module, $list, $namespace, $suffix); } } // 解除锁定 unlink($lockfile); } /** * 创建目录 * @access protected * @param array $list 目录列表 * @return void */ protected function buildDir($list) { foreach ($list as $dir) { $this->checkDirBuild($this->basePath . $dir); } } /** * 创建文件 * @access protected * @param array $list 文件列表 * @return void */ protected function buildFile($list) { foreach ($list as $file) { if (!is_dir($this->basePath . dirname($file))) { // 创建目录 mkdir($this->basePath . dirname($file), 0755, true); } if (!is_file($this->basePath . $file)) { file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "basePath . $module)) { // 创建模块目录 mkdir($this->basePath . $module); } if (basename($this->app->getRuntimePath()) != $module) { // 创建配置文件和公共文件 $this->buildCommon($module); // 创建模块的默认页面 $this->buildHello($module, $namespace, $suffix); } if (empty($list)) { // 创建默认的模块目录和文件 $list = [ '__file__' => ['common.php'], '__dir__' => ['controller', 'model', 'view', 'config'], ]; } // 创建子目录和文件 foreach ($list as $path => $file) { $modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR; if ('__dir__' == $path) { // 生成子目录 foreach ($file as $dir) { $this->checkDirBuild($modulePath . $dir); } } elseif ('__file__' == $path) { // 生成(空白)文件 foreach ($file as $name) { if (!is_file($modulePath . $name)) { file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "checkDirBuild(dirname($filename)); $content = ''; break; default: // 其他文件 $content = "app->getNameSpace(); $content = 'app->config('app.url_controller_layer'); } if ($this->app->config('app.app_multi_module')) { $modules = glob($this->basePath . '*', GLOB_ONLYDIR); foreach ($modules as $module) { $module = basename($module); if (in_array($module, $this->app->config('app.deny_module_list'))) { continue; } $path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR; $content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer); } } else { $path = $this->basePath . $layer . DIRECTORY_SEPARATOR; $content .= $this->buildDirRoute($path, $namespace, '', $suffix, $layer); } $filename = $this->app->getRuntimePath() . 'build_route.php'; file_put_contents($filename, $content); return $filename; } /** * 生成子目录控制器类的路由规则 * @access protected * @param string $path 控制器目录 * @param string $namespace 应用命名空间 * @param string $module 模块 * @param bool $suffix 类库后缀 * @param string $layer 控制器层目录名 * @return string */ protected function buildDirRoute($path, $namespace, $module, $suffix, $layer) { $content = ''; $controllers = glob($path . '*.php'); foreach ($controllers as $controller) { $controller = basename($controller, '.php'); $class = new \ReflectionClass($namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $controller); if (strpos($layer, '\\')) { // 多级控制器 $level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11)); $controller = $level . '.' . $controller; $length = strlen(strstr($layer, '\\', true)); } else { $length = strlen($layer); } if ($suffix) { $controller = substr($controller, 0, -$length); } $content .= $this->getControllerRoute($class, $module, $controller); } $subDir = glob($path . '*', GLOB_ONLYDIR); foreach ($subDir as $dir) { $content .= $this->buildDirRoute($dir . DIRECTORY_SEPARATOR, $namespace, $module, $suffix, $layer . '\\' . basename($dir)); } return $content; } /** * 生成控制器类的路由规则 * @access protected * @param string $class 控制器完整类名 * @param string $module 模块名 * @param string $controller 控制器名 * @return string */ protected function getControllerRoute($class, $module, $controller) { $content = ''; $comment = $class->getDocComment(); if (false !== strpos($comment, '@route(')) { $comment = $this->parseRouteComment($comment); $route = ($module ? $module . '/' : '') . $controller; $comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment); $content .= PHP_EOL . $comment; } elseif (false !== strpos($comment, '@alias(')) { $comment = $this->parseRouteComment($comment, '@alias('); $route = ($module ? $module . '/' : '') . $controller; $comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment); $content .= PHP_EOL . $comment; } $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { $comment = $this->getMethodRouteComment($module, $controller, $method); if ($comment) { $content .= PHP_EOL . $comment; } } return $content; } /** * 解析路由注释 * @access protected * @param string $comment * @param string $tag * @return string */ protected function parseRouteComment($comment, $tag = '@route(') { $comment = substr($comment, 3, -2); $comment = explode(PHP_EOL, substr(strstr(trim($comment), $tag), 1)); $comment = array_map(function ($item) {return trim(trim($item), ' \t*');}, $comment); if (count($comment) > 1) { $key = array_search('', $comment); $comment = array_slice($comment, 0, false === $key ? 1 : $key); } $comment = implode(PHP_EOL . "\t", $comment) . ';'; if (strpos($comment, '{')) { $comment = preg_replace_callback('/\{\s?.*?\s?\}/s', function ($matches) { return false !== strpos($matches[0], '"') ? '[' . substr(var_export(json_decode($matches[0], true), true), 7, -1) . ']' : $matches[0]; }, $comment); } return $comment; } /** * 获取方法的路由注释 * @access protected * @param string $module 模块 * @param string $controller 控制器名 * @param \ReflectMethod $reflectMethod * @return string|void */ protected function getMethodRouteComment($module, $controller, $reflectMethod) { $comment = $reflectMethod->getDocComment(); if (false !== strpos($comment, '@route(')) { $comment = $this->parseRouteComment($comment); $action = $reflectMethod->getName(); if ($suffix = $this->app->config('app.action_suffix')) { $action = substr($action, 0, -strlen($suffix)); } $route = ($module ? $module . '/' : '') . $controller . '/' . $action; $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment); $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment); return $comment; } } /** * 创建模块的欢迎页面 * @access protected * @param string $module 模块名 * @param string $namespace 应用类库命名空间 * @param bool $suffix 类库后缀 * @return void */ protected function buildHello($module, $namespace, $suffix = false) { $filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php'; if (!is_file($filename)) { $content = file_get_contents($this->app->getThinkPath() . 'tpl' . DIRECTORY_SEPARATOR . 'default_index.tpl'); $content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content); $this->checkDirBuild(dirname($filename)); file_put_contents($filename, $content); } } /** * 创建模块的公共文件 * @access protected * @param string $module 模块名 * @return void */ protected function buildCommon($module) { $filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php'; $this->checkDirBuild(dirname($filename)); if (!is_file($filename)) { file_put_contents($filename, "basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php'; if (!is_file($filename)) { file_put_contents($filename, "