Passed
Pull Request — master (#58)
by Dmitriy
13:13 queued 10:22
created

AbstractGenerator::loadStickyAttributes()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
eloc 9
nc 5
nop 0
dl 0
loc 11
ccs 0
cts 10
cp 0
c 0
b 0
f 0
cc 6
crap 42
rs 9.2222
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\CodeFile;
13
use Yiisoft\Yii\Gii\Exception\InvalidConfigException;
14
use Yiisoft\Yii\Gii\Exception\InvalidGeneratorCommandException;
15
use Yiisoft\Yii\Gii\GeneratorInterface;
16
use Yiisoft\Yii\Gii\GiiParametersProvider;
17
18
/**
19
 * This is the base class for all generator classes.
20
 *
21
 * A generator instance is responsible for taking user inputs, validating them,
22
 * and using them to generate the corresponding code based on a set of code template files.
23
 *
24
 * A generator class typically needs to implement the following methods:
25
 *
26
 * - {@see GeneratorInterface::getName()}: returns the name of the generator
27
 * - {@see GeneratorInterface::getDescription()}: returns the detailed description of the generator
28
 * - {@see GeneratorInterface::generate()}: generates the code based on the current user input and the specified code
29
 * template files. This is the place where main code generation code resides.
30
 */
31
abstract class AbstractGenerator implements GeneratorInterface
32
{
33
    private string $directory = 'src/Controller';
34
35 3
    public function __construct(
36
        protected Aliases $aliases,
37
        protected ValidatorInterface $validator,
38
        protected GiiParametersProvider $parametersProvider,
39
    ) {
40
    }
41
42
    /**
43
     * Returns a list of code template files that are required.
44
     * Derived classes usually should override this method if they require the existence of
45
     * certain template files.
46
     *
47
     * @return array list of code template files that are required. They should be file paths
48
     * relative to {@see getTemplatePath()}.
49
     */
50
    public function requiredTemplates(): array
51
    {
52
        return [];
53
    }
54
55
    /**
56
     * Returns the root path to the default code template files.
57
     * The default implementation will return the "templates" subdirectory of the
58
     * directory containing the generator class file.
59
     *
60
     * @throws ReflectionException
61
     *
62
     * @return string the root path to the default code template files.
63
     */
64 1
    private function defaultTemplate(): string
65
    {
66 1
        $class = new ReflectionClass($this);
67
68 1
        return dirname($class->getFileName()) . '/default';
69
    }
70
71
    /**
72
     * @throws ReflectionException
73
     * @throws InvalidConfigException
74
     *
75
     * @return string the root path of the template files that are currently being used.
76
     */
77 1
    public function getTemplatePath(AbstractGeneratorCommand $command): string
78
    {
79 1
        $template = $command->getTemplate();
80
81 1
        if ($template === 'default') {
82 1
            return $this->defaultTemplate();
83
        }
84
85
        if (isset($this->parametersProvider->getTemplates()[$template])) {
86
            return $this->parametersProvider->getTemplates()[$template];
87
        }
88
89
        throw new InvalidConfigException("Unknown template: {$template}");
90
    }
91
92
    /**
93
     * @param AbstractGeneratorCommand $command
94
     *
95
     * @throws InvalidGeneratorCommandException
96
     *
97
     * @return array|CodeFile
98
     */
99 2
    final public function generate(AbstractGeneratorCommand $command): array
100
    {
101 2
        $result = $this->validator->validate($command);
102
103 2
        if (!$result->isValid()) {
104 1
            throw new InvalidGeneratorCommandException($result);
105
        }
106
107 1
        return $this->doGenerate($command);
108
    }
109
110
    abstract protected function doGenerate(AbstractGeneratorCommand $command): array;
111
112
    /**
113
     * Generates code using the specified code template and parameters.
114
     * Note that the code template will be used as a PHP file.
115
     *
116
     * @param string $template the code template file. This must be specified as a file path
117
     * relative to {@see getTemplatePath()}.
118
     * @param array $params list of parameters to be passed to the template file.
119
     *
120
     * @throws Throwable
121
     *
122
     * @return string the generated code
123
     */
124 1
    protected function render(AbstractGeneratorCommand $command, string $template, array $params = []): string
125
    {
126 1
        $file = sprintf(
127
            '%s/%s.php',
128 1
            $this->aliases->get($this->getTemplatePath($command)),
129
            $template
130
        );
131
132 1
        $renderer = function (): void {
133 1
            extract(func_get_arg(1));
134
            /** @psalm-suppress UnresolvableInclude */
135 1
            require func_get_arg(0);
136
        };
137
138 1
        $obInitialLevel = ob_get_level();
139 1
        ob_start();
140 1
        ob_implicit_flush(false);
141
        try {
142
            /** @psalm-suppress PossiblyInvalidFunctionCall */
143 1
            $renderer->bindTo($this)($file, array_merge($params, ['command' => $command]));
144 1
            return ob_get_clean();
145
        } catch (Throwable $e) {
146
            while (ob_get_level() > $obInitialLevel) {
147
                if (!@ob_end_clean()) {
148
                    ob_clean();
149
                }
150
            }
151
            throw $e;
152
        }
153
    }
154
155 1
    public function getDirectory(): string
156
    {
157 1
        return $this->directory;
158
    }
159
}
160