Completed
Pull Request — master (#43)
by Jitendra
02:05
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
use Symfony\Component\Finder\Finder;
17
18
class TwigGenerator implements GeneratorInterface
19
{
20
    /** @var \Twig_Environment */
21
    protected $twig;
22
23
    /** @var Path */
24
    protected $pathUtil;
25
26
    /** @var Inflector */
27
    protected $inflector;
28
29
    /** @var string|array */
30
    protected $templatePath;
31
32
    /** @var string */
33
    protected $cachePath;
34
35
    /** @var array Templates required for type 'project' only */
36
    protected $projectTemplates = [
37
        '.env.example' => true,
38
        'package.json' => true,
39
    ];
40
41
    /** @var array Templates only loaded by some specific commands */
42
    protected $commandTemplates = [
43
        'test' => true,
44
        'docs' => true,
45
    ];
46
47
    public function __construct(string $templatePath, string $cachePath)
48
    {
49
        $this->templatePath = $templatePath;
50
        $this->cachePath    = $cachePath;
51
        $this->pathUtil     = new Path;
52
        $this->inflector    = new Inflector;
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function generate(string $targetPath, array $parameters, CollisionHandlerInterface $handler = null): int
59
    {
60
        $generated = 0;
61
62
        if (!$this->twig) {
63
            $this->initTwig();
64
        }
65
66
        $templates = $this->pathUtil->findFiles([$this->templatePath], '.twig', false);
67
        foreach ($templates as $template) {
68
            if ($this->shouldGenerate($template, $parameters)) {
69
                $generated += (int) $this->doGenerate($template, $targetPath, $parameters, $handler);
70
            }
71
        }
72
73
        $this->pathUtil->createBinaries($parameters['bin'] ?? [], $parameters['path'] ?? '');
74
75
        return $generated;
76
    }
77
78
    public function generateTests(array $testMetadata, array $parameters): int
79
    {
80
        if (!$this->twig) {
81
            $this->initTwig();
82
        }
83
84
        $generated = 0;
85
86
        foreach ($testMetadata as $metadata) {
87
            // Skip existing
88
            if (\is_file($targetFile = $metadata['testPath'])) {
89
                continue;
90
            }
91
92
            $content = $this->twig->render('tests/test.twig', $metadata + $parameters);
93
94
            $generated += (int) $this->pathUtil->writeFile($targetFile, $content);
95
        }
96
97
        return $generated;
98
    }
99
100
    public function generateDocs(array $docsMetadata, array $parameters): int
101
    {
102
        if (!$this->twig) {
103
            $this->initTwig();
104
        }
105
106
        $targetFile = $parameters['output'] ?? 'README.md';
107
        $docContent = $this->twig->render('docs/docs.twig', \compact('docsMetadata') + $parameters);
108
        $docContent = "<!-- DOCS START -->\n$docContent\n<!-- DOCS END -->";
109
110
        if (null === $oldContent = $this->pathUtil->read($targetFile)) {
111
            return (int) $this->pathUtil->writeFile($targetFile, $docContent);
112
        }
113
114
        if (\strpos($oldContent, '<!-- DOCS START -->')) {
115
            $docContent = \preg_replace('~<!-- DOCS START -->.*?<!-- DOCS END -->~s', $docContent, $oldContent);
116
117
            return (int) $this->pathUtil->writeFile($targetFile, $docContent);
118
        }
119
120
        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

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