• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

miaoxing / plugin / 7293609938

19 Dec 2023 01:41PM UTC coverage: 39.661%. Remained the same
7293609938

push

github

twinh
refactor(plugin, phpstan): GMetadata::handle() never returns void so it can be removed from the return type

0 of 1 new or added line in 1 file covered. (0.0%)

936 of 2360 relevant lines covered (39.66%)

18.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Command/GMetadata.php
1
<?php
2

3
namespace Miaoxing\Plugin\Command;
4

5
use Miaoxing\Plugin\BasePlugin;
6
use ReflectionClass;
7
use ReflectionMethod;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Wei\BaseModel;
10
use Wei\Model\CamelCaseTrait;
11

12
/**
13
 * @mixin \StrMixin
14
 * @mixin \ClsMixin
15
 * @mixin \PluginMixin
16
 * @internal will change in the future
17
 */
18
final class GMetadata extends BaseCommand
19
{
20
    use PluginIdTrait;
21

22
    public function handle()
23
    {
24
        $id = $this->getPluginId();
×
25

26
        $plugin = wei()->plugin->getOneById($id);
×
27

28
        $dir = $plugin->getBasePath() . '/src/Metadata/';
×
29
        if (!is_dir($dir)) {
×
30
            mkdir($dir);
×
31
        }
32

33
        $services = wei()->classMap->generate(
×
34
            [$plugin->getBasePath() . '/src'],
×
35
            '/Service/?*Model.php', // 排除 Model.php
×
36
            'Service',
×
37
            false
×
38
        );
×
39

40
        foreach ($services as $name => $class) {
×
41
            $uses = $this->cls->usesDeep($class);
×
42
            $camelCase = isset($uses[CamelCaseTrait::class]);
×
43
            $this->createClass($name, $plugin, $camelCase);
×
44
        }
45

NEW
46
        $this->suc('创建成功');
×
47
    }
48

49
    protected function configure()
50
    {
51
        $this
×
52
            ->addArgument('plugin-id', InputArgument::OPTIONAL, 'The id of plugin');
×
53
    }
54

55
    protected function createClass($model, $plugin, $camelCase)
56
    {
57
        /** @var BaseModel $modelObject */
58
        $modelObject = $this->wei->get($model);
×
59
        $table = $modelObject->getDb()->getTable($modelObject->getTable());
×
60
        $columns = wei()->db->fetchAll('SHOW FULL COLUMNS FROM ' . $table);
×
61
        $modelColumns = $modelObject->getColumns();
×
62

63
        $docBlocks = [];
×
64
        foreach ($columns as $column) {
×
65
            $propertyName = $camelCase ? $this->str->camel($column['Field']) : $column['Field'];
×
66

67
            $phpType = $this->getPhpType($column['Type']);
×
68

69
            // TODO 支持其他类型
70
            if ('json' === $column['Type'] && ($modelColumns[$propertyName]['cast'] ?? null) === 'object') {
×
71
                $phpType = 'object';
×
72
            }
73

74
            if (isset($modelColumns[$propertyName]['nullable']) && $modelColumns[$propertyName]['nullable']) {
×
75
                $phpType .= '|null';
×
76
            }
77

78
            $propertyName = $camelCase ? $this->str->camel($column['Field']) : $column['Field'];
×
79
            $docBlocks[$propertyName] = rtrim(sprintf(
×
80
                ' * @property %s $%s %s',
×
81
                $phpType,
×
82
                $propertyName,
×
83
                $column['Comment']
×
84
            ));
×
85
        }
86

87
        // 获取getXxxAttribute的定义
88
        $reflectionClass = new ReflectionClass($modelObject);
×
89
        preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($modelObject)), $matches);
