Skip to content

Commit

Permalink
added schema report command, fixed bug with schema inheritance, cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
oliwierptak committed Aug 6, 2021
1 parent bb0fe46 commit 6ad2443
Show file tree
Hide file tree
Showing 37 changed files with 1,049 additions and 508 deletions.
366 changes: 250 additions & 116 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions bin/popo
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

require_once getcwd().'/vendor/autoload.php';

///use Popo\Command\ConfigureCommand;
use Popo\Command\GenerateCommand;
use Popo\Command\ReportCommand;
use Symfony\Component\Console\Application;

$console = new Application('popo', \Popo\PopoDefinesInterface::VERSION);
$console->add(new GenerateCommand());
//$console->add(new ConfigureCommand());
$console->add(new ReportCommand());

$console->run();
26 changes: 14 additions & 12 deletions popo-schema.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
$: #shared configuration for all POPO objects defined below
namespace: string
outputPath: string
namespaceRoot: string|null # if set remaps namespace and outputPath
extend: string|null # which class POPO objects should extend from
implement: string|null # which interface POPO objects should implement
comment: string|null # Class docblock comment
config:
namespace: string
outputPath: string
namespaceRoot: string|null # if set remaps namespace and outputPath
extend: string|null # which class POPO objects should extend from
implement: string|null # which interface POPO objects should implement
comment: string|null # Class docblock comment
default: array # default values
property: array #shared properties

SchemaName: #defines shared configuration and POPO objects under SchemaName
$: # shared configuration for all POPO objects in SchemaName
namespace: string
outputPath: string
namespaceRoot: string|null
extend: string|null
implement: string|null
comment: string|null
config:
namespace: string
outputPath: string
namespaceRoot: string|null
extend: string|null
implement: string|null
comment: string|null
default: array
property: [{
name: string,
Expand Down
214 changes: 127 additions & 87 deletions src/Popo/Builder/SchemaBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Popo\Builder;

use JetBrains\PhpStorm\Pure;
use Popo\Loader\SchemaLoader;
use Popo\PopoConfigurator;
use Popo\PopoDefinesInterface;
Expand All @@ -12,8 +13,6 @@
use Popo\Schema\Schema;
use Popo\Schema\Property;
use Popo\Schema\SchemaFile;
use RuntimeException;
use function array_key_exists;

class SchemaBuilder
{
Expand All @@ -25,73 +24,157 @@ public function build(PopoConfigurator $configurator): array
{
$result = [];
$data = $this->loader->load($configurator);
$globalConfig = $this->generateSharedConfig($configurator, $data);

foreach ($data as $schemaFile) {
foreach ($schemaFile->getData() as $schemaName => $popoCollection) {
$schemaConfigData = $this->configMerger->mergeGlobalSchema($globalConfig, $schemaName);

foreach ($popoCollection as $popoName => $popoData) {
$popoData = $this->configMerger->mergePopoSchema($schemaFile, $schemaConfigData, $popoData);

$popoSchema = (new Schema)
->setName($popoName)
->setSchemaName($schemaName)
->setDefault($popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_DEFAULT] ?? [])
->setConfig(
(new Config)->fromArray($popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_CONFIG])
);

$this->configMerger->updateSchemaConfigFromCommandConfiguration($popoSchema, $configurator);

$this->validate(
$popoSchema,
$popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY],
$schemaFile->getFilename()->getPathname()
$sharedSchemaFile = $this->loadSharedConfig($configurator);
$tree = $this->generateSchemaTree($data, $sharedSchemaFile);

foreach ($tree as $schemaName => $popoCollection) {
foreach ($popoCollection as $popoName => $popoData) {
$popoSchema = (new Schema)
->setName($popoName)
->setSchemaName($schemaName)
->setDefault($popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_DEFAULT] ?? [])
->setConfig(
(new Config)->fromArray($popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_CONFIG])
);

$result[$schemaName][$popoName] = $this->buildSchemaPropertyCollection(
$popoSchema,
$popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY],
);
}
$popoSchema = $this->updateSchemaConfigFromCommandConfiguration(
$popoSchema,
$configurator
);

$result[$schemaName][$popoName] = $this->buildSchemaPropertyCollection(
$popoSchema,
$popoData[PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY],
);
}
}

return $result;
}

