|
1 | 1 | <?php
|
2 | 2 |
|
3 |
| -collect(glob(base_path('**/Models/*.php')))->each(fn($file) => include_once($file)); |
4 |
| - |
5 | 3 | if (class_exists('\phpDocumentor\Reflection\DocBlockFactory')) {
|
6 | 4 | $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
|
7 | 5 | } else {
|
8 | 6 | $factory = null;
|
9 | 7 | }
|
10 | 8 |
|
11 |
| -function getMethodDocblocks($method, $factory) |
12 |
| -{ |
13 |
| - if ($factory !== null) { |
14 |
| - $docblock = $factory->create($method->getDocComment()); |
15 |
| - $params = collect($docblock->getTagsByName("param"))->map(fn($p) => (string) $p)->all(); |
16 |
| - $return = (string) $docblock->getTagsByName("return")[0] ?? null; |
| 9 | +$docblocks = new class($factory) { |
| 10 | + public function __construct(protected $factory) {} |
| 11 | + |
| 12 | + public function forMethod($method) |
| 13 | + { |
| 14 | + if ($this->factory !== null) { |
| 15 | + $docblock = $this->factory->create($method->getDocComment()); |
| 16 | + $params = collect($docblock->getTagsByName("param"))->map(fn($p) => (string) $p)->all(); |
| 17 | + $return = (string) $docblock->getTagsByName("return")[0] ?? null; |
| 18 | + |
| 19 | + return [$params, $return]; |
| 20 | + } |
| 21 | + |
| 22 | + |
| 23 | + $params = collect($method->getParameters()) |
| 24 | + ->map(function (\ReflectionParameter $param) { |
| 25 | + $types = match ($param?->getType()) { |
| 26 | + null => [], |
| 27 | + default => method_exists($param->getType(), "getTypes") |
| 28 | + ? $param->getType()->getTypes() |
| 29 | + : [$param->getType()] |
| 30 | + }; |
| 31 | + |
| 32 | + $types = collect($types) |
| 33 | + ->filter() |
| 34 | + ->values() |
| 35 | + ->map(fn($t) => $t->getName()); |
| 36 | + |
| 37 | + return trim($types->join("|") . " $" . $param->getName()); |
| 38 | + }) |
| 39 | + ->all(); |
| 40 | + |
| 41 | + $return = $method->getReturnType()?->getName(); |
17 | 42 |
|
18 | 43 | return [$params, $return];
|
19 | 44 | }
|
| 45 | +}; |
20 | 46 |
|
| 47 | +$models = new class($factory) { |
| 48 | + protected $output; |
21 | 49 |
|
22 |
| - $params = collect($method->getParameters()) |
23 |
| - ->map(function (\ReflectionParameter $param) { |
24 |
| - $types = match ($param?->getType()) { |
25 |
| - null => [], |
26 |
| - default => method_exists($param->getType(), "getTypes") |
27 |
| - ? $param->getType()->getTypes() |
28 |
| - : [$param->getType()] |
29 |
| - }; |
| 50 | + public function __construct(protected $factory) |
| 51 | + { |
| 52 | + $this->output = new \Symfony\Component\Console\Output\BufferedOutput(); |
| 53 | + } |
30 | 54 |
|
31 |
| - $types = collect($types) |
32 |
| - ->filter() |
33 |
| - ->values() |
34 |
| - ->map(fn($t) => $t->getName()); |
| 55 | + public function all() |
| 56 | + { |
| 57 | + collect(glob(base_path('**/Models/*.php')))->each(fn($file) => include_once($file)); |
35 | 58 |
|
36 |
| - return trim($types->join("|") . " $" . $param->getName()); |
37 |
| - }) |
38 |
| - ->all(); |
| 59 | + return collect(get_declared_classes()) |
| 60 | + ->filter(fn($class) => is_subclass_of($class, \Illuminate\Database\Eloquent\Model::class)) |
| 61 | + ->filter(fn($class) => !in_array($class, [\Illuminate\Database\Eloquent\Relations\Pivot::class, \Illuminate\Foundation\Auth\User::class])) |
| 62 | + ->values() |
| 63 | + ->flatMap(fn(string $className) => $this->getInfo($className)) |
| 64 | + ->filter(); |
| 65 | + } |
39 | 66 |
|
40 |
| - $return = $method->getReturnType()?->getName(); |
| 67 | + protected function getCastReturnType($className) |
| 68 | + { |
| 69 | + if ($className === null) { |
| 70 | + return null; |
| 71 | + } |
41 | 72 |
|
42 |
| - return [$params, $return]; |
43 |
| -} |
| 73 | + try { |
| 74 | + $method = (new \ReflectionClass($className))->getMethod('get'); |
44 | 75 |
|
45 |
| -function getBuilderMethod($method, $factory) |
46 |
| -{ |
47 |
| - [$params, $return] = getMethodDocblocks($method, $factory); |
| 76 | + if ($method->hasReturnType()) { |
| 77 | + return $method->getReturnType()->getName(); |
| 78 | + } |
48 | 79 |
|
49 |
| - return [ |
50 |
| - "name" => $method->getName(), |
51 |
| - "parameters" => $params, |
52 |
| - "return" => $return, |
53 |
| - ]; |
54 |
| -} |
| 80 | + return $className; |
| 81 | + } catch (\Exception | \Throwable $e) { |
| 82 | + return $className; |
| 83 | + } |
| 84 | + } |
55 | 85 |
|
56 |
| -function getCastReturnType($className) |
57 |
| -{ |
58 |
| - if ($className === null) { |
59 |
| - return null; |
| 86 | + protected function fromArtisan($className) |
| 87 | + { |
| 88 | + try { |
| 89 | + \Illuminate\Support\Facades\Artisan::call( |
| 90 | + "model:show", |
| 91 | + [ |
| 92 | + "model" => $className, |
| 93 | + "--json" => true, |
| 94 | + ], |
| 95 | + $this->output |
| 96 | + ); |
| 97 | + } catch (\Exception | \Throwable $e) { |
| 98 | + return null; |
| 99 | + } |
| 100 | + |
| 101 | + return json_decode($this->output->fetch(), true); |
60 | 102 | }
|
61 | 103 |
|
62 |
| - try { |
63 |
| - $class = new \ReflectionClass($className); |
64 |
| - $method = $class->getMethod('get'); |
| 104 | + protected function collectExistingProperties($reflection) |
| 105 | + { |
| 106 | + if ($this->factory === null) { |
| 107 | + return collect(); |
| 108 | + } |
| 109 | + |
| 110 | + if ($comment = $reflection->getDocComment()) { |
| 111 | + $docblock = $this->factory->create($comment); |
65 | 112 |
|
66 |
| - if ($method->hasReturnType()) { |
67 |
| - return $method->getReturnType()->getName(); |
| 113 | + $existingProperties = collect($docblock->getTagsByName("property"))->map(fn($p) => $p->getVariableName()); |
| 114 | + $existingReadProperties = collect($docblock->getTagsByName("property-read"))->map(fn($p) => $p->getVariableName()); |
| 115 | + |
| 116 | + return $existingProperties->merge($existingReadProperties); |
68 | 117 | }
|
69 | 118 |
|
70 |
| - return $className; |
71 |
| - } catch (\Exception | \Throwable $e) { |
72 |
| - return $className; |
| 119 | + return collect(); |
73 | 120 | }
|
74 |
| -} |
75 | 121 |
|
76 |
| -function getModelInfo($className, $factory) |
77 |
| -{ |
78 |
| - $output = new \Symfony\Component\Console\Output\BufferedOutput(); |
79 |
| - |
80 |
| - try { |
81 |
| - \Illuminate\Support\Facades\Artisan::call( |
82 |
| - "model:show", |
83 |
| - [ |
84 |
| - "model" => $className, |
85 |
| - "--json" => true, |
86 |
| - ], |
87 |
| - $output |
88 |
| - ); |
89 |
| - } catch (\Exception | \Throwable $e) { |
90 |
| - return null; |
91 |
| - } |
| 122 | + protected function getInfo($className) |
| 123 | + { |
| 124 | + if (($data = $this->fromArtisan($className)) === null) { |
| 125 | + return null; |
| 126 | + } |
92 | 127 |
|
93 |
| - $data = json_decode($output->fetch(), true); |
| 128 | + $reflection = (new \ReflectionClass($className)); |
94 | 129 |
|
95 |
| - if ($data === null) { |
96 |
| - return null; |
| 130 | + $existingProperties = $this->collectExistingProperties($reflection); |
| 131 | + |
| 132 | + $data['attributes'] = collect($data['attributes']) |
| 133 | + ->map(fn($attrs) => array_merge($attrs, [ |
| 134 | + 'title_case' => \Illuminate\Support\Str::of($attrs['name'])->title()->replace('_', '')->toString(), |
| 135 | + 'documented' => $existingProperties->contains($attrs['name']), |
| 136 | + 'cast' => $this->getCastReturnType($attrs['cast']) |
| 137 | + ])) |
| 138 | + ->toArray(); |
| 139 | + |
| 140 | + $data['scopes'] = collect($reflection->getMethods()) |
| 141 | + ->filter(fn($method) => $method->isPublic() && !$method->isStatic() && str_starts_with($method->name, 'scope')) |
| 142 | + ->map(fn($method) => \Illuminate\Support\Str::of($method->name)->replace('scope', '')->lcfirst()->toString()) |
| 143 | + ->values() |
| 144 | + ->toArray(); |
| 145 | + |
| 146 | + $data['uri'] = $reflection->getFileName(); |
| 147 | + |
| 148 | + return [ |
| 149 | + $className => $data, |
| 150 | + ]; |
97 | 151 | }
|
| 152 | +}; |
98 | 153 |
|
99 |
| - $reflection = (new \ReflectionClass($className)); |
| 154 | +$builder = new class($docblocks) { |
| 155 | + public function __construct(protected $docblocks) {} |
100 | 156 |
|
101 |
| - if ($factory !== null && ($comment = $reflection->getDocComment())) { |
102 |
| - $docblock = $factory->create($comment); |
103 |
| - $existingProperties = collect($docblock->getTagsByName("property"))->map(fn($p) => $p->getVariableName()); |
104 |
| - $existingReadProperties = collect($docblock->getTagsByName("property-read"))->map(fn($p) => $p->getVariableName()); |
105 |
| - $existingProperties = $existingProperties->merge($existingReadProperties); |
106 |
| - } else { |
107 |
| - $existingProperties = collect(); |
| 157 | + public function methods() |
| 158 | + { |
| 159 | + $reflection = new \ReflectionClass(\Illuminate\Database\Query\Builder::class); |
| 160 | + |
| 161 | + return collect($reflection->getMethods(\ReflectionMethod::IS_PUBLIC)) |
| 162 | + ->filter(fn(ReflectionMethod $method) => !str_starts_with($method->getName(), "__")) |
| 163 | + ->map(fn(\ReflectionMethod $method) => $this->getMethodInfo($method)) |
| 164 | + ->filter() |
| 165 | + ->values(); |
108 | 166 | }
|
109 | 167 |
|
110 |
| - $data['attributes'] = collect($data['attributes']) |
111 |
| - ->map(fn($attrs) => array_merge($attrs, [ |
112 |
| - 'title_case' => str_replace('_', '', \Illuminate\Support\Str::title($attrs['name'])), |
113 |
| - 'documented' => $existingProperties->contains($attrs['name']), |
114 |
| - 'cast' => getCastReturnType($attrs['cast']) |
115 |
| - ])) |
116 |
| - ->toArray(); |
117 |
| - |
118 |
| - $data['scopes'] = collect($reflection->getMethods()) |
119 |
| - ->filter(fn($method) => $method->isPublic() && !$method->isStatic() && $method->name !== '__construct') |
120 |
| - ->filter(fn($method) => str_starts_with($method->name, 'scope')) |
121 |
| - ->map(fn($method) => str_replace('scope', '', $method->name)) |
122 |
| - ->map(fn($method) => strtolower(substr($method, 0, 1)) . substr($method, 1)) |
123 |
| - ->values() |
124 |
| - ->toArray(); |
125 |
| - |
126 |
| - $data['uri'] = $reflection->getFileName(); |
127 |
| - |
128 |
| - return [ |
129 |
| - $className => $data, |
130 |
| - ]; |
131 |
| -} |
| 168 | + protected function getMethodInfo($method) |
| 169 | + { |
| 170 | + [$params, $return] = $this->docblocks->forMethod($method); |
| 171 | + |
| 172 | + return [ |
| 173 | + "name" => $method->getName(), |
| 174 | + "parameters" => $params, |
| 175 | + "return" => $return, |
| 176 | + ]; |
| 177 | + } |
| 178 | +}; |
132 | 179 |
|
133 |
| -$reflection = new \ReflectionClass(\Illuminate\Database\Query\Builder::class); |
134 |
| -$builderMethods = collect($reflection->getMethods(\ReflectionMethod::IS_PUBLIC)) |
135 |
| - ->filter(fn(ReflectionMethod $method) => !str_starts_with($method->getName(), "__")) |
136 |
| - ->map(fn(\ReflectionMethod $method) => getBuilderMethod($method, $factory)) |
137 |
| - ->filter() |
138 |
| - ->values(); |
139 |
| - |
140 |
| -echo collect([ |
141 |
| - 'builderMethods' => $builderMethods, |
142 |
| - 'models' => collect(get_declared_classes()) |
143 |
| - ->filter(fn($class) => is_subclass_of($class, \Illuminate\Database\Eloquent\Model::class)) |
144 |
| - ->filter(fn($class) => !in_array($class, [\Illuminate\Database\Eloquent\Relations\Pivot::class, \Illuminate\Foundation\Auth\User::class])) |
145 |
| - ->values() |
146 |
| - ->flatMap(fn(string $className) => getModelInfo($className, $factory)) |
147 |
| - ->filter(), |
148 |
| -])->toJson(); |
| 180 | +echo json_encode([ |
| 181 | + 'builderMethods' => $builder->methods(), |
| 182 | + 'models' => $models->all(), |
| 183 | +]); |
0 commit comments