×
90
        foreach ($matches[1] as $key => $attr) {
×
91
            $propertyName = $camelCase ? lcfirst($attr) : $this->str->snake($attr);
×
92
            if (isset($docBlocks[$propertyName])) {
×
93
                continue;
×
94
            }
95

96
            $method = rtrim($matches[0][$key], ';');
×
97
            $reflectionMethod = $reflectionClass->getMethod($method);
×
98
            $name = $this->getDocCommentTitle($reflectionMethod->getDocComment());
×
99
            $return = $this->getMethodReturn($reflectionClass, $reflectionMethod) ?: 'mixed';
×
100
            $docBlocks[$propertyName] = rtrim(sprintf(' * @property %s $%s %s', $return, $propertyName, $name));
×
101
        }
102

103
        $class = ucfirst(substr($model, 0, -strlen('Model'))) . 'Trait';
×
104
        $file = $this->getFile($plugin, $class);
×
105
        $docBlock = implode("\n", $docBlocks) . "\n";
×
106
        $this->createFile($file, $this->getNamespace($plugin), $class, $docBlock);
×
107
    }
108

109
    protected function getPhpType($columnType)
110
    {
111
        $parts = explode('(', $columnType);
×
112
        $type = $parts[0];
×
113
        $length = (int) ($parts[1] ?? 0);
×
114

115
        switch ($type) {
116
            case 'int':
×
117
            case 'smallint':
×
118
            case 'mediumint':
×
119
                return 'int';
×
120

121
            case 'tinyint':
×
122
                return 1 === $length ? 'bool' : 'int';
×
123

124
            case 'bigint':
×
125
            case 'varchar':
×
126
            case 'char':
×
127
            case 'mediumtext':
×
128
            case 'text':
×
129
            case 'timestamp':
×
130
            case 'datetime':
×
131
            case 'date':
×
132
            case 'decimal':
×
133
            case 'binary':
×
134
            case 'varbinary':
×
135
                return 'string';
×
136

137
            case 'json':
×
138
                return 'array';
×
139

140
            default:
141
                return $type;
×
142
        }
143
    }
144

145
    protected function getTable($class)
146
    {
147
        if (wei()->isEndsWith($class, 'Record')) {
×
148
            $class = substr($class, 0, -6);
×
149
        }
150

151
        $table = $this->str->snake($class);
×
152
        $table = $this->str->pluralize($table);
×
153

154
        return $table;
×
155
    }
156

157
    protected function getFile(BasePlugin $plugin, $name)
158
    {
159
        return $plugin->getBasePath() . '/src/Metadata/' . ucfirst($name) . '.php';
×
160
    }
161

162
    protected function createDir($dir)
163
    {
164
        if (!is_dir($dir)) {
×
165
            mkdir($dir, 0777, true);
×
166
            chmod($dir, 0777);
×
167
        }
168
    }
169

170
    protected function getNamespace(BasePlugin $plugin)
171
    {
172
        $class = get_class($plugin);
×
173
        $parts = explode('\\', $class);
×
174
        array_pop($parts);
×
175

176
        return implode('\\', $parts) . '\Metadata';
×
177
    }
178

179
    protected function createFile($file, $namespace, $class, $docBlock)
180
    {
181
        $table = $this->getTable($class);
×
182

183
        $this->suc('生成文件 ' . $file);
×
184

185
        ob_start();
×
186
        require $this->plugin->getById('plugin')->getBasePath() . '/stubs/metadata.php';
×
187
        $content = ob_get_clean();
×
188

189
        file_put_contents($file, $content);
×
190
        chmod($file, 0777);
×
191
    }
192

193
    protected function getMethodReturn(ReflectionClass $class, ReflectionMethod $method)
194
    {
195
        $doc = $method->getDocComment();
×
196
        preg_match('/@return (.+?)\n/', $doc, $matches);
×
197
        if (!$matches) {
×
198
            return false;
×
199
        }
200

201
        return $matches[1] ?: false;
×
202
    }
203

204
    /**
205
     * 返回注释的标题(第一行)
206
     *
207
     * @param string $docComment
208
     * @return bool|mixed
209
     */
210
    protected function getDocCommentTitle($docComment)
211
    {
212
        preg_match('#\* ([^@]+?)\n#is', $docComment, $matches);
×
213
        if ($matches) {
×
214
            return $matches[1];
×
215
        }
216

217
        return false;
×
218
    }
219
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc