• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / schema-generator / 3828622569

pending completion
3828622569

push

github

GitHub
chore: bump deps (#402)

1738 of 2198 relevant lines covered (79.07%)

16.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.06
/src/FilesGenerator.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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
declare(strict_types=1);
13

14
namespace ApiPlatform\SchemaGenerator;
15

16
use ApiPlatform\SchemaGenerator\Model\Class_;
17
use Nette\InvalidArgumentException as NetteInvalidArgumentException;
18
use Nette\PhpGenerator\PhpFile;
19
use PhpCsFixer\Cache\NullCacheManager;
20
use PhpCsFixer\Differ\NullDiffer;
21
use PhpCsFixer\Error\ErrorsManager;
22
use PhpCsFixer\FixerFactory;
23
use PhpCsFixer\Linter\Linter;
24
use PhpCsFixer\RuleSet as LegacyRuleSet;
25
use PhpCsFixer\RuleSet\RuleSet;
26
use PhpCsFixer\Runner\Runner;
27
use Psr\Log\LoggerAwareTrait;
28
use Symfony\Component\Console\Question\ChoiceQuestion;
29
use Symfony\Component\Console\Style\SymfonyStyle;
30
use Symfony\Component\Filesystem\Filesystem;
31
use Symfony\Component\String\Inflector\InflectorInterface;
32
use Twig\Environment;
33

34
final class FilesGenerator
35
{
36
    use LoggerAwareTrait;
37

38
    private InflectorInterface $inflector;
39
    private Printer $printer;
40
    private Environment $twig;
41
    private Filesystem $filesystem;
42
    private SymfonyStyle $io;
43

44
    public function __construct(InflectorInterface $inflector, Printer $printer, Environment $twig, SymfonyStyle $io)
45
    {
46
        $this->inflector = $inflector;
26✔
47
        $this->printer = $printer;
26✔
48
        $this->twig = $twig;
26✔
49
        $this->filesystem = new Filesystem();
26✔
50
        $this->io = $io;
26✔
51
    }
52

53
    /**
54
     * Generates files.
55
     *
56
     * @param Class_[]      $classes
57
     * @param Configuration $config
58
     */
59
    public function generate(array $classes, array $config): void
60
    {
61
        $interfaceMappings = [];
26✔
62
        $generatedFiles = [];
26✔
63

64
        foreach ($classes as $className => $class) {
26✔
65
            $classDir = $this->namespaceToDir($class->namespace, $config);
26✔
66
            $this->filesystem->mkdir($classDir);
26✔
67

68
            $path = sprintf('%s%s.php', $classDir, $className);
26✔
69

70
            $file = null;
26✔
71
            if (file_exists($path) && is_file($path) && is_readable($path) && $fileContent = file_get_contents($path)) {
26✔
72
                $choice = $this->io->askQuestion(new ChoiceQuestion(sprintf('File "%s" already exists, keep your changes and update it (use) or overwrite it (overwrite)?', $path), ['use', 'overwrite'], 0));
×
73
                if ('use' === $choice) {
×
74
                    $file = PhpFile::fromCode($fileContent);
×
75
                    $this->logger ? $this->logger->info(sprintf('Using "%s" as base file.', $path)) : null;
×
76
                }
77
            }
78

79
            try {
80
                file_put_contents($path, $this->printer->printFile($class->toNetteFile($config, $this->inflector, $file)));
26✔
81
            } catch (NetteInvalidArgumentException $exception) {
2✔
82
                $this->logger ? $this->logger->warning($exception->getMessage()) : null;
2✔
83
            }
84

85
            $generatedFiles[] = $path;
26✔
86

87
            if (null !== $class->interfaceNamespace()) {
26✔
88
                $interfaceDir = $this->namespaceToDir($class->interfaceNamespace(), $config);
2✔
89
                $this->filesystem->mkdir($interfaceDir);
2✔
90

91
                $path = sprintf('%s%s.php', $interfaceDir, $class->interfaceName());
2✔
92
                $generatedFiles[] = $path;
2✔
93
                file_put_contents($path, $this->printer->printFile($class->interfaceToNetteFile($config['header'] ?? null)));
2✔
94

95
                if ($config['doctrine']['resolveTargetEntityConfigPath'] && !$class->isAbstract) {
2✔
96
                    $interfaceMappings[$class->interfaceNamespace().'\\'.$class->interfaceName()] = $class->namespace.'\\'.$className;
×
97
                }
98
            }
99
        }
100

101
        if ($config['doctrine']['resolveTargetEntityConfigPath'] && \count($interfaceMappings) > 0) {
26✔
102
            $file = $config['output'].'/'.$config['doctrine']['resolveTargetEntityConfigPath'];
×
103
            $dir = \dirname($file);
×
104
            $this->filesystem->mkdir($dir);
×
105

106
            $fileType = $config['doctrine']['resolveTargetEntityConfigType'];
×
107

108
            $mappingTemplateFile = 'doctrine.xml.twig';
×
109
            if ('yaml' === $fileType) {
×
110
                $mappingTemplateFile = 'doctrine.yaml.twig';
×
111
            }
112

113
            file_put_contents(
×
114
                $file,
×
115
                $this->twig->render($mappingTemplateFile, ['mappings' => $interfaceMappings])
×
116
            );
×
117

118
            $generatedFiles[] = $file;
×
119
        }
120

121
        $this->fixCs($generatedFiles);
26✔
122
    }
123

124
    /**
125
     * Converts a namespace to a directory path according to PSR-4.
126
     *
127
     * @param Configuration $config
128
     */
129
    private function namespaceToDir(string $namespace, array $config): string
130
    {
131
        if (null !== ($prefix = $config['namespaces']['prefix'] ?? null) && 0 === strpos($namespace, $prefix)) {
26✔
132
            $namespace = substr($namespace, \strlen($prefix));
2✔
133
        }
134

135
        return sprintf('%s/%s/', $config['output'], str_replace('\\', '/', $namespace));
26✔
136
    }
137

138
    /**
139
     * Uses PHP CS Fixer to make generated files following PSR and Symfony Coding Standards.
140
     *
141
     * @param string[] $files
142
     */
143
    private function fixCs(array $files): void
144
    {
145
        $fileInfos = [];
26✔
146
        foreach ($files as $file) {
26✔
147
            $fileInfos[] = new \SplFileInfo($file);
26✔
148
        }
149

150
        // to keep compatibility with both versions of php-cs-fixer: 2.x and 3.x
151
        // ruleset object must be created depending on which class is available
152
        $rulesetClass = class_exists(LegacyRuleSet::class) ? LegacyRuleSet::class : Ruleset::class;
26✔
153
        $fixers = (new FixerFactory())
26✔
154
            ->registerBuiltInFixers()
26✔
155
            ->useRuleSet(new $rulesetClass([ // @phpstan-ignore-line
26✔
156
                '@PhpCsFixer' => true,
26✔
157
                '@Symfony' => true,
26✔
158
                'array_syntax' => ['syntax' => 'short'],
26✔
159
                'phpdoc_order' => true,
26✔
160
                'declare_strict_types' => true,
26✔
161
            ]))
26✔
162
            ->getFixers();
26✔
163

164
        $runner = new Runner(
26✔
165
            new \ArrayIterator($fileInfos),
26✔
166
            $fixers,
26✔
167
            new NullDiffer(),
26✔
168
            null,
26✔
169
            new ErrorsManager(),
26✔
170
            new Linter(),
26✔
171
            false,
26✔
172
            new NullCacheManager()
26✔
173
        );
26✔
174
        $runner->fix();
26✔
175
    }
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc