Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#685)
by
unknown
05:56
created

AbstractClassGenerator::clearImplements()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the OverblogGraphQLPhpGenerator package.
5
 *
6
 * (c) Overblog <http://github.com/overblog/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Overblog\GraphQLGenerator\Generator;
13
14
use Overblog\GraphQLGenerator\ClassUtils;
15
16
abstract class AbstractClassGenerator
17
{
18
    public const MODE_DRY_RUN = 1;
19
    public const MODE_MAPPING_ONLY = 2;
20
    public const MODE_WRITE = 4;
21
    public const MODE_OVERRIDE = 8;
22
23
    protected const SKELETON_FILE_PREFIX = '.php.skeleton';
24
25
    /**
26
     * The namespace that contains all classes.
27
     *
28
     * @var string
29
     */
30
    private $classNamespace;
31
32
    private $internalUseStatements = [];
33
34
    private $useStatements = [];
35
36
    private $traits = [];
37
38
    private $implements = [];
39
40
    private $skeletonDirs = [];
41
42
    /**
43
     * Number of spaces to use for indention in generated code.
44
     */
45
    private $numSpaces;
46
47
    /**
48
     * The actual spaces to use for indention.
49
     *
50
     * @var string
51
     */
52
    private $spaces;
53
54
    private static $templates = [];
55
56
    /**
57
     * @param string $classNamespace The namespace to use for the classes.
58
     * @param string[]|string $skeletonDirs
59
     */
60
    public function __construct($classNamespace = null, $skeletonDirs = [])
61
    {
62
        $this->setClassNamespace($classNamespace);
63
        $this->setSkeletonDirs($skeletonDirs);
64
        $this->setNumSpaces(4);
65
    }
66
67
    public function getClassNamespace()
68
    {
69
        return $this->classNamespace;
70
    }
71
72
    public function setClassNamespace($classNamespace): self
73
    {
74
        $this->classNamespace = ClassUtils::cleanClasseName($classNamespace);
75
76
        return $this;
77
    }
78
79
    public function setSkeletonDirs($skeletonDirs): self
80
    {
81
        $this->skeletonDirs = [];
82
83
        if (\is_string($skeletonDirs)) {
84
            $this->addSkeletonDir($skeletonDirs);
85
        } else {
86
            if (!\is_array($skeletonDirs) && !$skeletonDirs instanceof \Traversable) {
87
                throw new \InvalidArgumentException(
88
                    \sprintf('Skeleton dirs must be array or object implementing \Traversable interface, "%s" given.', \gettype($skeletonDirs))
89
                );
90
            }
91
92
            foreach ($skeletonDirs as $skeletonDir) {
93
                $this->addSkeletonDir($skeletonDir);
94
            }
95
        }
96
97
        return $this;
98
    }
99
100
    public function getSkeletonDirs(bool $withDefault = true): array
101
    {
102
        $skeletonDirs = $this->skeletonDirs ;
103
104
        if ($withDefault) {
105
            $skeletonDirs[] = __DIR__.'/../Resources/skeleton';
106
        }
107
108
        return $skeletonDirs;
109
    }
110
111
    public function addSkeletonDir($skeletonDir): self
112
    {
113
        if (!\is_string($skeletonDir) && !\is_object($skeletonDir) && !\is_callable([$skeletonDir, '__toString'])) {
114
            throw new \InvalidArgumentException(
115
                \sprintf('Skeleton dir must be string or object implementing __toString, "%s" given.', \gettype($skeletonDir))
116
            );
117
        }
118
119
        $skeletonDir = (string) $skeletonDir;
120
121
        if (!\is_dir($skeletonDir)) {
122
            throw new \InvalidArgumentException(\sprintf('Skeleton dir "%s" not found.', $skeletonDir));
123
        }
124
        $this->skeletonDirs[] = \realpath($skeletonDir);
125
126
        return $this;
127
    }
128
129
130
    /**
131
     * Sets the number of spaces the exported class should have.
132
     *
133
     * @param integer $numSpaces
134
     *
135
     * @return self
136
     */
137
    public function setNumSpaces(int $numSpaces): self
138
    {
139
        $this->spaces = \str_repeat(' ', $numSpaces);
140
        $this->numSpaces = $numSpaces;
141
142
        return $this;
143
    }
144
145
    public function addTrait(string $trait): self
146
    {
147
        $cleanTrait = $this->shortenClassName($trait, false);
148
        if (!\in_array($cleanTrait, $this->traits)) {
149
            $this->traits[] = $cleanTrait;
150
        }
151
152
        return $this;
153
    }
154
155
    public function clearTraits(): self
156
    {
157
        $this->traits = [];
158
159
        return $this;
160
    }
161
162
    public function addImplement(string $implement): self
163
    {
164
        $cleanImplement = $this->shortenClassName($implement, false);
165
        if (!\in_array($cleanImplement, $this->implements)) {
166
            $this->implements[] = $cleanImplement;
167
        }
168
169
        return $this;
170
    }
171
172
    public function clearImplements(): self
173
    {
174
        $this->implements = [];
175
176
        return $this;
177
    }
178
179
    public function addUseStatement(string $useStatement): self
180
    {
181
        $cleanUse = ClassUtils::cleanClasseName($useStatement);
182
        if (!\in_array($cleanUse, $this->useStatements)) {
183
            $this->useStatements[] = $cleanUse;
184
        }
185
186
        return $this;
187
    }
188
189
    public function clearUseStatements(): self
190
    {
191
        $this->useStatements = [];
192
193
        return $this;
194
    }
195
196
    public function getSkeletonContent(string $skeleton, bool $withDefault = true)
197
    {
198
        $skeletonDirs = $this->getSkeletonDirs($withDefault);
199
200
        foreach ($skeletonDirs as $skeletonDir) {
201
            $path = $skeletonDir.'/'.$skeleton.static::SKELETON_FILE_PREFIX;
202
203
            if (!\file_exists($path)) {
204
                continue;
205
            }
206
207
            if (!isset(self::$templates[$path])) {
208
                $content = \trim(\file_get_contents($path));
209
210
                self::$templates[$path] = $content;
211
            }
212
213
            return self::$templates[$path];
214
        }
215
216
        throw new \InvalidArgumentException(
217
            \sprintf(
218
                'Skeleton "%s" could not be found in %s.',
219
                $skeleton,
220
                \implode(', ', $skeletonDirs)
221
            )
222
        );
223
    }
224
225
    protected function addInternalUseStatement(string $use): void
226
    {
227
        $cleanUse = ClassUtils::cleanClasseName($use);
228
        if (!\in_array($cleanUse, $this->internalUseStatements)) {
229
            $this->internalUseStatements[] = $cleanUse;
230
        }
231
    }
232
233
    protected function clearInternalUseStatements(): self
234
    {
235
        $this->internalUseStatements = [];
236
237
        return $this;
238
    }
239
240
    protected function shortenClassName(string $definition, bool $isInternal = true): string
241
    {
242
        $shortName = ClassUtils::shortenClassName($definition);
243
244
        $useStatement = \preg_replace('@\:\:.*$@i', '', $definition);
245
        if ($isInternal) {
246
            $this->addInternalUseStatement($useStatement);
247
        } else {
248
            $this->addUseStatement($useStatement);
249
        }
250
251
        return $shortName;
252
    }
253
254
    protected function shortenClassFromCode(?string $code): string
255
    {
256
        $codeParsed = ClassUtils::shortenClassFromCode(
257
            $code,
258
            function ($matches) {
259
                return $this->shortenClassName($matches[1]);
260
            }
261
        );
262
263
        return $codeParsed;
264
    }
265
266
    protected function processPlaceHoldersReplacements(array $placeHolders, string $content, array $values): string
267
    {
268
        $replacements = [];
269
270
        foreach ($placeHolders as $placeHolder) {
271
            $generator = [$this, 'generate'.\ucfirst($placeHolder)];
272
            $name = '<'.$placeHolder.'>';
273
274
            if (\is_callable($generator)) {
275
                $replacements[$name] = \call_user_func_array($generator, [$values]);
276
            } else {
277
                throw new \RuntimeException(
278
                    \sprintf(
279
                        'Generator [%s] for placeholder "%s" is not callable.',
280
                        \get_class($generator[0]).'::'.$generator[1],
281
                        $placeHolder
282
                    )
283
                );
284
            }
285
        }
286
287
        return \strtr($content, $replacements);
288
    }
289
290
    protected function processTemplatePlaceHoldersReplacements(string $template, array $values, array $skip = []): string
291
    {
292
        $code = $this->getSkeletonContent($template);
293
        $placeHolders = $this->getPlaceHolders($code);
294
        $code = $this->processPlaceHoldersReplacements(\array_diff($placeHolders, $skip), $code, $values);
295
296
        return $code;
297
    }
298
299
    protected function getPlaceHolders(string $content): array
300
    {
301
        \preg_match_all('@<([\w]+)>@i', $content, $placeHolders);
302
303
        return $placeHolders[1] ?? [];
304
    }
305
306
    /**
307
     * @param string $code      Code to prefix
308
     * @param int    $num       Number of indents
309
     * @param bool   $skipFirst Skip first line
310
     *
311
     * @return string
312
     */
313
    protected function prefixCodeWithSpaces(string $code, int $num = 1, bool $skipFirst = true): string
314
    {
315
        $lines = \explode("\n", $code);
316
317
        foreach ($lines as $key => $value) {
318
            if (!empty($value)) {
319
                $lines[$key] = \str_repeat($this->spaces, $num).$lines[$key];
320
            }
321
        }
322
323
        if ($skipFirst) {
324
            $lines[0] = \ltrim($lines[0]);
325
        }
326
327
        return \implode("\n", $lines);
328
    }
329
330
    protected function generateSpaces(): string
331
    {
332
        return $this->spaces;
333
    }
334
335
    protected function generateNamespace(): ?string
336
    {
337
        return null !== $this->classNamespace ? 'namespace '.$this->classNamespace.';' : null;
338
    }
339
340
    protected function generateUseStatement(array $config): string
0 ignored issues
show
Unused Code introduced by
The parameter $config is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

340
    protected function generateUseStatement(/** @scrutinizer ignore-unused */ array $config): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
341
    {
342
        $statements = \array_merge($this->internalUseStatements, $this->useStatements);
343
        \sort($statements);
344
345
        $useStatements = $this->tokenizeUseStatements($statements);
346
347
        return $useStatements;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $useStatements could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
348
    }
349
350
    protected function generateClassType(): string
351
    {
352
        return 'final ';
353
    }
354
355
    protected function generateImplements(): ?string
356
    {
357
        return \count($this->implements) ? ' implements '.\implode(', ', $this->implements) : null;
358
    }
359
360
    protected function generateTraits(): ?string
361
    {
362
        $traits = $this->tokenizeUseStatements($this->traits, '<spaces>');
363
364
        return $traits ? $traits."\n" : $traits;
365
    }
366
367
    protected function tokenizeUseStatements(array $useStatements, $prefix = ''): ?string
368
    {
369
        if (empty($useStatements)) {
370
            return null;
371
        }
372
373
        $code = '';
374
375
        foreach ($useStatements as $useStatement) {
376
            $code .= "\n${prefix}use $useStatement;";
377
        }
378
379
        return $code;
380
    }
381
382
    /**
383
     * Generates classes files.
384
     *
385
     * @param array    $configs raw configs
386
     * @param string   $outputDirectory
387
     * @param int $mode
388
     *
389
     * @return array classes map [[FQCLN => classPath], [FQCLN => classPath], ...]
390
     */
391
    abstract public function generateClasses(array $configs, ?string $outputDirectory, int $mode = self::MODE_WRITE): array;
392
393
    /**
394
     * Generates a class file.
395
     *
396
     * @param array  $config
397
     * @param string $outputDirectory
398
     * @param int    $mode
399
     *
400
     * @return array classes map [FQCLN => classPath]
401
     */
402
    abstract public function generateClass(array $config, ?string $outputDirectory, int $mode = self::MODE_WRITE): array;
403
}
404