CodeEngine.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | saiadmin [ saiadmin快速开发框架 ]
  4. // +----------------------------------------------------------------------
  5. // | Author: sai <1430792918@qq.com>
  6. // +----------------------------------------------------------------------
  7. namespace plugin\saiadmin\utils\code;
  8. use Twig\TwigFilter;
  9. use Twig\Environment;
  10. use Twig\Loader\FilesystemLoader;
  11. use plugin\saiadmin\exception\ApiException;
  12. // 定义目录分隔符常量
  13. defined('DS') or define('DS', DIRECTORY_SEPARATOR);
  14. /**
  15. * 代码生成引擎
  16. */
  17. class CodeEngine
  18. {
  19. /**
  20. * @var array 值栈
  21. */
  22. private array $value = [];
  23. /**
  24. * 模板名称
  25. * @var string
  26. */
  27. private string $stub = 'saiadmin';
  28. /**
  29. * 获取配置文件
  30. * @return string[]
  31. */
  32. private static function _getConfig(): array
  33. {
  34. return [
  35. 'template_path' => base_path() . DS . 'plugin' . DS . 'saiadmin' . DS . 'utils' . DS . 'code' . DS . 'stub',
  36. 'generate_path' => runtime_path() . DS . 'code_engine' . DS . 'saiadmin',
  37. ];
  38. }
  39. /**
  40. * 初始化
  41. * @param array $data 数据
  42. */
  43. public function __construct(array $data)
  44. {
  45. // 读取配置文件
  46. $config = self::_getConfig();
  47. // 判断模板是否存在
  48. if (!is_dir($config['template_path'])) {
  49. throw new ApiException('模板目录不存在!');
  50. }
  51. // 判断文件生成目录是否存在
  52. if (!is_dir($config['generate_path'])) {
  53. mkdir($config['generate_path'], 0770, true);
  54. }
  55. // 赋值
  56. $this->value = $data;
  57. }
  58. /**
  59. * 设置模板名称
  60. * @param $stub
  61. * @return void
  62. */
  63. public function setStub($stub): void
  64. {
  65. $this->stub = $stub;
  66. }
  67. /**
  68. * 渲染文件内容
  69. */
  70. public function renderContent($path, $filename): string
  71. {
  72. $config = self::_getConfig();
  73. $path = $config['template_path'] . DS . $this->stub . DS . $path;
  74. $loader = new FilesystemLoader($path);
  75. $twig = new Environment($loader);
  76. $camelFilter = new TwigFilter('camel', function ($value) {
  77. static $cache = [];
  78. $key = $value;
  79. if (isset($cache[$key])) {
  80. return $cache[$key];
  81. }
  82. $value = ucwords(str_replace(['-', '_'], ' ', $value));
  83. return $cache[$key] = str_replace(' ', '', $value);
  84. });
  85. $boolFilter = new TwigFilter('bool', function ($value) {
  86. if ($value == 1) {
  87. return 'true';
  88. } else {
  89. return 'false';
  90. }
  91. });
  92. $formatFilter = new TwigFilter('formatNumber', function ($value) {
  93. if (ctype_digit((string) $value)) {
  94. return $value;
  95. } else {
  96. return '1';
  97. }
  98. });
  99. $defaultFilter = new TwigFilter('parseNumber', function ($value) {
  100. if (\is_numeric($value)) {
  101. return $value;
  102. } else {
  103. return 'null';
  104. }
  105. });
  106. $containsFilter = new TwigFilter('str_contains', function ($haystack, $needle) {
  107. return str_contains($haystack ?? '', $needle ?? '');
  108. });
  109. $twig->addFilter($camelFilter);
  110. $twig->addFilter($boolFilter);
  111. $twig->addFilter($containsFilter);
  112. $twig->addFilter($formatFilter);
  113. $twig->addFilter($defaultFilter);
  114. return $twig->render($filename, $this->value);
  115. }
  116. /**
  117. * 生成后端文件
  118. */
  119. public function generateBackend($action, $content): void
  120. {
  121. $outPath = '';
  122. if ($this->value['template'] == 'app') {
  123. $rootPath = base_path() . DS . 'app' . DS . $this->value['namespace'];
  124. $adminPath = '';
  125. } else {
  126. $rootPath = base_path() . DS . 'plugin' . DS . $this->value['namespace'] . DS . 'app';
  127. $adminPath = DS . 'admin';
  128. }
  129. $subPath = DS . $this->value['package_name'];
  130. switch ($action) {
  131. case 'controller':
  132. $outPath = $rootPath . $adminPath . DS . 'controller' . $subPath . DS . $this->value['class_name'] . 'Controller.php';
  133. break;
  134. case 'logic':
  135. $outPath = $rootPath . $adminPath . DS . 'logic' . $subPath . DS . $this->value['class_name'] . 'Logic.php';
  136. break;
  137. case 'validate':
  138. $outPath = $rootPath . $adminPath . DS . 'validate' . $subPath . DS . $this->value['class_name'] . 'Validate.php';
  139. break;
  140. case 'model':
  141. $outPath = $rootPath . DS . 'model' . $subPath . DS . $this->value['class_name'] . '.php';
  142. break;
  143. default:
  144. break;
  145. }
  146. if (empty($outPath)) {
  147. throw new ApiException('文件类型异常,无法生成指定文件!');
  148. }
  149. if (!is_dir(dirname($outPath))) {
  150. mkdir(dirname($outPath), 0777, true);
  151. }
  152. file_put_contents($outPath, $content);
  153. }
  154. /**
  155. * 生成前端文件
  156. */
  157. public function generateFrontend($action, $content): void
  158. {
  159. $rootPath = dirname(base_path()) . DS . $this->value['generate_path'];
  160. if (!is_dir($rootPath)) {
  161. throw new ApiException('前端目录查找失败,必须与后端目录为同级目录!');
  162. }
  163. $rootPath = $rootPath . DS . 'src' . DS . 'views' . DS . 'plugin' . DS . $this->value['namespace'];
  164. $subPath = DS . $this->value['package_name'];
  165. switch ($action) {
  166. case 'index':
  167. $outPath = $rootPath . $subPath . DS . $this->value['business_name'] . DS . 'index.vue';
  168. break;
  169. case 'edit-dialog':
  170. $outPath = $rootPath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'edit-dialog.vue';
  171. break;
  172. case 'table-search':
  173. $outPath = $rootPath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'table-search.vue';
  174. break;
  175. case 'api':
  176. $outPath = $rootPath . DS . 'api' . $subPath . DS . $this->value['business_name'] . '.ts';
  177. break;
  178. default:
  179. break;
  180. }
  181. if (empty($outPath)) {
  182. throw new ApiException('文件类型异常,无法生成指定文件!');
  183. }
  184. if (!is_dir(dirname($outPath))) {
  185. mkdir(dirname($outPath), 0777, true);
  186. }
  187. file_put_contents($outPath, $content);
  188. }
  189. /**
  190. * 生成临时文件
  191. */
  192. public function generateTemp(): void
  193. {
  194. $config = self::_getConfig();
  195. $rootPath = $config['generate_path'];
  196. $vuePath = $rootPath . DS . 'vue' . DS . 'src' . DS . 'views' . DS . 'plugin' . DS . $this->value['namespace'];
  197. $phpPath = $rootPath . DS . 'php';
  198. $sqlPath = $rootPath . DS . 'sql';
  199. if ($this->value['template'] == 'app') {
  200. $phpPath = $phpPath . DS . 'app' . DS . $this->value['namespace'];
  201. $adminPath = '';
  202. } else {
  203. $phpPath = $phpPath . DS . 'plugin' . DS . $this->value['namespace'] . DS . 'app';
  204. $adminPath = DS . 'admin';
  205. }
  206. $subPath = DS . $this->value['package_name'];
  207. $indexOutPath = $vuePath . $subPath . DS . $this->value['business_name'] . DS . 'index.vue';
  208. $this->checkPath($indexOutPath);
  209. $indexContent = $this->renderContent('vue', 'index.stub');
  210. file_put_contents($indexOutPath, $indexContent);
  211. $editOutPath = $vuePath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'edit-dialog.vue';
  212. $this->checkPath($editOutPath);
  213. $editContent = $this->renderContent('vue', 'edit-dialog.stub');
  214. file_put_contents($editOutPath, $editContent);
  215. $searchOutPath = $vuePath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'table-search.vue';
  216. $this->checkPath($searchOutPath);
  217. $searchContent = $this->renderContent('vue', 'table-search.stub');
  218. file_put_contents($searchOutPath, $searchContent);
  219. $viewOutPath = $vuePath . DS . 'api' . $subPath . DS . $this->value['business_name'] . '.ts';
  220. $this->checkPath($viewOutPath);
  221. $viewContent = $this->renderContent('ts', 'api.stub');
  222. file_put_contents($viewOutPath, $viewContent);
  223. $controllerOutPath = $phpPath . $adminPath . DS . 'controller' . $subPath . DS . $this->value['class_name'] . 'Controller.php';
  224. $this->checkPath($controllerOutPath);
  225. $controllerContent = $this->renderContent('php', 'controller.stub');
  226. file_put_contents($controllerOutPath, $controllerContent);
  227. $logicOutPath = $phpPath . $adminPath . DS . 'logic' . $subPath . DS . $this->value['class_name'] . 'Logic.php';
  228. $this->checkPath($logicOutPath);
  229. $logicContent = $this->renderContent('php', 'logic.stub');
  230. file_put_contents($logicOutPath, $logicContent);
  231. $validateOutPath = $phpPath . $adminPath . DS . 'validate' . $subPath . DS . $this->value['class_name'] . 'Validate.php';
  232. $this->checkPath($validateOutPath);
  233. $validateContent = $this->renderContent('php', 'validate.stub');
  234. file_put_contents($validateOutPath, $validateContent);
  235. $modelOutPath = $phpPath . DS . 'model' . $subPath . DS . $this->value['class_name'] . '.php';
  236. $this->checkPath($modelOutPath);
  237. $modelContent = $this->renderContent('php', 'model.stub');
  238. file_put_contents($modelOutPath, $modelContent);
  239. $sqlOutPath = $sqlPath . DS . 'sql.sql';
  240. $this->checkPath($sqlOutPath);
  241. $sqlContent = $this->renderContent('sql', 'sql.stub');
  242. file_put_contents($sqlOutPath, $sqlContent);
  243. }
  244. /**
  245. * 检查并生成路径
  246. * @param $path
  247. * @return void
  248. */
  249. protected function checkPath($path): void
  250. {
  251. if (!is_dir(dirname($path))) {
  252. mkdir(dirname($path), 0777, true);
  253. }
  254. }
  255. }