AbstractGenerator::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 0
c 0
b 0
f 0
dl 0
loc 5
ccs 1
cts 1
cp 1
rs 10
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Gii\Generator;
6
7
use ReflectionClass;
8
use ReflectionException;
9
use Throwable;
10
use Yiisoft\Aliases\Aliases;
11
use Yiisoft\Validator\ValidatorInterface;
12
use Yiisoft\Yii\Gii\Component\CodeFile\CodeFile;
13
use Yiisoft\Yii\Gii\Exception\InvalidConfigException;
14
use Yiisoft\Yii\Gii\Exception\InvalidGeneratorCommandException;
15
use Yiisoft\Yii\Gii\GeneratorCommandInterface;
16
use Yiisoft\Yii\Gii\GeneratorInterface;
17
use Yiisoft\Yii\Gii\ParametersProvider;
18
19
/**
20
 * This is the base class for all generator classes.
21
 *
22
 * A generator instance is responsible for taking user inputs, validating them,
23
 * and using them to generate the corresponding code based on a set of code template files.
24
 *
25
 * A generator class typically needs to implement the following methods:
26
 *
27
 * - {@see GeneratorInterface::getName()}: returns the name of the generator
28
 * - {@see GeneratorInterface::getDescription()}: returns the detailed description of the generator
29
 * - {@see GeneratorInterface::generate()}: generates the code based on the current user input and the specified code
30
 * template files. This is the place where main code generation code resides.
31
 */
32
abstract class AbstractGenerator implements GeneratorInterface
33
{
34 8
    public function __construct(
35
        protected Aliases $aliases,
36
        protected ValidatorInterface $validator,
37
        protected ParametersProvider $parametersProvider,
38
    ) {
39 8
    }
40
41
    public function getRequiredTemplates(): array
42
    {
43
        return [];
44
    }
45
46
    /**
47
     * Returns the root path to the default code template files.
48
     * The default implementation will return the "templates" subdirectory of the
49
     * directory containing the generator class file.
50
     *
51
     * @throws ReflectionException
52
     *
53
     * @return string the root path to the default code template files.
54
     */
55 2
    private function defaultTemplate(): string
56
    {
57 2
        $class = new ReflectionClass($this);
58
59 2
        return dirname($class->getFileName()) . '/default';
60
    }
61
62 4
    public function getTemplatePath(GeneratorCommandInterface $command): string
63
    {
64 4
        $template = $command->getTemplate();
65
66 4
        if ($template === 'default') {
67 2
            return $this->defaultTemplate();
68
        }
69
70 2
        $templates = $this->parametersProvider->getTemplates(static::getId());
71
72 2
        return $templates[$template] ?? throw new InvalidConfigException("Unknown template: \"{$template}\"");
0 ignored issues
show
Bug Best Practice introduced by
The expression return $templates[$template] ?? ThrowNode also could return the type array|true which is incompatible with the return type mandated by Yiisoft\Yii\Gii\Generato...face::getTemplatePath() of string.
Loading history...
73
    }
74
75
    /**
76
     * @param GeneratorCommandInterface $command
77
     *
78
     * @throws InvalidGeneratorCommandException
79
     *
80
     * @return CodeFile[]
81
     */
82 7
    final public function generate(GeneratorCommandInterface $command): array
83
    {
84 7
        $result = $this->validator->validate($command);
85
86 7
        if (!$result->isValid()) {
87 3
            throw new InvalidGeneratorCommandException($result);
88
        }
89
90 4
        return $this->doGenerate($command);
91
    }
92
93
    abstract protected function doGenerate(GeneratorCommandInterface $command): array;
94
95
    /**
96
     * Generates code using the specified code template and parameters.
97
     * Note that the code template will be used as a PHP file.
98
     *
99
     * @param string $templateFile the code template file. This must be specified as a file path
100
     * relative to {@see getTemplatePath()}.
101
     * @param array $params list of parameters to be passed to the template file.
102
     *
103
     * @throws Throwable
104
     *
105
     * @return string the generated code
106
     */
107 4
    protected function render(GeneratorCommandInterface $command, string $templateFile, array $params = []): string
108
    {
109 4
        $file = sprintf(
110 4
            '%s/%s',
111 4
            $this->aliases->get($this->getTemplatePath($command)),
112 4
            $templateFile
113 4
        );
114
115 4
        $renderer = function (): void {
116 4
            extract(func_get_arg(1));
117
            /** @psalm-suppress UnresolvableInclude */
118 4
            require func_get_arg(0);
119 4
        };
120
121 4
        $obInitialLevel = ob_get_level();
122 4
        ob_start();
123 4
        ob_implicit_flush(false);
124
        try {
125
            /** @psalm-suppress PossiblyNullFunctionCall */
126 4
            $renderer->bindTo($this)($file, array_merge($params, ['command' => $command]));
127 4
            return ob_get_clean();
128
        } catch (Throwable $e) {
129
            while (ob_get_level() > $obInitialLevel) {
130
                if (!@ob_end_clean()) {
131
                    ob_clean();
132
                }
133
            }
134
            throw $e;
135
        }
136
    }
137
}
138