protected function generateSharedConfig(PopoConfigurator $configurator, array $data): SchemaFile
protected function loadSharedConfig(PopoConfigurator $configurator): SchemaFile
{
$file = (new SchemaFile)->setSharedConfig(
[
PopoDefinesInterface::CONFIGURATION_SCHEMA_CONFIG => [],
PopoDefinesInterface::CONFIGURATION_SCHEMA_DEFAULT => [],
PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY => [],
]
);
$file = (new SchemaFile)->setFileConfig(PopoDefinesInterface::SCHEMA_DEFAULT_DATA);

$schemaConfigFilename = trim((string) $configurator->getSchemaConfigFilename());
if ($schemaConfigFilename === '') {
return $file;
}

$sharedConfigFile = current(
return current(
$this->loader->load(
(new PopoConfigurator)->setSchemaPath($configurator->getSchemaConfigFilename())
)
);
}

/**
* @param SchemaFile[] $data
* @param \Popo\Schema\SchemaFile $sharedSchemaFile
*
* @return array
*/
#[Pure] protected function generateSchemaTree(array $data, SchemaFile $sharedSchemaFile): array
{
$result = [];

foreach ($data as $schemaFile) {
$schemaCollection = array_merge($sharedSchemaFile->getData(), $schemaFile->getData());

return $this->configMerger->generateSharedConfig($sharedConfigFile, $data);
foreach ($schemaCollection as $schemaName => $popoCollection) {
$schemaConfigData = $this->configMerger->mergeSchemaConfiguration(
$schemaName,
$sharedSchemaFile,
$schemaFile
);

$result = $this->configMerger->mergePopoCollection(
$schemaName,
$popoCollection,
$schemaConfigData,
$result
);
}
}

return $result;
}

protected function buildSchemaPropertyCollection(Schema $schema, array $propertyCollection): Schema
public function generateSchemaReport(PopoConfigurator $configurator): array
{
$sharedSchemaFile = $this->loadSharedConfig($configurator);
$data = $this->loader->load($configurator);

return $this->generateReport($data, $sharedSchemaFile);
}

/**
* @param SchemaFile[] $data
* @param \Popo\Schema\SchemaFile $sharedSchemaFile
*
* @return array
*/
protected function generateReport(array $data, SchemaFile $sharedSchemaFile): array
{
$propertyCollection = $this->sortPropertyCollectionByName($propertyCollection);
$result = [];

foreach ($data as $schemaFile) {
$fileConfig = $schemaFile->getFileConfig()['property'] ?? [];
$schemaCollection = array_merge($sharedSchemaFile->getData(), $schemaFile->getData());

foreach ($schemaCollection as $schemaName => $popoCollection) {
$schemaConfigData = array_merge(
$sharedSchemaFile->getSchemaConfig()[$schemaName]['property'] ?? [],
$schemaFile->getSchemaConfig()[$schemaName]['property'] ?? []
);

foreach ($popoCollection as $popoName => $popoData) {
foreach ($popoData['property'] as $propertyData) {
$result[$schemaName][$popoName][$propertyData['name']][] = 'property-config:' . $schemaFile
->getFilename()
->getPathname();
}

foreach ($fileConfig as $dataItem) {
$result[$schemaName][$popoName][$dataItem['name']][] = 'file-config:' . $schemaFile
->getFilename()
->getPathname();
}

foreach ($schemaConfigData as $dataItem) {
$result[$schemaName][$popoName][$dataItem['name']][] = 'schema-config:' . $schemaFile
->getFilename()
->getPathname();
}
}
}
}

return $result;
}

public function updateSchemaConfigFromCommandConfiguration(
Schema $popoSchema,
PopoConfigurator $configurator
): Schema {
$popoSchema->getConfig()->setNamespace(
$configurator->getNamespace() ?? $popoSchema->getConfig()->getNamespace()
);
$popoSchema->getConfig()->setNamespaceRoot(
$configurator->getNamespaceRoot() ?? $popoSchema->getConfig()->getNamespaceRoot()
);
$popoSchema->getConfig()->setOutputPath(
$configurator->getOutputPath() ?? $popoSchema->getConfig()->getOutputPath()
);

return $popoSchema;
}

protected function buildSchemaPropertyCollection(Schema $schema, array $propertyCollection): Schema
{
$properties = [];
foreach ($propertyCollection as $propertyData) {
$properties[] = $this->buildProperty($schema, $propertyData);
foreach ($propertyCollection as $propertyName => $propertyData) {
$properties[$propertyName] = $this->buildProperty($schema, $propertyData);
}

$schema->setPropertyCollection($properties);
Expand All @@ -112,47 +195,4 @@ private function buildProperty(Schema $schema, array $propertyData): Property

return $property;
}

protected function sortPropertyCollectionByName(array $propertyCollection): array
{
usort(
$propertyCollection,
function (mixed $a, mixed $b) {
return strcasecmp(
$a[PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY_NAME],
$b[PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY_NAME]
);
}
);

return $propertyCollection;
}

/**
* @param \Popo\Schema\Schema $popoSchema
* @param array $propertyCollection
* @param string $filename
*
* @return void
* @throws \RuntimeException
*/
protected function validate(Schema $popoSchema, array $propertyCollection, string $filename): void
{
$names = [];
foreach ($propertyCollection as $property) {
if (array_key_exists($property[PopoDefinesInterface::CONFIGURATION_SCHEMA_PROPERTY_NAME], $names)) {
throw new RuntimeException(
sprintf(
'Property with name "%s" is already defined and cannot be used for "%s::%s" in "%s"',
$property['name'],
$popoSchema->getSchemaName(),
$popoSchema->getName(),
$filename
)
);
}

$names[$property['name']] = true;
}
}
}
2 changes: 1 addition & 1 deletion src/Popo/Command/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
try {
return $this->executeCommand($input, $output);
} catch (Throwable $exception) {
$output->writeln(sprintf('<fg=red>ERROR</> <fg=white>v%s</>', $exception->getMessage()));
$output->writeln(sprintf('<fg=red>ERROR</> <fg=white>%s</>', $exception->getMessage()));
return 1;
}
}
Expand Down
Loading

0 comments on commit 6ad2443

Please sign in to comment.