Skip to content

Commit c1c77cd

Browse files
authored
Merge pull request #121 from dunglas/jlagneau-master
Support for PSR-4 prefixes
2 parents 6c9f8a3 + 7cc8072 commit c1c77cd

File tree

9 files changed

+107
-14
lines changed

9 files changed

+107
-14
lines changed

src/Command/GenerateTypesCommand.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,32 @@ final class GenerateTypesCommand extends Command
3535
{
3636
private const DEFAULT_CONFIG_FILE = 'schema.yaml';
3737

38+
private $namespacePrefix;
39+
private $defaultOutput;
40+
3841
/**
3942
* {@inheritdoc}
4043
*/
4144
protected function configure(): void
4245
{
46+
if (file_exists('composer.json') && is_file('composer.json') && is_readable('composer.json')) {
47+
$composer = json_decode(file_get_contents('composer.json'), true);
48+
foreach ($composer['autoload']['psr-4'] ?? [] as $prefix => $output) {
49+
if ('' === $prefix) {
50+
continue;
51+
}
52+
53+
$this->namespacePrefix = $prefix;
54+
$this->defaultOutput = $output;
55+
56+
break;
57+
}
58+
}
59+
4360
$this
4461
->setName('generate-types')
4562
->setDescription('Generate types')
46-
->addArgument('output', InputArgument::REQUIRED, 'The output directory')
63+
->addArgument('output', $this->defaultOutput ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The output directory', $this->defaultOutput)
4764
->addArgument('config', InputArgument::OPTIONAL, 'The config file to use (default to "schema.yaml" in the current directory, will generate all types if no config file exists)');
4865
}
4966

@@ -52,10 +69,18 @@ protected function configure(): void
5269
*/
5370
protected function execute(InputInterface $input, OutputInterface $output): void
5471
{
72+
$defaultOutput = $this->defaultOutput ? realpath($this->defaultOutput) : null;
5573
$outputDir = $input->getArgument('output');
74+
$configArgument = $input->getArgument('config');
75+
5676
if ($dir = realpath($input->getArgument('output'))) {
5777
if (!is_dir($dir)) {
58-
throw new \InvalidArgumentException(sprintf('The file "%s" is not a directory.', $dir));
78+
if (!$this->defaultOutput) {
79+
throw new \InvalidArgumentException(sprintf('The file "%s" is not a directory.', $dir));
80+
}
81+
82+
$dir = $defaultOutput;
83+
$configArgument = $outputDir;
5984
}
6085

6186
if (!is_writable($dir)) {
@@ -69,7 +94,6 @@ protected function execute(InputInterface $input, OutputInterface $output): void
6994
$outputDir = realpath($outputDir);
7095
}
7196

72-
$configArgument = $input->getArgument('config');
7397
if ($configArgument) {
7498
if (!file_exists($configArgument)) {
7599
throw new \InvalidArgumentException(sprintf('The file "%s" doesn\'t exist.', $configArgument));
@@ -95,7 +119,7 @@ protected function execute(InputInterface $input, OutputInterface $output): void
95119
}
96120

97121
$processor = new Processor();
98-
$configuration = new TypesGeneratorConfiguration();
122+
$configuration = new TypesGeneratorConfiguration($dir === $defaultOutput ? $this->namespacePrefix : null);
99123
$processedConfiguration = $processor->processConfiguration($configuration, [$config]);
100124
$processedConfiguration['output'] = $outputDir;
101125
if (!$processedConfiguration['output']) {

src/TypesGenerator.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,10 @@ private function generateClassUses(array $annotationGenerators, array $classes,
827827
*/
828828
private function namespaceToDir(array $config, string $namespace): string
829829
{
830+
if (null !== ($prefix = $config['namespaces']['prefix'] ?? null) && 0 === strpos($namespace, $prefix)) {
831+
$namespace = substr($namespace, strlen($prefix));
832+
}
833+
830834
return sprintf('%s/%s/', $config['output'], strtr($namespace, '\\', '/'));
831835
}
832836

src/TypesGeneratorConfiguration.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,20 @@ final class TypesGeneratorConfiguration implements ConfigurationInterface
3939
public const GOOD_RELATIONS_OWL_URL = __DIR__.'/../data/v1.owl';
4040
public const SCHEMA_ORG_NAMESPACE = 'http://schema.org/';
4141

42+
private $defaultPrefix;
43+
44+
public function __construct(?string $defaultPrefix = null)
45+
{
46+
$this->defaultPrefix = $defaultPrefix;
47+
}
48+
4249
/**
4350
* {@inheritdoc}
4451
*/
4552
public function getConfigTreeBuilder(): TreeBuilder
4653
{
54+
$namespacePrefix = $this->defaultPrefix ?? 'AppBundle\\';
55+
4756
$treeBuilder = new TreeBuilder();
4857
$rootNode = $treeBuilder->root('config');
4958
$rootNode
@@ -92,9 +101,10 @@ function ($rdfa) {
92101
->addDefaultsIfNotSet()
93102
->info('PHP namespaces')
94103
->children()
95-
->scalarNode('entity')->defaultValue('AppBundle\Entity')->info('The namespace of the generated entities')->example('Acme\Entity')->end()
96-
->scalarNode('enum')->defaultValue('AppBundle\Enum')->info('The namespace of the generated enumerations')->example('Acme\Enum')->end()
97-
->scalarNode('interface')->defaultValue('AppBundle\Model')->info('The namespace of the generated interfaces')->example('Acme\Model')->end()
104+
->scalarNode('prefix')->defaultValue($this->defaultPrefix)->info('The global namespace\'s prefix')->example('App\\')->end()
105+
->scalarNode('entity')->defaultValue("{$namespacePrefix}Entity")->info('The namespace of the generated entities')->example('App\Entity')->end()
106+
->scalarNode('enum')->defaultValue("{$namespacePrefix}Enum")->info('The namespace of the generated enumerations')->example('App\Enum')->end()
107+
->scalarNode('interface')->defaultValue("{$namespacePrefix}Model")->info('The namespace of the generated interfaces')->example('App\Model')->end()
98108
->end()
99109
->end()
100110
->arrayNode('doctrine')

tests/Command/DumpConfigurationTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,17 @@ public function testDumpConfiguration()
7676
# PHP namespaces
7777
namespaces:
7878
79+
# The global namespace's prefix
80+
prefix: null # Example: App\
81+
7982
# The namespace of the generated entities
80-
entity: AppBundle\Entity # Example: Acme\Entity
83+
entity: AppBundle\Entity # Example: App\Entity
8184
8285
# The namespace of the generated enumerations
83-
enum: AppBundle\Enum # Example: Acme\Enum
86+
enum: AppBundle\Enum # Example: App\Enum
8487
8588
# The namespace of the generated interfaces
86-
interface: AppBundle\Model # Example: Acme\Model
89+
interface: AppBundle\Model # Example: App\Model
8790
8891
# Doctrine
8992
doctrine:

tests/Command/GenerateTypesCommandTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,41 @@ public function testDoNotGenerateId()
308308
$this->assertNotContains('function getId', $person);
309309
$this->assertNotContains('function setId', $person);
310310
}
311+
312+
public function testNamespacesPrefix()
313+
{
314+
$outputDir = __DIR__.'/../../build/namespaces-prefix';
315+
$config = __DIR__.'/../config/namespaces-prefix.yaml';
316+
317+
$this->fs->mkdir($outputDir);
318+
319+
$commandTester = new CommandTester(new GenerateTypesCommand());
320+
$this->assertEquals(0, $commandTester->execute(['output' => $outputDir, 'config' => $config]));
321+
322+
$person = file_get_contents("$outputDir/Entity/Person.php");
323+
324+
$this->assertContains('namespace App\Entity;', $person);
325+
}
326+
327+
public function testNamespacesPrefixAutodetect()
328+
{
329+
$outputDir = __DIR__.'/../../build/namespaces-prefix-autodetect/';
330+
331+
$this->fs->mkdir($outputDir);
332+
$this->fs->copy(__DIR__.'/../config/namespaces-prefix-autodetect/composer.json', "$outputDir/composer.json");
333+
$this->fs->copy(__DIR__.'/../config/namespaces-prefix-autodetect/schema.yaml', "$outputDir/schema.yaml");
334+
335+
$currentDir = getcwd();
336+
chdir($outputDir);
337+
try {
338+
$commandTester = new CommandTester(new GenerateTypesCommand());
339+
$this->assertEquals(0, $commandTester->execute([]));
340+
341+
$person = file_get_contents("$outputDir/src/Entity/Person.php");
342+
343+
$this->assertContains('namespace App\Entity;', $person);
344+
} finally {
345+
chdir($currentDir);
346+
}
347+
}
311348
}

tests/TypesGeneratorTest.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,17 +392,16 @@ private function getClasses(): array
392392
private function getContextMatcher(array $class)
393393
{
394394
$config = $this->getConfig();
395-
$classes = $this->getClasses();
396395

397-
return function ($context) use ($config, $classes, $class) {
396+
return function ($context) use ($config, $class) {
398397
if (!isset($context['config']) || $config !== $context['config']) {
399398
return false;
400399
}
401400

402401
$baseClass = $class;
403402
unset($baseClass['fields']);
404403

405-
if (!isset($context['class']) || !is_array($context['class']) || $this->arrayEqual($baseClass, array_intersect_key($context['class'], $baseClass))) {
404+
if (!isset($context['class']) || !\is_array($context['class']) || $this->arrayEqual($baseClass, array_intersect_key($context['class'], $baseClass))) {
406405
return false;
407406
}
408407

@@ -416,6 +415,6 @@ private function getContextMatcher(array $class)
416415

417416
private function arrayEqual(array $a, array $b): bool
418417
{
419-
return count($a) === count($b) && !array_diff($a, $b);
418+
return \count($a) === \count($b) && !array_diff($a, $b);
420419
}
421420
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"autoload": {
3+
"psr-4": { "App\\": "src/" }
4+
}
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
types:
2+
Person:
3+
properties:
4+
name: ~
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespaces:
2+
prefix: App\
3+
entity: App\Entity
4+
types:
5+
Person:
6+
properties:
7+
name: ~

0 commit comments

Comments
 (0)