We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.
Total Complexity | 56 |
Total Lines | 353 |
Duplicated Lines | 0 % |
Coverage | 100% |
Changes | 0 |
Complex classes like AbstractTypeGenerator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractTypeGenerator, and based on these observations, apply Extract Interface, too.
1 | <?php declare(strict_types=1); |
||
24 | abstract class AbstractTypeGenerator extends AbstractClassGenerator |
||
25 | { |
||
26 | public const DEFAULT_CLASS_NAMESPACE = 'Overblog\\CG\\GraphQLGenerator\\__Schema__'; |
||
27 | |||
28 | protected const DEFERRED_PLACEHOLDERS = ['useStatement', 'spaces', 'closureUseStatements']; |
||
29 | |||
30 | protected const CLOSURE_TEMPLATE = <<<EOF |
||
31 | function (%s) <closureUseStatements>{ |
||
32 | <spaces><spaces>return %s; |
||
33 | <spaces>} |
||
34 | EOF; |
||
35 | |||
36 | private const TYPE_SYSTEMS = [ |
||
37 | 'object' => ObjectType::class, |
||
38 | 'interface' => InterfaceType::class, |
||
39 | 'enum' => EnumType::class, |
||
40 | 'union' => UnionType::class, |
||
41 | 'input-object' => InputObjectType::class, |
||
42 | 'custom-scalar' => CustomScalarType::class, |
||
43 | ]; |
||
44 | |||
45 | private const INTERNAL_TYPES = [ |
||
46 | Type::STRING => '\\GraphQL\\Type\\Definition\\Type::string()', |
||
47 | Type::INT => '\\GraphQL\\Type\\Definition\\Type::int()', |
||
48 | Type::FLOAT => '\\GraphQL\\Type\\Definition\\Type::float()', |
||
49 | Type::BOOLEAN => '\\GraphQL\\Type\\Definition\\Type::boolean()', |
||
50 | Type::ID => '\\GraphQL\\Type\\Definition\\Type::id()', |
||
51 | ]; |
||
52 | |||
53 | private const WRAPPED_TYPES = [ |
||
54 | 'NonNull' => '\\GraphQL\\Type\\Definition\\Type::nonNull', |
||
55 | 'ListOf' => '\\GraphQL\\Type\\Definition\\Type::listOf', |
||
56 | ]; |
||
57 | |||
58 | private $canManageExpressionLanguage = false; |
||
59 | |||
60 | /** |
||
61 | * @var null|ExpressionLanguage |
||
62 | */ |
||
63 | private $expressionLanguage; |
||
64 | |||
65 | /** |
||
66 | * @var int |
||
67 | */ |
||
68 | protected $cacheDirMask; |
||
69 | |||
70 | /** |
||
71 | * @param string $classNamespace The namespace to use for the classes. |
||
72 | * @param string[]|string $skeletonDirs |
||
73 | * @param int $cacheDirMask |
||
74 | */ |
||
75 | 102 | public function __construct(string $classNamespace = self::DEFAULT_CLASS_NAMESPACE, $skeletonDirs = [], int $cacheDirMask = 0775) |
|
76 | { |
||
77 | 102 | parent::__construct($classNamespace, $skeletonDirs); |
|
78 | 102 | $this->cacheDirMask = $cacheDirMask; |
|
79 | 102 | } |
|
80 | |||
81 | 97 | public function setExpressionLanguage(ExpressionLanguage $expressionLanguage = null): self |
|
82 | { |
||
83 | 97 | $this->expressionLanguage = $expressionLanguage; |
|
84 | 97 | $this->canManageExpressionLanguage = null !== $expressionLanguage; |
|
85 | |||
86 | 97 | return $this; |
|
87 | } |
||
88 | |||
89 | 55 | public function getExpressionLanguage(): ExpressionLanguage |
|
90 | { |
||
91 | 55 | return $this->expressionLanguage; |
|
|
|||
92 | } |
||
93 | |||
94 | 61 | public function isExpression($str): bool |
|
95 | { |
||
96 | 61 | return $this->canManageExpressionLanguage && $str instanceof Expression; |
|
97 | } |
||
98 | |||
99 | 60 | public static function getInternalTypes(string $name): ?string |
|
100 | { |
||
101 | 60 | return isset(self::INTERNAL_TYPES[$name]) ? self::INTERNAL_TYPES[$name] : null; |
|
102 | } |
||
103 | |||
104 | 60 | public static function getWrappedType(string $name): ?string |
|
105 | { |
||
106 | 60 | return isset(self::WRAPPED_TYPES[$name]) ? self::WRAPPED_TYPES[$name] : null; |
|
107 | } |
||
108 | |||
109 | 61 | protected function generateParentClassName(array $config): string |
|
110 | { |
||
111 | 61 | return $this->shortenClassName(self::TYPE_SYSTEMS[$config['type']]); |
|
112 | } |
||
113 | |||
114 | 32 | protected function generateClassName(array $config): string |
|
115 | { |
||
116 | 32 | return $config['config']['name'].'Type'; |
|
117 | } |
||
118 | |||
119 | 29 | protected function generateClassDocBlock(array $config): string |
|
120 | { |
||
121 | 29 | $className = $this->generateClassName($config); |
|
122 | 29 | $namespace = $this->getClassNamespace(); |
|
123 | |||
124 | return <<<EOF |
||
125 | |||
126 | /** |
||
127 | 29 | * Class $className |
|
128 | 29 | * @package $namespace |
|
129 | */ |
||
130 | EOF; |
||
131 | } |
||
132 | |||
133 | 61 | protected function varExportFromArrayValue(array $values, string $key, string $default = 'null', array $compilerNames = []): string |
|
134 | { |
||
135 | 61 | if (!isset($values[$key])) { |
|
136 | 61 | return $default; |
|
137 | } |
||
138 | |||
139 | 61 | $code = $this->varExport($values[$key], $default, $compilerNames); |
|
140 | |||
141 | 61 | return $code; |
|
142 | } |
||
143 | |||
144 | 61 | protected function varExport($var, ?string $default = null, array $compilerNames = []): ?string |
|
145 | { |
||
146 | switch (true) { |
||
147 | 61 | case \is_array($var): |
|
148 | 27 | $indexed = \array_keys($var) === \range(0, \count($var) - 1); |
|
149 | 27 | $r = []; |
|
150 | 27 | foreach ($var as $key => $value) { |
|
151 | 27 | $r[] = ($indexed ? '' : $this->varExport($key, $default).' => ') |
|
152 | 27 | .$this->varExport($value, $default); |
|
153 | } |
||
154 | 27 | return "[".\implode(", ", $r)."]"; |
|
155 | |||
156 | 61 | case $this->isExpression($var): |
|
157 | 23 | return $code = $this->getExpressionLanguage()->compile($var, $compilerNames); |
|
158 | |||
159 | 61 | case \is_object($var): |
|
160 | 1 | return $default; |
|
161 | |||
162 | 61 | case \is_string($var): |
|
163 | 61 | $string = \var_export($var, true); |
|
164 | |||
165 | // handle multi-line strings |
||
166 | 61 | $lines = \explode("\n", $string); |
|
167 | 61 | if (\count($lines) > 1) { |
|
168 | 23 | $firstLine = \sprintf('%s\' . "\n"', \array_shift($lines)); |
|
169 | 23 | $lastLine = \sprintf("'%s", \array_pop($lines)); |
|
170 | 23 | $lines = \array_map( |
|
171 | function ($line) { |
||
172 | 23 | return \sprintf('\'%s\' . "\n"', $line); |
|
173 | 23 | }, |
|
174 | 23 | $lines |
|
175 | ); |
||
176 | 23 | \array_unshift($lines, $firstLine); |
|
177 | 23 | \array_push($lines, $lastLine); |
|
178 | 23 | $string = \implode(' . ', $lines); |
|
179 | } |
||
180 | |||
181 | 61 | return $string; |
|
182 | |||
183 | default: |
||
184 | 27 | return \var_export($var, true); |
|
185 | } |
||
186 | } |
||
187 | |||
188 | 61 | protected function processFromArray(array $values, string $templatePrefix) |
|
189 | { |
||
190 | 61 | $code = ''; |
|
191 | |||
192 | 61 | foreach ($values as $name => $value) { |
|
193 | 61 | $value['name'] = $value['name'] ?? $name; |
|
194 | 61 | $code .= "\n".$this->processTemplatePlaceHoldersReplacements($templatePrefix.'Config', $value); |
|
195 | } |
||
196 | |||
197 | 60 | return '['.$this->prefixCodeWithSpaces($code, 2)."\n<spaces>]"; |
|
198 | } |
||
199 | |||
200 | 60 | protected function callableCallbackFromArrayValue(array $value, string $key, ?string $argDefinitions = null, string $default = 'null', array $compilerNames = null) |
|
201 | { |
||
202 | 60 | if (!$this->arrayKeyExistsAndIsNotNull($value, $key)) { |
|
203 | 60 | return $default; |
|
204 | } |
||
205 | |||
206 | 52 | $code = static::CLOSURE_TEMPLATE; |
|
207 | |||
208 | 52 | if (\is_callable($value[$key])) { |
|
209 | 25 | $func = $value[$key]; |
|
210 | 25 | $code = \sprintf($code, null, '\call_user_func_array(%s, \func_get_args())'); |
|
211 | |||
212 | 25 | if (\is_array($func) && isset($func[0]) && \is_string($func[0])) { |
|
213 | 25 | $code = \sprintf($code, $this->varExport($func)); |
|
214 | |||
215 | 25 | return $code; |
|
216 | 23 | } elseif (\is_string($func)) { |
|
217 | 23 | $code = \sprintf($code, \var_export($func, true)); |
|
218 | |||
219 | 23 | return $code; |
|
220 | } |
||
221 | 50 | } elseif ($this->isExpression($value[$key])) { |
|
222 | 48 | if (null === $compilerNames) { |
|
223 | 48 | $compilerNames = []; |
|
224 | 48 | if (null !== $argDefinitions) { |
|
225 | 48 | \preg_match_all('@\$([a-z_][a-z0-9_]+)@i', $argDefinitions, $matches); |
|
226 | 48 | $compilerNames = $matches[1] ?? []; |
|
227 | } |
||
228 | } |
||
229 | 48 | $code = \sprintf( |
|
230 | 48 | $code, |
|
231 | 48 | $this->shortenClassFromCode($argDefinitions), |
|
232 | 48 | $this->getExpressionLanguage()->compile($value[$key], $compilerNames) |
|
233 | ); |
||
234 | |||
235 | 48 | return $code; |
|
236 | 19 | } elseif (!\is_object($value[$key])) { |
|
237 | 19 | $code = \sprintf($code, null, $this->varExportFromArrayValue($value, $key, $default)); |
|
238 | |||
239 | 19 | return $code; |
|
240 | } |
||
241 | |||
242 | 1 | return $default; |
|
243 | } |
||
244 | |||
245 | 61 | protected function generateConfig(array $config) |
|
246 | { |
||
247 | 61 | $template = \str_replace(' ', '', \ucwords(\str_replace('-', ' ', $config['type']))).'Config'; |
|
248 | 61 | $code = $this->processTemplatePlaceHoldersReplacements($template, $config['config']); |
|
249 | 60 | $code = \ltrim($this->prefixCodeWithSpaces($code, 2)); |
|
250 | |||
251 | 60 | return $code; |
|
252 | } |
||
253 | |||
254 | 28 | protected function generateClosureUseStatements(array $config): ?string |
|
255 | { |
||
256 | 28 | return null; |
|
257 | } |
||
258 | |||
259 | 61 | protected function typeAlias2String($alias): string |
|
260 | { |
||
261 | // Non-Null |
||
262 | 61 | if ('!' === $alias[\strlen($alias) - 1]) { |
|
263 | 59 | return \sprintf('%s(%s)', $this->shortenClassName(static::getWrappedType('NonNull')), $this->typeAlias2String(\substr($alias, 0, -1))); |
|
264 | } |
||
265 | // List |
||
266 | 61 | if ('[' === $alias[0]) { |
|
267 | 48 | $got = $alias[\strlen($alias) - 1]; |
|
268 | 48 | if (']' !== $got) { |
|
269 | 1 | throw new \RuntimeException( |
|
270 | 1 | \sprintf('Malformed ListOf wrapper type %s expected "]" but got %s.', \json_encode($alias), \json_encode($got)) |
|
271 | ); |
||
272 | } |
||
273 | |||
274 | 47 | return \sprintf('%s(%s)', $this->shortenClassName(static::getWrappedType('ListOf')), $this->typeAlias2String(\substr($alias, 1, -1))); |
|
275 | } |
||
276 | |||
277 | 60 | if (null !== ($systemType = static::getInternalTypes($alias))) { |
|
278 | 60 | return $this->shortenClassName($systemType); |
|
279 | } |
||
280 | |||
281 | 49 | return $this->resolveTypeCode($alias); |
|
282 | } |
||
283 | |||
284 | 23 | protected function resolveTypeCode(string $alias): string |
|
287 | } |
||
288 | |||
289 | 60 | protected function resolveTypesCode(array $values, string $key): string |
|
290 | { |
||
291 | 60 | if (isset($values[$key])) { |
|
292 | 55 | $types = \sprintf(static::CLOSURE_TEMPLATE, '', $this->types2String($values[$key])); |
|
293 | } else { |
||
294 | 28 | $types = '[]'; |
|
295 | } |
||
296 | |||
297 | 60 | return $types; |
|
298 | } |
||
299 | |||
300 | 55 | protected function types2String(array $types): string |
|
305 | } |
||
306 | |||
307 | 60 | protected function arrayKeyExistsAndIsNotNull(array $value, $key): bool |
|
308 | { |
||
309 | 60 | return \array_key_exists($key, $value) && null !== $value[$key]; |
|
310 | } |
||
311 | |||
312 | /** |
||
313 | * Configs has the following structure: |
||
314 | * <code> |
||
315 | * [ |
||
316 | * [ |
||
317 | * 'type' => 'object', // the file type |
||
318 | * 'config' => [], // the class config |
||
319 | * ], |
||
320 | * [ |
||
321 | * 'type' => 'interface', |
||
322 | * 'config' => [], |
||
323 | * ], |
||
324 | * //... |
||
325 | * ] |
||
326 | * </code> |
||
327 | * |
||
328 | * @param array $configs |
||
329 | * @param string $outputDirectory |
||
330 | * @param int $mode |
||
331 | * |
||
332 | * @return array |
||
333 | */ |
||
334 | 65 | public function generateClasses(array $configs, ?string $outputDirectory, int $mode = self::MODE_WRITE): array |
|
346 | } |
||
347 | |||
348 | /** |
||
349 | * @param array $config |
||
350 | * @param string $outputDirectory |
||
351 | * @param int $mode |
||
352 | * |
||
353 | * @return array |
||
354 | */ |
||
355 | 65 | public function generateClass(array $config, ?string $outputDirectory, int $mode = self::MODE_WRITE): array |
|
377 | } |
||
378 | } |
||
379 |