diff --git a/Gii.php b/Gii.php new file mode 100644 index 0000000..409f1de --- /dev/null +++ b/Gii.php @@ -0,0 +1,340 @@ +gen($input, $db); + } + + + /** + * @param InputInterface $input + * @param $db + * @return array + * @throws Exception + */ + public function gen(InputInterface $input, $db): array + { + $this->input = $input; + $this->db = $db; + + $make = $this->input->getOption('make'); + if (empty($make)) { + throw new Exception('构建类型不能为空~'); + } + switch (strtolower($make)) { + case 'task': + $task = new GiiTask(); + $task->setInput($this->input); + return $task->generate(); + case 'middleware': + $task = new GiiMiddleware(); + $task->setInput($this->input); + return $task->generate(); + case 'rpc-client': + $task = new GiiRpcClient(); + $task->setInput($this->input); + return $task->generate(); + case 'rpc-service': + $task = new GiiRpcService(); + $task->setInput($this->input); + return $task->generate(); + case 'json-rpc': + $task = new GiiJsonRpc(); + $task->setInput($this->input); + return $task->create(); + default: + return $this->getModel($make, $input); + } + } + + + /** + * @param $make + * @param $input + * @return array + * @throws Exception + */ + private function getModel($make, $input): array + { + return $this->makeByDatabases($make, $input); + } + + + /** + * @param $make + * @param InputInterface $input + * @return array + * @throws Exception + */ + private function makeByDatabases($make, InputInterface $input): array + { + if ($input->hasOption('name')) { + $this->tableName = $input->getOption('name'); + } + return match ($make) { + 'controller' => $this->getTable(1, 0), + 'model' => $this->getTable(0, 1), + default => [], + }; + } + + + /** + * @param $controller + * @param $model + * @return array + * + * @throws Exception + */ + private function getTable($controller, $model): array + { + $tables = $this->getFields($this->getTables()); + if (empty($tables)) { + return []; + } + + $fileList = []; + foreach ($tables as $key => $val) { + $data = $this->createModelFile($key, $val); + if ($controller == 1) { + $fileList[] = $this->generateController($data); + } + if ($model == 1) { + $fileList[] = $this->generateModel($data); + } + } + return $fileList; + } + + /** + * @param array $data + * @return string + * @throws Exception + */ + private function generateModel(array $data): string + { + $controller = new GiiModel($data['classFileName'], $data['tableName'], $data['visible'], $data['res'], $data['fields']); + $controller->setConnection($this->db); + $controller->setModelPath($this->modelPath); + $controller->setModelNamespace($this->modelNamespace); + $controller->setInput($this->input); +// $controller->setModule($this->input->getArgument('module')); + $controller->setControllerPath($this->controllerPath); + $controller->setControllerNamespace($this->controllerNamespace); + return $controller->generate(); + } + + /** + * @param array $data + * @return string + * @throws Exception + */ + private function generateController(array $data): string + { + $controller = new GiiController($data['classFileName'], $data['fields']); + $controller->setConnection($this->db); + $controller->setModelPath($this->modelPath); + $controller->setInput($this->input); + $controller->setModelNamespace($this->modelNamespace); + $controller->setControllerPath($this->controllerPath); +// $controller->setModule($this->input->getArgument('module')); + $controller->setControllerNamespace($this->controllerNamespace); + return $controller->generate(); + } + + /** + * @return array|string|null + * @throws Exception + */ + private function getTables(): array|string|null + { + if (empty($this->tableName)) { + return $this->showAll(); + } + $res = $this->tableName; + if (is_string($res)) { + $res = explode(',', $this->tableName); + } + if (empty($res)) { + return []; + } + return $res; + } + + /** + * @return array + * @throws Exception + */ + private function showAll(): array + { + $res = []; + $_tables = Db::findAllBySql('show tables from `' . $this->db->database . '`', [], $this->db); + if (empty($_tables)) { + return $res; + } + foreach ($_tables as $key => $val) { + $res[] = array_shift($val); + } + return $res; + } + + /** + * @param $table + * @return bool|int|null + * @throws Exception + */ + private function getIndex($table): bool|int|null + { + $data = Db::findAllBySql('SHOW INDEX FROM ' . $table, [], $this->db); + + return empty($data) ? NULL : $data[0]; + } + + /** + * @param $tables + * + * @return array + * @throws + */ + private function getFields($tables): array + { + $res = []; + if (!is_array($tables)) { + $tables = [$tables]; + } + foreach ($tables as $key => $val) { + if (empty($val)) continue; + $_tmp = Db::findAllBySql('SHOW FULL FIELDS FROM `' . $this->db->database . '`.' . $val, [], $this->db); + if (empty($_tmp)) { + continue; + } + $res[$val] = $_tmp; + } + return $res; + } + + /** + * @param $tableName + * @param $tables + * + * @return array + * @throws Exception + */ + public function createModelFile($tableName, $tables): array + { + $res = $visible = $fields = $keys = []; + foreach ($tables as $_key => $_val) { + $keys = $tableName; + if ($_val['Extra'] == 'auto_increment' || $_val['Key'] == 'PRI') { + $keys = $tableName; + } + if (!isset($keys) && !($index = $this->getIndex($tableName))) { + $keys = $index['Column_name']; + } + if (in_array(strtoupper($_val['Field']), $this->keyword)) { + throw new Exception('You can not use keyword "' . $_val['Field'] . '" as field at table "' . $tableName . '"'); + } + array_push($visible, $this->createVisible($_val['Field'])); + array_push($fields, $_val); + $res[] = $this->createSetFunc($_val['Field'], $_val['Comment']); + } + + $classFileName = $this->getClassName($tableName); + + return [ + 'classFileName' => $classFileName, + 'tableName' => $keys, + 'visible' => $visible, + 'fields' => $fields, + 'res' => $res, + ]; + } + + /** + * @param $field + * @return string + * 创建变量注释 + */ + private function createVisible($field): string + { + return ' + * @property $' . $field; + } + + /** + * @param $field + * @param $comment + * @return string + * 暂时不知道干嘛用的 + */ + private function createSetFunc($field, $comment): string + { + return ' + ' . str_pad('\'' . $field . '\'', 20, ' ', STR_PAD_RIGHT) . '=> \'' . (empty($comment) ? ucfirst($field) : $comment) . '\','; + } + + /** + * @param $tableName + * @return string + * 构建类名称 + */ + private function getClassName($tableName): string + { + $res = []; + $tableName = str_replace($this->db->tablePrefix,'', $tableName); + foreach (explode('_', $tableName) as $n => $val) { + $res[] = ucfirst($val); + } + return implode('', $res); + } + +} diff --git a/GiiBase.php b/GiiBase.php new file mode 100644 index 0000000..a7d970f --- /dev/null +++ b/GiiBase.php @@ -0,0 +1,412 @@ + ['tinyint', 'smallint', 'mediumint', 'int', 'bigint'], + 'string' => ['char', 'varchar', 'tinytext', 'text', 'mediumtext', 'longtext', 'enum'], + 'date' => ['date'], + 'time' => ['time'], + 'year' => ['year'], + 'datetime' => ['datetime'], + 'timestamp' => ['timestamp'], + 'float' => ['float', 'double', 'decimal',], + ]; + public ?string $tableName = NULL; + + public ?Connection $db = null; + + /** + * @param string $modelPath + */ + public function setModelPath(string $modelPath): void + { + $this->modelPath = $modelPath; + } + + /** + * @param string $modelNamespace + */ + public function setModelNamespace(string $modelNamespace): void + { + $this->modelNamespace = $modelNamespace; + } + + /** + * @param string $controllerPath + */ + public function setControllerPath(string $controllerPath): void + { + $this->controllerPath = $controllerPath; + } + + /** + * @param $module + */ + public function setModule($module) + { + $this->module = $module; + } + + /** + * @param string $controllerNamespace + */ + public function setControllerNamespace(string $controllerNamespace): void + { + $this->controllerNamespace = $controllerNamespace; + } + + + /** + * @param InputInterface $input + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } + + + /** + * @param ReflectionClass $object + * @param $className + * + * @return string + */ + public function getUseContent(ReflectionClass $object, $className): string + { + if (empty($object)) { + return ''; + } + $file = $this->getFilePath($className); + if (!file_exists($file)) { + return ''; + } + $content = file_get_contents($file); + $explode = explode(PHP_EOL, $content); + $exists = array_slice($explode, 0, $object->getStartLine()); + $_tmp = []; + foreach ($exists as $key => $val) { + if (trim($val) == '/**') { + break; + } + $_tmp[] = $val; + } + return trim(implode(PHP_EOL, $_tmp)); + } + + + /** + * @param string $fileName + * @param ReflectionClass $class + * @return string + */ + protected function getImports(string $fileName, ReflectionClass $class): string + { + $startLine = 1; + $array = []; + $fileOpen = fopen($fileName, 'r'); + while (($content = fgets($fileOpen)) !== false) { + if (str_starts_with($content, 'use ')) { + $array[] = $content; + } + if ($startLine == $class->getStartLine()) { + break; + } + ++$startLine; + } + return implode($array); + } + + + /** + * @param ReflectionClass $class + * @return string + * @throws ReflectionException + */ + protected function getClassProperty(ReflectionClass $class): string + { + $html = ''; + + $rc = $class->getParentClass()->getConstants(); + + foreach ($class->getConstants() as $key => $val) { + if (isset($rc[$key])) { + continue; + } + if (is_numeric($val)) { + $html .= ' + const ' . $key . ' = ' . $val . ';' . "\n"; + } else { + $html .= ' + const ' . $key . ' = \'' . $val . '\';' . "\n"; + } + } + + foreach ($class->getDefaultProperties() as $key => $val) { + $property = $class->getProperty($key); + if ($key == 'primary' || $key == 'table' || $key == 'connection' || $key == 'rules') { + continue; + } + if ($property->class != $class->getName()) continue; + if (is_array($val)) { + $val = '[\'' . implode('\', \'', $val) . '\']'; + } else if (!is_numeric($val)) { + $val = '\'' . $val . '\''; + } + + if ($property->isProtected()) { + $debug = 'protected'; + } else if ($property->isPrivate()) { + $debug = 'private'; + } else { + $debug = 'public'; + } + if ($property->hasType()) { + $type = ' ' . $property->getType() . ' $' . $key . ' = ' . $val . ';' . "\n"; + } else { + $type = ' $' . $key . ' = ' . $val . ';' . "\n"; + } + if ($property->isStatic()) { + $html .= ' + ' . $debug . ' static' . $type; + } else { + $html .= ' + ' . $debug . $type; + } + + } + return $html; + } + + + /** + * @param ReflectionClass $class + * @param array $filters + * @return string + * @throws Exception + */ + protected function getClassMethods(ReflectionClass $class, array $filters = []): string + { + $methods = $class->getMethods(); + + $classFileName = str_replace(APP_PATH, '', $class->getFileName()); + + $content = []; + if (!empty($methods)) foreach ($methods as $key => $val) { + if ($val->class != $class->getName()) continue; + if (in_array($val->name, $filters)) continue; + $over = " + " . $val->getDocComment() . "\n"; + + $attributes = $val->getAttributes(); + if (!empty($attributes)) { + foreach ($attributes as $attribute) { + $explode = explode('\\', $attribute->getName()); + + $_array = []; + foreach ($attribute->getArguments() as $_key => $argument) { + $argument = $this->resolveArray($argument); + if (is_numeric($_key)) { + $_array[] = $argument; + } else { + $_array[] = $_key . ': ' . $argument . ''; + } + } + + if (empty($_array)) { + $end = " #[" . end($explode) . "] +"; + } else { + $end = " #[" . end($explode) . "(" . implode(',', $_array) . ")] +"; + } + if (str_contains($over, $end)) { + $over = str_replace($end, '', $over); + } + $over .= $end; + } + } + + $func = $this->getFuncLineContent($class, $classFileName, $val->name) . "\n"; + + $content[] = $over . $func; + } + return implode(PHP_EOL, $content); + } + + + /** + * @param $argument + * @return string + */ + private function resolveArray($argument): string + { + if (is_array($argument)) { + $__array = []; + foreach ($argument as $key => $value) { + if (is_string($value)) { + if (str_contains($value, '\\') && class_exists($value)) { + $explode_class = explode('\\', $value); + + $__array[] = end($explode_class) . '::class'; + } else { + $__array[] = '\'' . $value . '\''; + } + } else { + $value = str_replace('{', '[', Json::encode($value)); + $value = str_replace('}', ']', Json::encode($value)); + $value = str_replace(':', '=>', Json::encode($value)); + + $value = preg_replace('/"\d+"\=\>/', '', $value); + + $__array[] = $value; + } + } + + $argument = '[' . implode(', ', $__array) . ']'; + } else { + $argument = '\'' . $argument . '\''; + } + return $argument; + } + + + /** + * @param $fields + * @return mixed 返回表主键 + * 返回表主键 + */ + public function getPrimaryKey($fields): mixed + { + $condition = ['PRI', 'UNI']; + foreach ($fields as $field) { + if ($field['Extra'] == 'auto_increment') { + return $field['Field']; + } + if (in_array($field['Key'], $condition)) { + return $field['Field']; + } + } + return null; + } + + /** + * @param $className + * @return string + */ + private function getFilePath($className): string + { + if (strpos($className, '\\')) { + $className = str_replace('\\', '/', $className); + } + if (strpos($className, '\\')) { + $className = str_replace('\\', '/', $className); + } + + return APP_PATH . $className; + } + + /** + * @param ReflectionClass $object + * @param $className + * @param $method + * @return string + * @throws Exception + */ + public function getFuncLineContent(ReflectionClass $object, $className, $method): string + { + $fun = $object->getMethod($method); + + $content = file_get_contents($this->getFilePath($className)); + $explode = explode(PHP_EOL, $content); + $exists = array_slice($explode, $fun->getStartLine() - 1, $fun->getEndLine() - $fun->getStartLine() + 1); + return implode(PHP_EOL, $exists); + } + + + /** + * @return array + */ + protected function getModelPath(): array + { + $dbName = $this->db->id; + if (empty($dbName) || $dbName == 'db') { + $dbName = ''; + } + + $modelPath = [ + 'namespace' => $this->modelNamespace, + 'path' => $this->modelPath, + ]; + if (!is_dir($modelPath['path'])) { + mkdir($modelPath['path']); + } + if (!empty($dbName)) { + $modelPath['namespace'] = $this->modelNamespace . ucfirst($dbName); + $modelPath['path'] = $this->modelPath . ucfirst($dbName); + } + + if (!is_dir($modelPath['path'])) { + mkdir($modelPath['path']); + } + return $modelPath; + } + + /** + * @param $db + */ + public function setConnection($db) + { + $this->db = $db; + } + + /** + * @param $val + * @return string + */ + protected function checkIsRequired($val): string + { + return strtolower($val['Null']) == 'no' && $val['Default'] === NULL ? 'true' : 'false'; + } + + /** + * @return array + */ + public function getFileLists(): array + { + return $this->fileList; + } + +} diff --git a/GiiCommand.php b/GiiCommand.php new file mode 100644 index 0000000..81ae8c9 --- /dev/null +++ b/GiiCommand.php @@ -0,0 +1,76 @@ +setName('sw:gii') + ->addOption('make','m', InputArgument::OPTIONAL) + ->addOption('name','t', InputArgument::OPTIONAL) + ->addOption('databases','d', InputArgument::OPTIONAL) + ->setDescription('./snowflake sw:gii make=model|controller|task|interceptor|limits|middleware name=xxxx'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + * @throws ConfigException + * @throws Exception + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + /** @var Gii $gii */ + $gii = Kiri::app()->get('gii'); + + $connections = Kiri::app()->get('db'); + if (($db = $input->getOption('databases')) != null) { + $gii->run($connections->get($db), $input); + return 1; + } + + $action = $input->getOption('make'); + if (!in_array($action, ['model', 'controller'])) { + $gii->run(null, $input); + return 1; + } + + $array = []; + foreach (Config::get('databases.connections') as $key => $connection) { + $array[$key] = $gii->run($connections->get($key), $input); + } + + $output->writeln(json_encode($array, JSON_UNESCAPED_UNICODE)); + + return 1; + } + +} diff --git a/GiiController.php b/GiiController.php new file mode 100644 index 0000000..8016617 --- /dev/null +++ b/GiiController.php @@ -0,0 +1,537 @@ +className = $className; + $this->fields = $fields; + } + + + /** + * @return string|bool + * @throws ReflectionException + * @throws Exception + */ + public function generate(): string|bool + { + $path = $this->getControllerPath(); + $modelPath = $this->getModelPath(); + + $managerName = $this->className; + + $namespace = rtrim($path['namespace'], '\\'); + $model_namespace = rtrim($modelPath['namespace'], '\\'); + + $class = ''; + $controller = str_replace('\\\\', '\\', "$namespace\\{$managerName}Controller"); + + $html = "getImports($path['path'] . '/' . $managerName . 'Controller.php', $class); + } catch (\Throwable $Exception) { + logger()->addError($Exception, 'throwable'); + exit(); + } + } else { + $import = "use Kiri; +use Exception; +use Kiri\Annotation\Target; +use Kiri\Annotation\Route\Middleware; +use Kiri\Annotation\Route\Route; +use Kiri\Annotation\Route\RequestMethod; +use Kiri\Core\Str; +use Kiri\Core\Json; +use Kiri\Message\Controller; +use JetBrains\PhpStorm\ArrayShape; +use {$model_namespace}\\{$managerName}; +"; + } + if (!empty($import)) { + $html .= $import; + } + + $controllerName = $managerName; + + $historyModel = "use {$model_namespace}\\{$managerName};"; + if (!str_contains($html, $historyModel)) { + $html .= $historyModel; + } + + $html .= " + +/** + * Class {$controllerName}Controller + * + * @package controller + */ +#[Target] class {$controllerName}Controller extends Controller +{ + +"; + + + $funcNames = []; + if (is_object($class)) { + $html .= $this->getClassProperty($class); + $html .= $this->getClassMethods($class); + } + + $default = ['loadParam', 'actionAdd', 'actionUpdate', 'actionDetail', 'actionDelete', 'actionBatchDelete', 'actionList']; + + foreach ($default as $key => $val) { + if (str_contains($html, ' function ' . $val . '(')) { + continue; + } + $html .= $this->{'controllerMethod' . str_replace('action', '', $val)}($this->fields, $managerName, $managerName, $path) . "\n"; + } + + $html .= ' +}'; + + $file = $path['path'] . '/' . $controllerName . 'Controller.php'; + if (file_exists($file)) { + unlink($file); + } + + Kiri::writeFile($file, $html); + return $controllerName . 'Controller.php'; + } + + + /** + * @return array + */ + private function getControllerPath(): array + { + $dbName = $this->db->id; + if (empty($dbName) || $dbName == 'db') { + $dbName = ''; + } + + $module = empty($this->module) ? '' : $this->module; + $modelPath['namespace'] = $this->controllerNamespace . $module; + $modelPath['path'] = $this->controllerPath . $module; + if (!is_dir($modelPath['path'])) { + mkdir($modelPath['path']); + } + if (!empty($dbName)) { + $modelPath['namespace'] = $this->controllerNamespace . ucfirst($dbName); + $modelPath['path'] = $this->controllerPath . ucfirst($dbName); + } + + $modelPath['namespace'] = rtrim($modelPath['namespace'], '\\'); + $modelPath['path'] = rtrim($modelPath['path'], '\\'); + + if (!is_dir($modelPath['path'])) { + mkdir($modelPath['path']); + } + return $modelPath; + } + + /** + * @param $fields + * @param $className + * @param null $object + * @param $path + * @return string + * 新增 + */ + public function controllerMethodAdd($fields, $className, $object, $path): string + { + $_path = str_replace(CONTROLLER_PATH, '', $path['path']); + $_path = lcfirst(rtrim($_path, '/')) . '/' . lcfirst($className); + + $_path = ltrim($_path,'/'); + + return ' + /** + * @return string + * @throws Exception + */ + #[Route(uri: "' . $_path . '/add", method: RequestMethod::REQUEST_POST)] + #[Middleware(middleware: [])] + public function actionAdd(): string + { + $model = new ' . $className . '(); + $model->attributes = $this->loadParam(); + if (!$model->save()) { + return JSON::to(500, $model->getLastError()); + } + return JSON::to(0, $model->toArray()); + }'; + } + + /** + * @param $fields + * @param $className + * @param null $object + * @return string + * 通用 + */ + public function controllerMethodLoadParam($fields, $className, $object = NULL): string + { + return ' + /** + * @return array + * @throws Exception + */ + #[ArrayShape([])] + private function loadParam(): array + { + return [' . $this->getData($fields) . ' + ]; + }'; + } + + /** + * @param $fields + * @param $className + * @param null $object + * @param array $path + * @return string + * 构建更新 + */ + public function controllerMethodUpdate($fields, $className, $object = NULL, $path = []): string + { + $_path = str_replace(CONTROLLER_PATH, '', $path['path']); + $_path = lcfirst(rtrim($_path, '/')) . '/' . lcfirst($className); + + $_path = ltrim($_path,'/'); + + return ' + /** + * @return string + * @throws Exception + */ + #[Route(uri: "' . $_path . '/update", method: RequestMethod::REQUEST_POST)] + #[Middleware(middleware: [])] + public function actionUpdate(): string + { + $model = ' . $className . '::findOne($this->request->post(\'id\', 0)); + if (empty($model)) { + return JSON::to(500, SELECT_IS_NULL); + } + $model->attributes = $this->loadParam(); + + if (!$model->save()) { + return JSON::to(500, $model->getLastError()); + } + return JSON::to(0, $model->toArray()); + }'; + } + + /** + * @param $fields + * @param $className + * @param null $object + * @param array $path + * @return string + * 构建更新 + */ + public function controllerMethodBatchDelete($fields, $className, $object = NULL, $path = []): string + { + $_path = str_replace(CONTROLLER_PATH, '', $path['path']); + $_path = lcfirst(rtrim($_path, '/')) . '/' . lcfirst($className); + + $_path = ltrim($_path,'/'); + + return ' + /** + * @return string + * @throws Exception + */ + #[Route(uri: "' . $_path . '/batch-delete", method: RequestMethod::REQUEST_POST)] + #[Middleware(middleware: [])] + public function actionBatchDelete(): string + { + $_key = $this->request->array(\'ids\'); + if (empty($_key)) { + return JSON::to(500, PARAMS_IS_NULL); + } + + $model = ' . $className . '::find()->whereIn(\'id\', $_key); + if (!$model->delete()) { + return JSON::to(500, DB_ERROR_BUSY); + } + return JSON::to(0, $_key); + }'; + } + + /** + * @param $fields + * @param $className + * @param $managerName + * @param array $path + * @return string + * 构建详情 + */ + public function controllerMethodDetail($fields, $className, $managerName, $path = []): string + { + $_path = str_replace(CONTROLLER_PATH, '', $path['path']); + $_path = lcfirst(rtrim($_path, '/')) . '/' . lcfirst($className); + + $_path = ltrim($_path,'/'); + + return ' + /** + * @return string + * @throws Exception + */ + #[Route(uri: "' . $_path . '/detail", method: RequestMethod::REQUEST_POST)] + #[Middleware(middleware: [])] + public function actionDetail(): string + { + $model = ' . $managerName . '::findOne($this->request->query(\'id\')); + if (empty($model)) { + return JSON::to(404, SELECT_IS_NULL); + } + return JSON::to(0, $model->toArray()); + }'; + } + + /** + * @param $fields + * @param $className + * @param $managerName + * @param $path + * @return string + * 构建删除操作 + */ + public function controllerMethodDelete($fields, $className, $managerName, $path): string + { + $_path = str_replace(CONTROLLER_PATH, '', $path['path']); + $_path = lcfirst(rtrim($_path, '/')) . '/' . lcfirst($className); + + $_path = ltrim($_path,'/'); + + return ' + /** + * @return string + * @throws Exception + */ + #[Route(uri: "' . $_path . '/delete", method: RequestMethod::REQUEST_POST)] + #[Middleware(middleware: [])] + public function actionDelete(): string + { + $_key = $this->request->int(\'id\', true); + + $model = ' . $managerName . '::findOne($_key); + if (empty($model)) { + return JSON::to(500, SELECT_IS_NULL); + } + if (!$model->delete()) { + return JSON::to(500, $model->getLastError()); + } + return JSON::to(0, $model); + }'; + } + + /** + * @param $fields + * @param $className + * @param $managerName + * @param array $path + * @return string + * 构建查询列表 + */ + public function controllerMethodList($fields, $className, $managerName, $path = []): string + { + $_path = str_replace(CONTROLLER_PATH, '', $path['path']); + $_path = lcfirst(rtrim($_path, '/')) . '/' . lcfirst($className); + + + $_path = ltrim($_path,'/'); + + return ' + /** + * @return string + * @throws Exception + */ + #[Route(uri: "' . $_path . '/list", method: RequestMethod::REQUEST_GET)] + #[Middleware(middleware: [])] + public function actionList(): string + { + //分页处理 + $count = $this->request->query(\'count\', -1); + $order = $this->request->query(\'order\', \'id\'); + if (!empty($order)) { + $order .= !$this->request->query(\'isDesc\', 0) ? \' asc\' : \' desc\'; + } else { + $order = \'id desc\'; + } + + //列表输出 + $model = ' . $managerName . '::find()->where($this->request->gets())->orderBy($order); + + $keyword = $this->request->query(\'keyword\', null); + if (!empty($keyword)) { + $model->like(\'keyword\', $keyword); + } + + if ((int) $count === 1) { + $count = $model->count(); + } + if ($count != -100) { + $model->limit($this->request->offset() ,$this->request->size()); + } + + $data = $model->all()->toArray(); + + return JSON::to(0, $data, $count); + } + '; + } + + private function getData($fields): string + { + $html = ''; + + $length = $this->getMaxLength($fields); + + + foreach ($fields as $key => $val) { + preg_match('/\((\d+)(,(\d+))*\)/', $val['Type'], $number); + $type = strtolower(preg_replace('/\(\d+(,\d+)*\)/', '', $val['Type'])); + + $first = preg_replace('/\s+\w+/', '', $type); + if ($val['Field'] == 'id') continue; + if ($type == 'timestamp') continue; + $_field = []; + $_field['required'] = $this->checkIsRequired($val); + foreach ($this->type as $_key => $value) { + if (!in_array(strtolower($first), $value)) continue; + $comment = '//' . $val['Comment']; + $_field['type'] = $_key; + + if ($type == 'date' || $type == 'datetime' || $type == 'time') { + $_tps = match ($type) { + 'date' => '$this->request->' . $_key . '(\'' . $val['Field'] . '\', date(\'Y-m-d\'))', + 'time' => '$this->request->' . $_key . '(\'' . $val['Field'] . '\', date(\'H:i:s\'))', + default => '$this->request->' . $_key . '(\'' . $val['Field'] . '\', date(\'Y-m-d H:i:s\'))', + }; + $html .= ' + \'' . str_pad($val['Field'] . '\'', $length, ' ', STR_PAD_RIGHT) . ' => ' . str_pad($_tps . ',', 60, ' ', STR_PAD_RIGHT) . $comment; + } else { + $tmp = 'null'; + if (isset($number[0])) { + if (strpos(',', $number[0])) { + $tmp = '[' . $number[1] . ',' . $number[3] . ']'; + $_field['min'] = $number[1]; + $_field['max'] = $number[3]; + } else { + $tmp = '[0,' . $number[1] . ']'; + $_field['min'] = 0; + $_field['max'] = $number[1]; + } + } + if ($key == 'string') { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', ' . $_field['required'] . ', ' . $tmp . ')'; + } else if ($type == 'int') { + if ($number[0] == 10) { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', time())'; + } else { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', ' . $_field['required'] . ')'; + } + } else if ($type == 'float') { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', ' . $_field['required'] . ', ' . ($number[3] ?? '2') . ')'; + } else if ($key == 'email') { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', ' . $_field['required'] . ')'; + } else if ($key == 'timestamp') { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', time())'; + } else { + $_tps = '$this->request->' . $_key . '(\'' . $val['Field'] . '\', ' . $_field['required'] . ')'; + } + $html .= ' + \'' . str_pad($val['Field'] . '\'', $length, ' ', STR_PAD_RIGHT) . ' => ' . str_pad($_tps . ',', 60, ' ', STR_PAD_RIGHT) . $comment; + } + } + $this->rules[$val['Field']] = $_field; + } + return $html; + } + + + /** + * @param $fields + * @return int + */ + private function getMaxLength($fields): int + { + $length = 0; + foreach ($fields as $key => $val) { + if (mb_strlen($val['Field'] . ' >=') > $length) $length = mb_strlen($val['Field'] . ' >='); + } + return $length; + } + + /** + * @param $fields + * @return string + */ + private function getWhere($fields): string + { + $html = ''; + + $length = $this->getMaxLength($fields); + foreach ($fields as $key => $val) { + preg_match('/\d+/', $val['Type'], $number); + + $type = strtolower(preg_replace('/\(\d+\)/', '', $val['Type'])); + + $first = preg_replace('/\s+\w+/', '', $type); + + if ($type == 'timestamp') continue; + if ($type == 'json') continue; + + foreach ($this->type as $_key => $value) { + if (!in_array(strtolower($first), $value)) continue; + $comment = '//' . $val['Comment']; + if ($type == 'date' || $type == 'datetime' || $type == 'time') { + $_tps = '$this->request->query(\'' . $val['Field'] . '\', null)'; + $html .= ' + $pWhere[\'' . str_pad($val['Field'] . ' <=\']', $length, ' ', STR_PAD_RIGHT) . ' = ' . str_pad($_tps . ';', 60, ' ', STR_PAD_RIGHT) . $comment; + $html .= ' + $pWhere[\'' . str_pad($val['Field'] . ' >=\']', $length, ' ', STR_PAD_RIGHT) . ' = ' . str_pad($_tps . ';', 60, ' ', STR_PAD_RIGHT) . $comment; + } else { + + $_tps = '$this->request->query(\'' . $val['Field'] . '\', null)'; + $html .= ' + $pWhere[\'' . str_pad($val['Field'] . '\']', $length, ' ', STR_PAD_RIGHT) . ' = ' . str_pad($_tps . ';', 60, ' ', STR_PAD_RIGHT) . $comment; + } + } + } + return $html; + } +} diff --git a/GiiJsonRpc.php b/GiiJsonRpc.php new file mode 100644 index 0000000..fc4ceee --- /dev/null +++ b/GiiJsonRpc.php @@ -0,0 +1,113 @@ +createInterface($this->input->getArgument('name')), + $this->createProducers($this->input->getArgument('name')), + $this->createConsumer($this->input->getArgument('name')), + ]; + } + + + private function createInterface($name): string + { + $html = 'input->getArgument('name'); + if (empty($managerName)) { + throw new Exception('文件名称不能为空~'); + } + $html = 'classFileName = $classFileName; + $this->tableName = $tableName; + $this->visible = $visible; + $this->res = $res; + $this->fields = $fields; + } + + /** + * @throws ReflectionException + * @throws Exception + */ + public function generate(): string + { + $class = ''; + $modelPath = $this->getModelPath(); + $managerName = $this->classFileName; + + $namespace = rtrim($modelPath['namespace'], '\\'); + + if (file_exists($modelPath['path'] . '/' . $managerName . '.php')) { + try { + $className = str_replace('\\\\', '\\', "{$modelPath['namespace']}\\{$managerName}"); + + $class = Kiri::getDi()->getReflect($className); + + $html = 'getImports($modelPath['path'] . '/' . $managerName . '.php', $class); + if (!empty($imports)) { + $html .= $imports . PHP_EOL; + } + + if (!str_contains($imports, 'Database\Annotation\Set')) { + $html .= 'use Database\Annotation\Set;' . PHP_EOL; + } + if (!str_contains($imports, 'Database\Annotation\Get')) { + $html .= 'use Database\Annotation\Get;' . PHP_EOL; + } + } catch (\Throwable $e) { + logger()->addError($e, 'throwable'); + } + } + + + if (empty($html)) { + $html = 'setCreateSql($this->tableName); + + if (!str_contains($html, $createSql)) { + $html .= ' +' . $this->setCreateSql($this->tableName); + } + + $html .= ' + +/** + * Class ' . $managerName . ' + * @package Inter\mysql + *' . implode('', $this->visible) . ' + */ +#[Target] class ' . $managerName . ' extends Model +{ + +'; + + if (!empty($class)) { + $html .= $this->getClassProperty($class); + } + + $primary = $this->createPrimary($this->fields); + if (!empty($primary)) { + $html .= $primary . "\n"; + } + + $html .= $this->createTableName($this->tableName) . "\n"; + + $html .= $this->createDatabaseSource(); + + $html .= $this->createRules($this->fields); + + if (is_object($class)) { + $html .= $this->getClassMethods($class, ['rules', 'tableName', 'attributes', 'getDb']); + } else { + $other = $this->generate_json_function($html, $this->fields); + if (!empty($other)) { + $html .= implode($other); + } + } + + $html .= ' +}'; + + $file = rtrim($modelPath['path'], '/') . '/' . $managerName . '.php'; + if (file_exists($file)) { + unlink($file); + } + + Kiri::writeFile($file, $html); + return $managerName . '.php'; + } + + + /** + * @param $html + * @param $fields + * @return array + */ + private function generate_json_function($html, $fields): array + { + $strings = []; + foreach ($fields as $field) { + if ($field['Type'] === 'json') { + $function = ' + /** + * @param $value + * @return int|bool|string + * @throws Exception + */ + #[Set(\'' . $field['Field'] . '\')] + public function set' . ucfirst($field['Field']) . 'Attribute($value): int|bool|string + { + if ( !is_string($value) ) { + return JSON::encode($value); + } + return $value; + } + '; + + $get_function = ' + /** + * @param $value + * @return array|null|bool + */ + #[Get(\'' . $field['Field'] . '\')] + public function get' . ucfirst($field['Field']) . 'Attribute($value): array|null|bool + { + $value = stripcslashes($value); + if ( is_string($value) ) { + return JSON::decode($value, true); + } + return $value; + } + '; + + if (!str_contains($html, 'set' . ucfirst($field['Field']) . 'Attribute')) { + $strings[] = $function; + } + + if (!str_contains($html, 'get' . ucfirst($field['Field']) . 'Attribute')) { + $strings[] = $get_function; + } + } + } + + return $strings; + } + + + /** + * @param $field + * @return string + * 创建表名称 + */ + private function createTableName($field): string + { + + $prefixed = $this->db->tablePrefix; + if (!empty($prefixed)) { + if (str_starts_with($field, $prefixed)) { + $field = str_replace($prefixed, '', $field); + } + } + + return ' + /** + * @inheritdoc + */ + protected string $table = \'' . $field . '\'; +'; + } + + /** + * @param $fields + * @return string + * 创建效验规则 + */ + private function createRules($fields): string + { + $data = []; + foreach ($fields as $key => $val) { + if ($val['Extra'] == 'auto_increment') continue; + $type = preg_replace('/\(.*?\)|\s+\w+/', '', $val['Type']); + foreach ($this->type as $_key => $_val) { + if (in_array($type, $_val)) { + $type = lcfirst(str_replace('get', '', $_key)); + break; + } + } + $data[$type][] = $val; + } + + $_field_one = ''; + $required = $this->getRequired($fields); + if (!empty($required)) { + $_field_one .= $required; + } + foreach ($data as $key => $val) { + $field = '[\'' . implode('\', \'', array_column($val, 'Field')) . '\']'; + if (count($val) == 1) { + $field = '\'' . current($val)['Field'] . '\''; + } + $_field_one .= ' + [' . $field . ', \'' . $key . '\'],'; + } + foreach ($data as $key => $val) { + $length = $this->getLength($val); + if (!empty($length)) { + $_field_one .= $length . ','; + } + } + $required = $this->getUnique($fields); + if (!empty($required)) { + $_field_one .= $required; + } + return ' + /** + * @return array + */ + public function rules(): array + { + return [' . $_field_one . ' + ]; + } +'; + } + + /** + * @param $val + * @return string + */ + public function getLength($val): string + { + $data = []; + foreach ($val as $key => $_val) { + $preg = preg_match('/(\w+)\((.*?)\)/', $_val['Type'], $results); + if ($preg && isset($results[2])) { + $results[] = $_val['Field']; + + $data[$results[2]][] = $results; + } + } + if (empty($data)) return ''; + $string = []; + foreach ($data as $key => $_val) { + if (in_array($_val[0][1], $this->type['float'])) { + $e_x = explode(',', $key); + $key = '\'round\' => ' . $e_x[1] . ', \'maxLength\' => ' . ((int)$e_x[0] + 1); + } else if (is_string($key) && str_contains($key, ',')) { + } else { + $key = '\'maxLength\' => ' . $key; + } + if (count($_val) == 1) { + $_tmp = ' + [\'' . $_val[0][3] . '\', ' . ($_val[0][1] == 'enum' ? '\'enum\' => [' . $key .']' : $key) . ']'; + } else { + $_tmp = ' + [[\'' . implode('\', \'', array_column($_val, 3)) . '\'], ' . $key . ']'; + } + $string[] = $_tmp; + } + return implode(',', $string); + } + + /** + * @param $fields + * @return string + */ + public function getUnique($fields): string + { + $data = []; + foreach ($fields as $_val) { + if ($_val['Extra'] == 'auto_increment') continue; + if (str_contains($_val['Type'], 'unique')) { + $data[] = $_val['Field']; + } + } + if (empty($data)) { + return ''; + } + return ' + [[\'' . implode('\', \'', $data) . '\'], \'unique\'],'; + } + + /** + * @param $val + * @return string + */ + public function getRequired($val): string + { + $data = []; + foreach ($val as $_key => $_val) { + if ($_val['Extra'] == 'auto_increment') continue; + if ($_val['Key'] == 'PRI' || $_val['Key'] == 'UNI' || $this->checkIsRequired($_val) === 'true') { + array_push($data, $_val['Field']); + } + } + if (empty($data)) { + return ''; + } + return ' + [[\'' . implode('\', \'', $data) . '\'], \'required\'],'; + } + + /** + * 用来生成文档的 + * 格式 + * @param $fields + * @return null|string + * array( + * 'field' ,'字段類型' ,'是否必填' ,'字段长度' , '字段解释', + * 'field' ,'字段類型' ,'是否必填' ,'字段长度' , '字段解释', + * 'field' ,'字段類型' ,'是否必填' ,'字段长度' , '字段解释', + * 'field' ,'字段類型' ,'是否必填' ,'字段长度' , '字段解释', + * 'field' ,'字段類型' ,'是否必填' ,'字段长度' , '字段解释', + * 'field' ,'字段類型' ,'是否必填' ,'字段长度' , '字段解释', + * ) + */ + private function createPrimary($fields): ?string + { + $field = $this->getPrimaryKey($fields); + if (empty($field)) { + return null; + } + return ' + /** + * @var string|null + */ + protected ?string $primary = \'' . $field . '\';'; + } + + /** + * @return string + */ + private function createDatabaseSource(): string + { + return ' + /** + * @var string + */ + protected string $connection = \'' . $this->db->id . '\'; +'; + } + + /** + * @param $table + * @return string + * @throws Exception + */ + private function setCreateSql($table): string + { + if (isset(Gii::$createSqls[$table])) { + return Gii::$createSqls[$table]; + } + + $text = Db::showCreateSql($table, $this->db)['Create Table'] ?? ''; + + $_tmp = []; + foreach (explode(PHP_EOL, $text) as $val) { + $_tmp[] = '// ' . $val; + } + + return Gii::$createSqls[$table] = implode(PHP_EOL, $_tmp); + } + + +} diff --git a/GiiProviders.php b/GiiProviders.php new file mode 100644 index 0000000..9330a94 --- /dev/null +++ b/GiiProviders.php @@ -0,0 +1,34 @@ +set('gii', ['class' => Gii::class]); + + $container = Kiri::getDi(); + + $console = $container->get(\Symfony\Component\Console\Application::class); + $console->add($container->get(GiiCommand::class)); + } +} diff --git a/GiiRpcClient.php b/GiiRpcClient.php new file mode 100644 index 0000000..dc4892f --- /dev/null +++ b/GiiRpcClient.php @@ -0,0 +1,100 @@ +input->getArgument('name', null); + if (empty($managerName)) { + throw new Exception('文件名称不能为空~'); + } + + $service = $this->input->getArgument('service', strtolower($managerName)); + + $port = $this->input->getArgument('port', 443); + $mode = $this->input->getArgument('mode', 'SWOOLE_SOCK_TCP'); + + $html = ' \'127.0.0.1\', \'port\' => 5377]; + + + /** + * @return Client + * @throws Exception + */ + public function initClient(): Client + { + // TODO: Implement initClient() method. + return $this->client = $this->rpc->getClient(\'' . $service . '\'); + } + + + + /** + * @param string $event + * @param array $params + * @throws Exception + */ + #[Consumer(\'default\')] + public function push(string $event, array $params) + { + + } +}'; + + if (!is_dir(APP_PATH . 'app/Client/Rpc/')) { + mkdir(APP_PATH . 'app/Client/Rpc/', 0777, true); + } + + $file = APP_PATH . 'app/Client/Rpc/' . $managerName . 'Middleware.php'; + if (file_exists($file)) { + throw new Exception('File exists.'); + } + + Kiri::writeFile($file, $html); + return [$managerName . 'Middleware.php']; + } +} diff --git a/GiiRpcService.php b/GiiRpcService.php new file mode 100644 index 0000000..7ce27fb --- /dev/null +++ b/GiiRpcService.php @@ -0,0 +1,79 @@ +input->getArgument('name', null); + if (empty($managerName)) { + throw new Exception('文件名称不能为空~'); + } + + $port = $this->input->getArgument('port', 443); + + $html = 'input->getArgument('name', null); + if (empty($managerName)) { + throw new Exception('文件名称不能为空~'); + } + $html = 'params = $params; + return $this; + } + + + /** + * @return array + */ + public function getParams() + { + return $this->params; + } + + +}'; + + $file = APP_PATH . 'app/Async/' . $managerName . '.php'; + if (file_exists($file)) { + throw new Exception('File exists.'); + } + + Kiri::writeFile($file, $html); + return [$managerName . '.php']; + } + +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c91cd3d --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "game-worker/kiri-gii", + "description": "gii", + "authors": [ + { + "name": "XiangLin", + "email": "as2252258@163.com" + } + ], + "license": "MIT", + "require": { + "php": ">=8.0", + "composer-runtime-api": "^2.0" + }, + "autoload": { + "psr-4": { + "Gii\\": "./" + } + }, + "require-dev": { + } +}