Skip to content

Commit

Permalink
Merge pull request #81 from eclipxe13/version-2.18
Browse files Browse the repository at this point in the history
Prepare version 2.18.0
  • Loading branch information
eclipxe13 committed Dec 17, 2021
2 parents d63c855 + b42568a commit cd60eef
Show file tree
Hide file tree
Showing 42 changed files with 1,641 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -55,6 +55,7 @@
},
"autoload-dev": {
"psr-4": {
"CfdiUtils\\Development\\": "development/",
"CfdiUtilsTests\\": "tests/CfdiUtilsTests/"
}
},
Expand Down Expand Up @@ -82,7 +83,7 @@
"@php vendor/bin/phpstan analyse --no-progress src/ tests/"
],
"dev:coverage": [
"@php -dzend_extension=xdebug.so vendor/bin/phpunit --coverage-text --coverage-html build/coverage/html/"
"@php -dzend_extension=xdebug.so -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html build/coverage/html/"
]
},
"scripts-descriptions": {
Expand Down
57 changes: 57 additions & 0 deletions development/BaseCliApplication.php
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace CfdiUtils\Development;

abstract class BaseCliApplication
{
/** @var string */
private $command;

/** @var string[] */
private $arguments;

abstract public function printHelp(): void;

abstract public function execute(): int;

final public function __construct(string $command, string ...$arguments)
{
$this->command = $command;
$this->arguments = $arguments;
}

public function __invoke(): int
{
if ([] !== array_intersect(['-h', '--help'], $this->arguments)) {
$this->printHelp();
return 0;
}

try {
return $this->execute();
} catch (\Throwable $exception) {
file_put_contents('php://stderr', $exception->getMessage() . PHP_EOL, FILE_APPEND);
// file_put_contents('php://stderr', get_class($exception) . ': ' . $exception->getMessage() . PHP_EOL, FILE_APPEND);
// file_put_contents('php://stderr', $exception->getTraceAsString() . PHP_EOL, FILE_APPEND);
return min(1, $exception->getCode());
}
}

public function getCommand(): string
{
return $this->command;
}

/** @return string[] */
public function getArguments(): array
{
return $this->arguments;
}

public function getArgument(int $index): string
{
return $this->arguments[$index] ?? '';
}
}
38 changes: 38 additions & 0 deletions development/ElementsMaker/Dictionary.php
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace CfdiUtils\Development\ElementsMaker;

final class Dictionary
{
/** @var array<string, string> */
private $values;

/** @param array<string, string> $values */
public function __construct(array $values)
{
$this->values = $values;
}

public function get(string $key): string
{
return $this->values[$key] ?? '';
}

public function with(string $key, string $value): self
{
return new self(array_merge($this->values, [$key => $value]));
}

/** @return array<string, string> $values */
public function getValues(): array
{
return $this->values;
}

public function interpolate(string $subject): string
{
return strtr($subject, $this->values);
}
}
88 changes: 88 additions & 0 deletions development/ElementsMaker/ElementsMaker.php
@@ -0,0 +1,88 @@
<?php

declare(strict_types=1);

namespace CfdiUtils\Development\ElementsMaker;

final class ElementsMaker
{
/** @var Specifications */
private $specs;

/** @var string */
private $outputDir;

/** @var array<string, string> */
private $templates = [];

public function __construct(Specifications $specs, string $outputDir)
{
$this->specs = $specs;
$this->outputDir = $outputDir;
}

public static function make(string $specFile, string $outputDir): self
{
return new self(Specifications::makeFromFile($specFile), $outputDir);
}

public function write(): void
{
$this->createElement($this->specs->getStructure(), $this->specs->getDictionary(), true);
}

public function createElement(Structure $structure, Dictionary $dictionary, bool $isRoot = false): void
{
$prefix = $dictionary->get('#prefix#');
$dictionary = $dictionary->with('#element-name#', $structure->getName());
$sectionsContent = [];
$orderElements = $structure->getChildrenNames($prefix . ':');

if (count($orderElements) > 1) {
$sectionsContent[] = $this->template(
'get-children-order',
new Dictionary(['#elements#' => $this->elementsToString($orderElements)])
);
}

if ($isRoot) {
$sectionsContent[] = $this->template('get-fixed-attributes', $dictionary);
}

/** @var Structure $child */
foreach ($structure as $child) {
$childTemplate = ($child->isMultiple()) ? 'child-multiple' : 'child-single';
$sectionsContent[] = $this->template($childTemplate, new Dictionary(['#child-name#' => $child->getName()]));
$this->createElement($child, $dictionary);
}

$contents = $this->template('element', $dictionary->with('#sections#', implode('', $sectionsContent)));
$outputFile = $this->buildOutputFile($structure->getName());
file_put_contents($outputFile, $contents);
}

private function template(string $templateName, Dictionary $dictionary): string
{
if (! isset($this->templates[$templateName])) {
$fileName = __DIR__ . '/templates/' . $templateName . '.template';
$this->templates[$templateName] = file_get_contents($fileName) ?: '';
}

return $dictionary->interpolate($this->templates[$templateName]);
}

private function buildOutputFile(string $elementName): string
{
return $this->outputDir . DIRECTORY_SEPARATOR . $elementName . '.php';
}

/** @param string[] $array */
private function elementsToString(array $array): string
{
$parts = [];
foreach ($array as $value) {
$parts[] = var_export($value, true);
}
return "[\n" . implode(",\n", $parts) . ']';
}
}
51 changes: 51 additions & 0 deletions development/ElementsMaker/Specifications.php
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace CfdiUtils\Development\ElementsMaker;

final class Specifications
{
/** @var Structure */
private $structure;

/** @var Dictionary */
private $dictionary;

public function __construct(Structure $structure, Dictionary $dictionary)
{
$this->structure = $structure;
$this->dictionary = $dictionary;
}

public static function makeFromFile(string $specFile): self
{
$specFileReader = SpecificationsReader::fromFile($specFile);

$structure = Structure::makeFromStdClass(
$specFileReader->keyAsString('root-element'),
$specFileReader->keyAsStdClass('structure')
);

$dictionary = new Dictionary([
'#php-namespace#' => $specFileReader->keyAsString('php-namespace'),
'#prefix#' => $specFileReader->keyAsString('prefix'),
'#xml-namespace#' => $specFileReader->keyAsString('xml-namespace'),
'#xml-schemalocation#' => $specFileReader->keyAsString('xml-schemalocation'),
'#version-attribute#' => $specFileReader->keyAsString('version-attribute'),
'#version-value#' => $specFileReader->keyAsString('version-value'),
]);

return new self($structure, $dictionary);
}

public function getStructure(): Structure
{
return $this->structure;
}

public function getDictionary(): Dictionary
{
return $this->dictionary;
}
}
67 changes: 67 additions & 0 deletions development/ElementsMaker/SpecificationsReader.php
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace CfdiUtils\Development\ElementsMaker;

use JsonException;
use RuntimeException;
use stdClass;

final class SpecificationsReader
{
/** @var stdClass */
private $data;

public function __construct(stdClass $data)
{
$this->data = $data;
}

public static function fromFile(string $specFile): self
{
if (! file_exists($specFile)) {
throw new RuntimeException("Specification file '$specFile' does not exists");
}
$specContents = file_get_contents($specFile);
if (false === $specContents) {
throw new RuntimeException("Unable to read $specFile");
}
return self::fromJsonString($specContents);
}

public static function fromJsonString(string $specContents): self
{
try {
$data = json_decode($specContents, false, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw new RuntimeException('Unable to parse the JSON specification', 0, $exception);
}
if (! $data instanceof stdClass) {
throw new RuntimeException('The JSON specification does not contains a valid root object');
}
return new self($data);
}

public function keyAsString(string $name): string
{
if (! isset($this->data->{$name})) {
return '';
}
if (! is_string($this->data->{$name})) {
return '';
}
return $this->data->{$name};
}

public function keyAsStdClass(string $name): stdClass
{
if (! isset($this->data->{$name})) {
return (object) [];
}
if (! $this->data->{$name} instanceof stdClass) {
return (object) [];
}
return $this->data->{$name};
}
}

0 comments on commit cd60eef

Please sign in to comment.