Skip to content

Commit

Permalink
Introduce FileGenerator for generating code of classes and interfaces -
Browse files Browse the repository at this point in the history
Close #15
  • Loading branch information
sandrokeil committed Dec 11, 2020
1 parent 0bf1ce8 commit da8ddf2
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 86 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"require": {
"php": "^7.4 || ^8.0",
"open-code-modeling/json-schema-to-php": "^0.2.0 || 0.3.x-dev",
"open-code-modeling/php-code-ast": "^0.9.0 || 0.10.x-dev"
"open-code-modeling/php-code-ast": "^0.10.0 || 0.11.x-dev"
},
"require-dev": {
"jangregor/phpstan-prophecy": "^0.8.0",
Expand All @@ -43,7 +43,7 @@
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^0.12.33",
"phpstan/phpstan-strict-rules": "^0.12.4",
"phpunit/phpunit": "^9.2.6",
"phpunit/phpunit": "^9.5.0",
"prooph/php-cs-fixer-config": "^0.3",
"roave/security-advisories": "dev-master",
"squizlabs/php_codesniffer": "^3.4"
Expand Down
84 changes: 21 additions & 63 deletions src/ClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,18 @@
namespace OpenCodeModeling\JsonSchemaToPhpAst;

use OpenCodeModeling\CodeAst\Builder\ClassBuilder;
use OpenCodeModeling\CodeAst\Builder\ClassBuilderCollection;
use OpenCodeModeling\CodeAst\Builder\ClassConstBuilder;
use OpenCodeModeling\CodeAst\Builder\ClassMethodBuilder;
use OpenCodeModeling\CodeAst\Builder\ClassPropertyBuilder;
use OpenCodeModeling\CodeAst\Builder\FileCollection;
use OpenCodeModeling\CodeAst\Code\ClassConstGenerator;
use OpenCodeModeling\CodeAst\Package\ClassInfo;
use OpenCodeModeling\CodeAst\Package\ClassInfoList;
use OpenCodeModeling\JsonSchemaToPhp\Type\ArrayType;
use OpenCodeModeling\JsonSchemaToPhp\Type\ObjectType;
use OpenCodeModeling\JsonSchemaToPhp\Type\ReferenceType;
use OpenCodeModeling\JsonSchemaToPhp\Type\ScalarType;
use OpenCodeModeling\JsonSchemaToPhp\Type\TypeDefinition;
use OpenCodeModeling\JsonSchemaToPhp\Type\TypeSet;
use PhpParser\NodeTraverser;
use PhpParser\Parser;
use PhpParser\PrettyPrinterAbstract;

final class ClassGenerator
{
Expand Down Expand Up @@ -57,15 +53,15 @@ public function __construct(

/**
* @param ClassBuilder $classBuilder Main class
* @param ClassBuilderCollection $classBuilderCollection Collection for other classes
* @param FileCollection $fileCollection Collection for other classes
* @param TypeSet $typeSet
* @param string $srcFolder Source folder for namespace imports
* @param string|null $className Class name is used from $classBuilder if not set
* @return void
*/
public function generateClasses(
ClassBuilder $classBuilder,
ClassBuilderCollection $classBuilderCollection,
FileCollection $fileCollection,
TypeSet $typeSet,
string $srcFolder,
string $className = null
Expand Down Expand Up @@ -103,7 +99,7 @@ public function generateClasses(

$this->generateClasses(
ClassBuilder::fromScratch($itemClassName, $classNamespace)->setFinal(true),
$classBuilderCollection,
$fileCollection,
$itemTypeSet,
$srcFolder,
$itemPropertyName
Expand All @@ -113,7 +109,7 @@ public function generateClasses(
case $propertyType instanceof ObjectType:
$this->generateClasses(
ClassBuilder::fromScratch($propertyClassName, $classNamespace)->setFinal(true),
$classBuilderCollection,
$fileCollection,
$propertyTypeSet,
$srcFolder,
$propertyClassName
Expand All @@ -130,7 +126,7 @@ public function generateClasses(
if ($propertyRefType = $propertyType->resolvedType()) {
$this->generateClasses(
ClassBuilder::fromScratch($propertyClassName, $classNamespace)->setFinal(true),
$classBuilderCollection,
$fileCollection,
$propertyRefType,
$srcFolder,
$propertyType->name()
Expand All @@ -146,7 +142,7 @@ public function generateClasses(
$classBuilder->addNamespaceImport($classNamespace . '\\' . $propertyClassName);
break;
case $propertyType instanceof ScalarType:
$classBuilderCollection->add(
$fileCollection->add(
$this->generateValueObject($propertyClassName, $classNamespace, $propertyType)
);
$classBuilder->addNamespaceImport($classNamespace . '\\' . $propertyClassName);
Expand All @@ -161,17 +157,17 @@ public function generateClasses(
break;
}
}
$classBuilderCollection->add($classBuilder);
$fileCollection->add($classBuilder);
break;
case $type instanceof ScalarType:
$classBuilderCollection->add(
$fileCollection->add(
$this->generateValueObject(($this->classNameFilter)($className), $classNamespace, $type)
);
break;
case $type instanceof ArrayType:
$arrayClassBuilder = $this->generateValueObject(($this->classNameFilter)($className), $classNamespace, $type);
$this->addNamespaceImport($arrayClassBuilder, $type);
$classBuilderCollection->add($arrayClassBuilder);
$fileCollection->add($arrayClassBuilder);
break;
default:
break;
Expand All @@ -181,16 +177,19 @@ public function generateClasses(
/**
* Generation of getter methods for value object are skipped.
*
* @param ClassBuilderCollection $classBuilderCollection
* @param FileCollection $classBuilderCollection
* @param bool $typed
* @param callable $methodNameFilter Filter the property name to your desired method name e.g. with get prefix
*/
public function addGetterMethods(
ClassBuilderCollection $classBuilderCollection,
FileCollection $classBuilderCollection,
bool $typed,
callable $methodNameFilter
): void {
foreach ($classBuilderCollection as $classBuilder) {
if (! $classBuilder instanceof ClassBuilder) {
continue;
}
foreach ($classBuilder->getProperties() as $classPropertyBuilder) {
$methodName = ($methodNameFilter)($classPropertyBuilder->getName());

Expand All @@ -211,18 +210,21 @@ public function addGetterMethods(
/**
* Generation of constants for value object are skipped.
*
* @param ClassBuilderCollection $classBuilderCollection
* @param FileCollection $fileCollection
* @param callable $constantNameFilter Converts the name to a proper class constant name
* @param callable $constantValueFilter Converts the name to a proper class constant value e.g. snake_case or camelCase
* @param int $visibility Visibility of the class constant
*/
public function addClassConstantsForProperties(
ClassBuilderCollection $classBuilderCollection,
FileCollection $fileCollection,
callable $constantNameFilter,
callable $constantValueFilter,
int $visibility = ClassConstGenerator::FLAG_PUBLIC
): void {
foreach ($classBuilderCollection as $classBuilder) {
foreach ($fileCollection as $classBuilder) {
if (! $classBuilder instanceof ClassBuilder) {
continue;
}
foreach ($classBuilder->getProperties() as $classPropertyBuilder) {
$constantName = ($constantNameFilter)($classPropertyBuilder->getName());

Expand Down Expand Up @@ -252,50 +254,6 @@ public function generateValueObject(string $className, string $classNamespace, T
return $classBuilder;
}

/**
* @param ClassBuilderCollection $classBuilderCollection
* @param Parser $parser
* @param PrettyPrinterAbstract $printer
* @param callable|null $currentFileAst Callable to return current file AST, if null, file will be overwritten
* @return array<string, string> List of filename => code
*/
public function generateFiles(
ClassBuilderCollection $classBuilderCollection,
Parser $parser,
PrettyPrinterAbstract $printer,
callable $currentFileAst = null
): array {
$files = [];

if ($currentFileAst === null) {
$currentFileAst = static function (ClassBuilder $classBuilder, ClassInfo $classInfo) {
return [];
};
}

$previousNamespace = '__invalid//namespace__';

foreach ($classBuilderCollection as $classBuilder) {
if ($previousNamespace !== $classBuilder->getNamespace()) {
$previousNamespace = $classBuilder->getNamespace();
$classInfo = $this->classInfoList->classInfoForNamespace($previousNamespace);
$path = $classInfo->getPath($classBuilder->getNamespace() . '\\' . $classBuilder->getName());
}
// @phpstan-ignore-next-line
$filename = $classInfo->getFilenameFromPathAndName($path, $classBuilder->getName());

$nodeTraverser = new NodeTraverser();
$classBuilder->injectVisitors($nodeTraverser, $parser);

$files[$filename] = $printer->prettyPrintFile(
// @phpstan-ignore-next-line
$nodeTraverser->traverse($currentFileAst($classBuilder, $classInfo))
);
}

return $files;
}

private function addNamespaceImport(ClassBuilder $classBuilder, TypeDefinition $typeDefinition): void
{
switch (true) {
Expand Down
73 changes: 73 additions & 0 deletions src/FileGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/**
* @see https://github.com/open-code-modeling/json-schema-to-php-ast for the canonical source repository
* @copyright https://github.com/open-code-modeling/json-schema-to-php-ast/blob/master/COPYRIGHT.md
* @license https://github.com/open-code-modeling/json-schema-to-php-ast/blob/master/LICENSE.md MIT License
*/

declare(strict_types=1);

namespace OpenCodeModeling\JsonSchemaToPhpAst;

use OpenCodeModeling\CodeAst\Builder\File;
use OpenCodeModeling\CodeAst\Builder\FileCollection;
use OpenCodeModeling\CodeAst\Package\ClassInfo;
use OpenCodeModeling\CodeAst\Package\ClassInfoList;
use PhpParser\NodeTraverser;
use PhpParser\Parser;
use PhpParser\PrettyPrinterAbstract;

final class FileGenerator
{
private ClassInfoList $classInfoList;

public function __construct(ClassInfoList $classInfoList)
{
$this->classInfoList = $classInfoList;
}

/**
* @param FileCollection $fileCollection
* @param Parser $parser
* @param PrettyPrinterAbstract $printer
* @param callable|null $currentFileAst Callable to return current file AST, if null, file will be overwritten
* @return array<string, string> List of filename => code
*/
public function generateFiles(
FileCollection $fileCollection,
Parser $parser,
PrettyPrinterAbstract $printer,
callable $currentFileAst = null
): array {
$files = [];

if ($currentFileAst === null) {
$currentFileAst = static function (File $file, ClassInfo $classInfo) {
return [];
};
}

$previousNamespace = '__invalid//namespace__';

foreach ($fileCollection as $classBuilder) {
if ($previousNamespace !== $classBuilder->getNamespace()) {
$previousNamespace = $classBuilder->getNamespace();
$classInfo = $this->classInfoList->classInfoForNamespace($previousNamespace);
$path = $classInfo->getPath($classBuilder->getNamespace() . '\\' . $classBuilder->getName());
}
// @phpstan-ignore-next-line
$filename = $classInfo->getFilenameFromPathAndName($path, $classBuilder->getName());

$nodeTraverser = new NodeTraverser();
$classBuilder->injectVisitors($nodeTraverser, $parser);

$files[$filename] = $printer->prettyPrintFile(
// @phpstan-ignore-next-line
$nodeTraverser->traverse($currentFileAst($classBuilder, $classInfo))
);
}

return $files;
}
}
Loading

0 comments on commit da8ddf2

Please sign in to comment.