Completed
Pull Request — master (#43)
by Jitendra
02:09
created

TwigGenerator::generateDocs()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 6
nop 2
dl 0
loc 21
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the PHINT package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace Ahc\Phint\Generator;
13
14
use Ahc\Phint\Util\Inflector;
15
use Ahc\Phint\Util\Path;
16
17
class TwigGenerator implements GeneratorInterface
18
{
19
    /** @var \Twig_Environment */
20
    protected $twig;
21
22
    /** @var Path */
23
    protected $pathUtil;
24
25
    /** @var Inflector */
26
    protected $inflector;
27
28
    /** @var string|array */
29
    protected $templatePath;
30
31
    /** @var string */
32
    protected $cachePath;
33
34
    /** @var array Templates required for type 'project' only */
35
    protected $projectTemplates = [
36
        '.env.example' => true,
37
        'package.json' => true,
38
    ];
39
40
    /** @var array Templates only loaded by some specific commands */
41
    protected $commandTemplates = [
42
        'test' => true,
43
        'docs' => true,
44
    ];
45
46
    public function __construct(string $templatePath, string $cachePath)
47
    {
48
        $this->templatePath = $templatePath;
49
        $this->cachePath    = $cachePath;
50
        $this->pathUtil     = new Path;
51
        $this->inflector    = new Inflector;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function generate(string $targetPath, array $parameters, CollisionHandlerInterface $handler = null): int
58
    {
59
        $generated = 0;
60
61
        if (!$this->twig) {
62
            $this->initTwig();
63
        }
64
65
        $templates = $this->pathUtil->findFiles([$this->templatePath], '.twig', false);
66
        foreach ($templates as $template) {
67
            if ($this->shouldGenerate($template, $parameters)) {
68
                $generated += (int) $this->doGenerate($template, $targetPath, $parameters, $handler);
69
            }
70
        }
71
72
        $this->pathUtil->createBinaries($parameters['bin'] ?? [], $parameters['path'] ?? '');
73
74
        return $generated;
75
    }
76
77
    public function generateTests(array $testMetadata, array $parameters): int
78
    {
79
        if (!$this->twig) {
80
            $this->initTwig();
81
        }
82
83
        $generated = 0;
84
85
        foreach ($testMetadata as $metadata) {
86
            // Skip existing
87
            if (\is_file($targetFile = $metadata['testPath'])) {
88
                continue;
89
            }
90
91
            $content = $this->twig->render('tests/test.twig', $metadata + $parameters);
92
93
            $generated += (int) $this->pathUtil->writeFile($targetFile, $content);
94
        }
95
96
        return $generated;
97
    }
98
99
    public function generateDocs(array $docsMetadata, array $parameters): int
100
    {
101
        if (!$this->twig) {
102
            $this->initTwig();
103
        }
104
105
        $targetFile = $parameters['output'] ?? 'README.md';
106
        $docContent = $this->twig->render('docs/docs.twig', \compact('docsMetadata') + $parameters);
107
        $docContent = "<!-- DOCS START -->\n$docContent\n<!-- DOCS END -->";
108
109
        if (null === $oldContent = $this->pathUtil->read($targetFile)) {
110
            return (int) $this->pathUtil->writeFile($targetFile, $docContent);
111
        }
112
113
        if (\strpos($oldContent, '<!-- DOCS START -->')) {
114
            $docContent = \preg_replace('~<!-- DOCS START -->.*?<!-- DOCS END -->~s', $docContent, $oldContent);
115
116
            return (int) $this->pathUtil->writeFile($targetFile, $docContent);
117
        }
118
119
        return (int) $this->pathUtil->appendFile($targetFile, $content);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $content seems to be never defined.
Loading history...
Bug introduced by
The method appendFile() does not exist on Ahc\Phint\Util\Path. ( Ignorable by Annotation )

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

119
        return (int) $this->pathUtil->/** @scrutinizer ignore-call */ appendFile($targetFile, $content);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
120
    }
121
122
    protected function initTwig()
123
    {
124
        $options = [
125
            'auto_reload' => true,
126
            'cache'       => false,
127
        ];
128
129
        if ($this->cachePath) {
130
            $this->pathUtil->ensureDir($this->cachePath);
131
            $options['cache'] = $this->cachePath;
132
        }
133
134
        $this->twig = new \Twig_Environment(
135
            new \Twig_Loader_Filesystem($this->templatePath),
136
            $options
137
        );
138
139
        $this->twig->addFilter(new \Twig_SimpleFilter('snake', function ($x) {
140
            return $this->inflector->snakeCase($x);
141
        }));
142
143
        $this->twig->addFilter(new \Twig_SimpleFilter('lcfirst', function ($x) {
144
            return \lcfirst($x);
145
        }));
146
147
        $this->twig->addFilter(new \Twig_SimpleFilter('ucfirst', function ($x) {
148
            return \ucfirst($x);
149
        }));
150
151
        $this->twig->addFunction(new \Twig_Function('gmdate', function ($f = null) {
152
            return \gmdate($f ?? 'Y-m-d H:i:s');
153
        }));
154
155
        $this->twig->addFilter(new \Twig_SimpleFilter('call', function ($fn) {
156
            return $fn(\array_slice(\func_get_args(), 1));
157
        }));
158
    }
159
160
    protected function doGenerate(string $template, string $targetPath, array $parameters, CollisionHandlerInterface $handler = null): bool
161
    {
162
        $relativePath = $this->pathUtil->getRelativePath($template, $this->templatePath);
163
        $targetFile   = $this->pathUtil->join($targetPath, $this->getRelativeTarget($parameters, $relativePath));
164
        $fileExists   = \is_file($targetFile);
165
        $targetDir    = \dirname($targetFile);
0 ignored issues
show
Unused Code introduced by
The assignment to $targetDir is dead and can be removed.
Loading history...
166
        $content      = $this->twig->render($relativePath, $parameters);
167
168
        if ($handler && $fileExists) {
169
            return $handler->handle($targetFile, $content, $parameters);
170
        }
171
172
        if ($this->mayOverride($fileExists, $parameters)) {
173
            return $this->pathUtil->writeFile($targetFile, $content);
174
        }
175
176
        return false;
177
    }
178
179
    protected function getRelativeTarget(array $parameters, string $relativePath): string
180
    {
181
        $fileName   = \basename($relativePath, '.twig');
182
        $targetFile = \str_replace('.twig', '', $relativePath);
183
184
        if (!empty($parameters['ghTemplate']) && \in_array($fileName, ['ISSUE_TEMPLATE.md', 'PULL_REQUEST_TEMPLATE.md'])) {
185
            $targetFile = '.github/' . $fileName;
186
        }
187
188
        return $targetFile;
189
    }
190
191
    protected function shouldGenerate(string $template, array $parameters)
192
    {
193
        $name = \basename($template, '.twig');
194
195
        if (isset($this->projectTemplates[$name])) {
196
            return $parameters['type'] === 'project';
197
        }
198
199
        if (isset($this->commandTemplates[$name])) {
200
            return false;
201
        }
202
203
        return true;
204
    }
205
206
    protected function mayOverride(bool $fileExists, array $parameters)
207
    {
208
        if (!$fileExists) {
209
            return true;
210
        }
211
212
        // If using reference package then we dont overwrite!
213
        if (!empty($parameters['using'])) {
214
            return false;
215
        }
216
217
        if (!empty($parameters['sync'])) {
218
            return false;
219
        }
220
221
        return true;
222
    }
223
}
224