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

Completed
Pull Request — master (#607)
by
unknown
21:06
created

processPlaceHoldersReplacements()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 22
rs 9.8333
c 0
b 0
f 0
ccs 10
cts 10
cp 1
cc 3
nc 3
nop 3
crap 3
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
use Overblog\GraphQLGenerator\Exception\GeneratorException;
16
17
abstract class AbstractClassGenerator
18
{
19
    public const MODE_DRY_RUN = 1;
20
    public const MODE_MAPPING_ONLY = 2;
21
    public const MODE_WRITE = 4;
22
    public const MODE_OVERRIDE = 8;
23
24
    protected const SKELETON_FILE_PREFIX = '.php.skeleton';
25
26
    /**
27
     * The namespace that contains all classes.
28
     *
29
     * @var string
30
     */
31
    private $classNamespace;
32
33
    /**
34
     * @var array<string>
35
     */
36
    private $internalUseStatements = [];
37
38
    /**
39
     * @var array<string>
40
     */
41
    private $useStatements = [];
42
43
    /**
44
     * @var array<string>
45
     */
46
    private $traits = [];
47
48
    /**
49
     * @var array<string>
50
     */
51
    private $implements = [];
52
53
    /**
54
     * @var array<string>
55
     */
56
    private $skeletonDirs = [];
57
58
    /**
59
     * Number of spaces to use for indention in generated code.
60 182
     *
61
     * @var int
62 182
     */
63 182
    private $numSpaces;
64 182
65 182
    /**
66
     * The actual spaces to use for indention.
67 69
     *
68
     * @var string
69 69
     */
70
    private $spaces;
71
72 182
    /**
73
     * @var array<string>
74 182
     */
75
    private static $templates = [];
76 182
77
    /**
78
     * @param string $classNamespace The namespace to use for the classes.
79 182
     * @param string[]|string $skeletonDirs
80
     */
81 182
    public function __construct($classNamespace = null, $skeletonDirs = [])
82
    {
83 182
        $this->setClassNamespace($classNamespace);
0 ignored issues
show
Bug introduced by
It seems like $classNamespace can also be of type null; however, parameter $classNamespace of Overblog\GraphQLGenerato...or::setClassNamespace() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

83
        $this->setClassNamespace(/** @scrutinizer ignore-type */ $classNamespace);
Loading history...
84 1
        $this->setSkeletonDirs($skeletonDirs);
85
        $this->setNumSpaces(4);
86 182
    }
87 1
88 1
    public function getClassNamespace()
89
    {
90
        return $this->classNamespace;
91
    }
92 182
93 144
    public function setClassNamespace(string $classNamespace): self
94
    {
95
        $this->classNamespace = ClassUtils::cleanClasseName($classNamespace);
96
97 182
        return $this;
98
    }
99
100 68
    /**
101
     * @param string|array<string> $skeletonDirs
102 68
     */
103
    public function setSkeletonDirs($skeletonDirs): self
104 68
    {
105 68
        $this->skeletonDirs = [];
106
107
        if (\is_string($skeletonDirs)) {
108 68
            $this->addSkeletonDir($skeletonDirs);
109
        } else {
110
            if (!\is_array($skeletonDirs) && !$skeletonDirs instanceof \Traversable) {
0 ignored issues
show
introduced by
The condition is_array($skeletonDirs) is always true.
Loading history...
111 146
                throw new \InvalidArgumentException(
112
                    \sprintf('Skeleton dirs must be array or object implementing \Traversable interface, "%s" given.', \gettype($skeletonDirs))
113 146
                );
114 1
            }
115 1
116
            foreach ($skeletonDirs as $skeletonDir) {
117
                $this->addSkeletonDir($skeletonDir);
118
            }
119 145
        }
120
121 145
        return $this;
122 1
    }
123
124 144
    /**
125
     * @return array<string>
126 144
     */
127
    public function getSkeletonDirs(bool $withDefault = true): array
128
    {
129
        $skeletonDirs = $this->skeletonDirs ;
130
131
        if ($withDefault) {
132
            $skeletonDirs[] = __DIR__.'/../Resources/skeleton';
133
        }
134
135
        return $skeletonDirs;
136
    }
137 182
138
    /**
139 182
     * @param string|object $skeletonDir
140 182
     */
141
    public function addSkeletonDir($skeletonDir): self
142 182
    {
143
        if (!\is_string($skeletonDir) && !\is_object($skeletonDir) && !\is_callable([$skeletonDir, '__toString'])) {
144
            throw new \InvalidArgumentException(
145 1
                \sprintf('Skeleton dir must be string or object implementing __toString, "%s" given.', \gettype($skeletonDir))
146
            );
147 1
        }
148 1
149 1
        $skeletonDir = (string) $skeletonDir;
150
151
        if (!\is_dir($skeletonDir)) {
152 1
            throw new \InvalidArgumentException(\sprintf('Skeleton dir "%s" not found.', $skeletonDir));
153
        }
154
        $this->skeletonDirs[] = \realpath($skeletonDir);
155 1
156
        return $this;
157 1
    }
158
159 1
160
    /**
161
     * Sets the number of spaces the exported class should have.
162 144
     *
163
     * @param integer $numSpaces
164 144
     *
165 144
     * @return self
166 144
     */
167
    public function setNumSpaces(int $numSpaces): self
168
    {
169 144
        $this->spaces = \str_repeat(' ', $numSpaces);
170
        $this->numSpaces = $numSpaces;
171
172 1
        return $this;
173
    }
174 1
175
    public function addTrait(string $trait): self
176 1
    {
177
        $cleanTrait = $this->shortenClassName($trait, false);
178
        if (!\in_array($cleanTrait, $this->traits)) {
179 144
            $this->traits[] = $cleanTrait;
180
        }
181 144
182 144
        return $this;
183 144
    }
184
185
    public function clearTraits(): self
186 144
    {
187
        $this->traits = [];
188
189 1
        return $this;
190
    }
191 1
192
    public function addImplement(string $implement): self
193 1
    {
194
        $cleanImplement = $this->shortenClassName($implement, false);
195
        if (!\in_array($cleanImplement, $this->implements)) {
196 68
            $this->implements[] = $cleanImplement;
197
        }
198 68
199
        return $this;
200 68
    }
201 68
202
    public function clearImplements(): self
203 68
    {
204 38
        $this->implements = [];
205
206
        return $this;
207 67
    }
208 10
209
    public function addUseStatement(string $useStatement): self
210 10
    {
211
        $cleanUse = ClassUtils::cleanClasseName($useStatement);
212
        if (!\in_array($cleanUse, $this->useStatements)) {
213 67
            $this->useStatements[] = $cleanUse;
214
        }
215
216 1
        return $this;
217 1
    }
218 1
219 1
    public function clearUseStatements(): self
220 1
    {
221
        $this->useStatements = [];
222
223
        return $this;
224
    }
225 66
226
    public function getSkeletonContent(string $skeleton, bool $withDefault = true): string
227 66
    {
228 66
        $skeletonDirs = $this->getSkeletonDirs($withDefault);
229 66
230
        foreach ($skeletonDirs as $skeletonDir) {
231 66
            $path = $skeletonDir.'/'.$skeleton.static::SKELETON_FILE_PREFIX;
232
233 68
            if (!\file_exists($path)) {
234
                continue;
235 68
            }
236
237 68
            if (!isset(self::$templates[$path])) {
238
                $fileContent = \file_get_contents($path);
239
240 172
                if ($fileContent === false) {
241
                    throw new GeneratorException(sprintf('Unable to get content for %s', $path));
242 172
                }
243
244 172
                $content = \trim($fileContent);
245 172
246 66
                self::$templates[$path] = $content;
247
            }
248 144
249
            return self::$templates[$path];
250
        }
251 172
252
        throw new \InvalidArgumentException(
253
            \sprintf(
254 49
                'Skeleton "%s" could not be found in %s.',
255
                $skeleton,
256 49
                \implode(', ', $skeletonDirs)
257 49
            )
258
        );
259 49
    }
260 49
261
    protected function addInternalUseStatement(string $use): void
262
    {
263 49
        $cleanUse = ClassUtils::cleanClasseName($use);
264
        if (!\in_array($cleanUse, $this->internalUseStatements)) {
265
            $this->internalUseStatements[] = $cleanUse;
266 67
        }
267
    }
268 67
269
    protected function clearInternalUseStatements(): self
270 67
    {
271 67
        $this->internalUseStatements = [];
272 67
273
        return $this;
274 67
    }
275 66
276
    protected function shortenClassName(string $definition, bool $isInternal = true): string
277 1
    {
278 1
        $shortName = ClassUtils::shortenClassName($definition);
279 1
280 1
        $useStatement = \preg_replace('@\:\:.*$@i', '', $definition);
281 1
282
        if ($useStatement === null) {
283
            throw new GeneratorException(sprintf('Unable to get use statement for %s', $definition));
284
        }
285
286
        if ($isInternal) {
287 65
            $this->addInternalUseStatement($useStatement);
288
        } else {
289
            $this->addUseStatement($useStatement);
290 67
        }
291
292 67
        return $shortName;
293 67
    }
294 67
295
    protected function shortenClassFromCode(string $code): string
296 65
    {
297
        $codeParsed = ClassUtils::shortenClassFromCode(
298
            $code,
299 67
            function ($matches) {
300
                return $this->shortenClassName($matches[1]);
301 67
            }
302
        );
303 67
304
        if ($codeParsed === null) {
305
            throw new GeneratorException(sprintf('Unable to get class for shortened code %s', $code));
306
        }
307
308
        return $codeParsed;
309
    }
310
311
    protected function processPlaceHoldersReplacements(array $placeHolders, string $content, array $values): string
312
    {
313 65
        $replacements = [];
314
315 65
        foreach ($placeHolders as $placeHolder) {
316
            $generator = [$this, 'generate'.\ucfirst($placeHolder)];
317 65
            $name = '<'.$placeHolder.'>';
318 65
319 65
            if (\is_callable($generator)) {
320
                $replacements[$name] = \call_user_func_array($generator, [$values]);
321
            } else {
322
                throw new \RuntimeException(
323 65
                    \sprintf(
324 65
                        'Generator [%s] for placeholder "%s" is not callable.',
325
                        \get_class($generator[0]).'::'.$generator[1],
326
                        $placeHolder
327 65
                    )
328
                );
329
            }
330 66
        }
331
332 66
        return \strtr($content, $replacements);
333
    }
334
335 66
    protected function processTemplatePlaceHoldersReplacements(string $template, array $values, array $skip = []): string
336
    {
337 66
        $code = $this->getSkeletonContent($template);
338
        $placeHolders = $this->getPlaceHolders($code);
339
        $code = $this->processPlaceHoldersReplacements(\array_diff($placeHolders, $skip), $code, $values);
340 62
341
        return $code;
342 62
    }
343 62
344
    protected function getPlaceHolders(string $content): array
345 62
    {
346
        \preg_match_all('@<([\w]+)>@i', $content, $placeHolders);
347 62
348
        return $placeHolders[1] ?? [];
349
    }
350 66
351
    /**
352 66
     * @param string $code      Code to prefix
353
     * @param int    $num       Number of indents
354
     * @param bool   $skipFirst Skip first line
355 66
     *
356
     * @return string
357 66
     */
358
    protected function prefixCodeWithSpaces(string $code, int $num = 1, bool $skipFirst = true): string
359
    {
360 66
        $lines = \explode("\n", $code);
361
362 66
        foreach ($lines as $key => $value) {
363
            if (!empty($value)) {
364 66
                $lines[$key] = \str_repeat($this->spaces, $num).$lines[$key];
365
            }
366
        }
367 66
368
        if ($skipFirst) {
369 66
            $lines[0] = \ltrim($lines[0]);
370 66
        }
371
372
        return \implode("\n", $lines);
373 62
    }
374
375 62
    protected function generateSpaces(): string
376 62
    {
377
        return $this->spaces;
378
    }
379 62
380
    protected function generateNamespace(): ?string
381
    {
382
        return null !== $this->classNamespace ? 'namespace '.$this->classNamespace.';' : null;
383
    }
384
385
    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

385
    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...
386
    {
387
        $statements = \array_merge($this->internalUseStatements, $this->useStatements);
388
        \sort($statements);
389
390
        $useStatements = $this->tokenizeUseStatements($statements);
391
392
        return $useStatements;
393
    }
394
395
    protected function generateClassType(): string
396
    {
397
        return 'final ';
398
    }
399
400
    protected function generateImplements(): ?string
401
    {
402
        return \count($this->implements) ? ' implements '.\implode(', ', $this->implements) : null;
403
    }
404
405
    protected function generateTraits(): ?string
406
    {
407
        $traits = $this->tokenizeUseStatements($this->traits, '<spaces>');
408
409
        return $traits ? $traits."\n" : $traits;
410
    }
411
412
    protected function tokenizeUseStatements(array $useStatements, $prefix = ''): string
413
    {
414
        $code = '';
415
416
        foreach ($useStatements as $useStatement) {
417
            $code .= "\n${prefix}use $useStatement;";
418
        }
419
420
        return $code;
421
    }
422
423
    /**
424
     * Generates classes files.
425
     *
426
     * @param array    $configs raw configs
427
     * @param string   $outputDirectory
428
     * @param int $mode
429
     *
430
     * @return array classes map [[FQCLN => classPath], [FQCLN => classPath], ...]
431
     */
432
    abstract public function generateClasses(array $configs, ?string $outputDirectory, int $mode = self::MODE_WRITE): array;
433
434
    /**
435
     * Generates a class file.
436
     *
437
     * @param array  $config
438
     * @param string $outputDirectory
439
     * @param int    $mode
440
     *
441
     * @return array classes map [FQCLN => classPath]
442
     */
443
    abstract public function generateClass(array $config, ?string $outputDirectory, int $mode = self::MODE_WRITE): array;
444
}
445