Skip to content

Commit 9ef4596

Browse files
authored
Merge pull request #6 from phpcfdi/version-4.0.0
Refactorizar reintentos y limpiar aplicación (versión 4)
2 parents 6631d87 + bfd7ebf commit 9ef4596

File tree

9 files changed

+423
-263
lines changed

9 files changed

+423
-263
lines changed

.github/workflows/system.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,4 @@ jobs:
4343
- name: Install project dependencies
4444
run: composer upgrade --no-interaction --no-progress --prefer-dist --no-dev
4545
- name: System test with PHP ${{ matrix.php-version }}
46-
run: php bin/sat-pys-scraper --json build/result.json --xml build/result.xml --sort key
47-
env:
48-
MAX_TRIES: 5
46+
run: php bin/sat-pys-scraper --tries 5 --json build/result.json --xml build/result.xml --sort key

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ sin temor a romper tu aplicación.
173173
|---------|----------|-----------------------------------|
174174
| 1.0.0 | 8.2, 8.3 | 2023-12-13 Fuera de mantenimiento |
175175
| 2.0.0 | 8.2, 8.3 | 2024-03-07 Fuera de mantenimiento |
176-
| 3.0.0 | 8.2, 8.3 | 2024-03-07 |
176+
| 3.0.0 | 8.2, 8.3 | 2024-03-07 Fuera de mantenimiento |
177+
| 4.0.0 | 8.2, 8.3 | 2024-10-17 |
177178

178179
## Contribuciones
179180

bin/sat-pys-scraper

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,4 @@ use PhpCfdi\SatPysScraper\App\SatPysScraper;
77

88
require __DIR__ . '/../vendor/autoload.php';
99

10-
exit(call_user_func(function (string ...$argv): int {
11-
$result = 1;
12-
$maxTries = ($_SERVER['MAX_TRIES'] ?? null) ?? ($_ENV['MAX_TRIES'] ?? null);
13-
$maxTries = is_numeric($maxTries) ? intval($maxTries) : 1;
14-
for ($try = 0; $try < $maxTries; $try++) {
15-
$result = SatPysScraper::run($argv);
16-
if (0 === $result) {
17-
break;
18-
}
19-
}
20-
return $result;
21-
}, ...$argv));
10+
exit((new SatPysScraper())->run($argv));

docs/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ versión, aunque sí su incorporación en la rama principal de trabajo. Generalm
1111

1212
## Listado de cambios
1313

14+
### Versión 4.0.0 2024-10-17
15+
16+
Esta es una actualización de refactorización que obliga a crear una versión mayor.
17+
Si no utilizas entidades del espacio de nombres `PhpCfdi\SatPysScraper\App` entonces puedes hacer el cambio
18+
de la versión `3.x` a la versión `4.x` sin conflictos. En caso contrario debes revisar tu implementación.
19+
20+
- Se agrega el parámetro `--debug` que, si existe, vuelca los datos del error de ejecución.
21+
- Se agrega el parámetro `--tries` que, si existe, reintenta la descarga de información hasta ese número de veces.
22+
- Se extrae el procesamiento de argumentos a su propia clase.
23+
- Se extrae el almacenamiento de argumentos a su propia clase en lugar de un arreglo.
24+
- Se reorganizan las pruebas de acuerdo a los cambios previos.
25+
- La ejecución del flujo de trabajo `system.yml` en el trabajo `system-tests` se configura con `--tries 5`.
26+
- Se vuelve a simplificar la herramienta `bin/sat-pys-scraper` para que toda su lógica esté probada.
27+
- Ya no se usa la variable de entorno `MAX_TRIES`.
28+
1429
### Versión 3.0.2 2024-10-17
1530

1631
A la herramienta `bin/sat-pys-scraper` se le puede definir un número máximo de ejecuciones en la

src/App/Arguments.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpCfdi\SatPysScraper\App;
6+
7+
final readonly class Arguments
8+
{
9+
public function __construct(
10+
public string $xml,
11+
public string $json,
12+
public string $sort,
13+
public int $tries,
14+
public bool $quiet,
15+
public bool $debug,
16+
) {
17+
}
18+
}

src/App/ArgumentsBuilder.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpCfdi\SatPysScraper\App;
6+
7+
final class ArgumentsBuilder
8+
{
9+
private string $xml = '';
10+
11+
private string $json = '';
12+
13+
private string $sort = 'key';
14+
15+
private int $tries = 1;
16+
17+
private bool $quiet = false;
18+
19+
private bool $debug = false;
20+
21+
/** @throws ArgumentException */
22+
public function build(string ...$arguments): Arguments
23+
{
24+
$arguments = array_values($arguments);
25+
while ([] !== $arguments) {
26+
$argument = (string) array_shift($arguments);
27+
match (true) {
28+
in_array($argument, ['--xml', '-x'], true) => $this->setXml((string) array_shift($arguments)),
29+
in_array($argument, ['--json', '-j'], true) => $this->setJson((string) array_shift($arguments)),
30+
in_array($argument, ['--sort', '-s'], true) => $this->setSort((string) array_shift($arguments)),
31+
in_array($argument, ['--tries', '-t'], true) => $this->setTries((string) array_shift($arguments)),
32+
in_array($argument, ['--quiet', '-q'], true) => $this->setQuiet(),
33+
in_array($argument, ['--debug', '-d'], true) => $this->setDebug(),
34+
default => throw new ArgumentException(sprintf('Invalid argument "%s"', $argument)),
35+
};
36+
}
37+
38+
if ('' === $this->xml && '' === $this->json) {
39+
throw new ArgumentException('Did not specify --xml or --json arguments');
40+
}
41+
if ('-' === $this->xml && '-' === $this->json) {
42+
throw new ArgumentException('Cannot send --xml and --json result to standard output at the same time');
43+
}
44+
45+
return new Arguments(
46+
xml: '-' === $this->xml ? 'php://stdout' : $this->xml,
47+
json: '-' === $this->json ? 'php://stdout' : $this->json,
48+
sort: $this->sort,
49+
tries: $this->tries,
50+
quiet: $this->quiet,
51+
debug: $this->debug,
52+
);
53+
}
54+
55+
private function setXml(string $argument): void
56+
{
57+
$this->xml = $argument;
58+
if ('-' === $argument) {
59+
$this->quiet = true;
60+
}
61+
}
62+
63+
private function setJson(string $argument): void
64+
{
65+
$this->json = $argument;
66+
if ('-' === $argument) {
67+
$this->quiet = true;
68+
}
69+
}
70+
71+
/** @throws ArgumentException */
72+
private function setSort(string $argument): void
73+
{
74+
if (! in_array($argument, ['key', 'name'], true)) {
75+
throw new ArgumentException(sprintf('Invalid sort "%s"', $argument));
76+
}
77+
$this->sort = $argument;
78+
}
79+
80+
/** @throws ArgumentException */
81+
private function setTries(string $argument): void
82+
{
83+
$this->tries = (int) $argument;
84+
if ((string) $this->tries !== $argument || $this->tries < 1) {
85+
throw new ArgumentException(sprintf('Invalid tries "%s"', $argument));
86+
}
87+
}
88+
89+
private function setQuiet(): void
90+
{
91+
$this->quiet = true;
92+
}
93+
94+
private function setDebug(): void
95+
{
96+
$this->debug = true;
97+
}
98+
}

src/App/SatPysScraper.php

Lines changed: 60 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use GuzzleHttp\Client;
88
use PhpCfdi\SatPysScraper\Data\Types;
9+
use PhpCfdi\SatPysScraper\Exceptions\HttpException;
10+
use PhpCfdi\SatPysScraper\Exceptions\HttpServerException;
911
use PhpCfdi\SatPysScraper\Generator;
1012
use PhpCfdi\SatPysScraper\NullGeneratorTracker;
1113
use PhpCfdi\SatPysScraper\Scraper;
@@ -15,40 +17,14 @@
1517

1618
final readonly class SatPysScraper
1719
{
18-
/**
19-
* @param list<string> $arguments
20-
*/
21-
public function __construct(private string $command, private array $arguments, private ScraperInterface $scraper)
20+
public function printHelp(string $command): void
2221
{
23-
}
24-
25-
/** @param string[] $argv */
26-
public static function run(
27-
array $argv,
28-
ScraperInterface $scraper = new Scraper(new Client()),
29-
string $stdErrFile = 'php://stderr'
30-
): int {
31-
$command = (string) array_shift($argv);
32-
$argv = array_values($argv);
33-
$app = new self($command, $argv, $scraper);
34-
try {
35-
$app->execute();
36-
return 0;
37-
} catch (Throwable $exception) {
38-
file_put_contents($stdErrFile, 'ERROR: ' . $exception->getMessage() . PHP_EOL, FILE_APPEND);
39-
return 1;
40-
}
41-
}
42-
43-
public function printHelp(): void
44-
{
45-
$command = basename($this->command);
4622
echo <<< HELP
4723
$command - Crea un archivo XML con la clasificación de productos y servicios del SAT.
4824
4925
Sintaxis:
5026
$command help|-h|--help
51-
$command [--quiet|-q] [--json|-j JSON_FILE] [--xml|-x XML_FILE]
27+
$command [--quiet|-q] [--debug|-d] [--json|-j JSON_FILE] [--xml|-x XML_FILE] [--tries|-t TRIES]
5228
5329
Argumentos:
5430
--xml|-x XML_FILE
@@ -59,12 +35,18 @@ public function printHelp(): void
5935
los datos generados en formato JSON.
6036
--sort|-s SORT
6137
Establece el orden de elementos, default: key, se puede usar "key" o "name".
38+
--tries|-t TRIES
39+
Establece cuántas veces debe intentar hacer la descarga si encuentra un error de servidor.
40+
Default: 1. El valor debe ser mayor o igual a 1.
41+
--debug|-d
42+
Mensajes de intentos e información del error se envían a la salida estándar de error.
6243
--quiet|-q
6344
Modo de operación silencioso.
6445
6546
Notas:
6647
Debe especificar al menos un argumento "--xml" o "--json", o ambos.
6748
No se puede especificar "-" como salida de "--xml" y "--json" al mismo tiempo.
49+
Al especificar la salida "-" se activa automáticamente el modo silencioso.
6850
6951
Acerca de:
7052
Este script pertenece al proyecto https://github.com/phpcfdi/sat-pys-scraper
@@ -74,84 +56,66 @@ public function printHelp(): void
7456
HELP;
7557
}
7658

77-
/** @throws ArgumentException */
78-
public function execute(): void
59+
/** @param list<string> $argv */
60+
public function run(array $argv, ScraperInterface|null $scraper = null, string $stdErrFile = 'php://stderr'): int
7961
{
80-
if ([] !== array_intersect($this->arguments, ['help', '-h', '--help'])) {
81-
$this->printHelp();
82-
return;
83-
}
84-
85-
$arguments = $this->processArguments(...$this->arguments);
86-
$tracker = ($arguments['quiet']) ? new NullGeneratorTracker() : new PrinterGeneratorTracker();
87-
$types = (new Generator($this->scraper, $tracker))->generate();
88-
89-
// sort types
90-
match ($arguments['sort']) {
91-
'key' => $types->sortByKey(),
92-
'name' => $types->sortByName(),
93-
default => throw new ArgumentException('Unrecognized sort argument'),
94-
};
62+
$command = (string) array_shift($argv);
63+
$app = new self();
9564

96-
if ('' !== $arguments['xml']) {
97-
$this->toXml($arguments['xml'], $types);
98-
}
99-
if ('' !== $arguments['json']) {
100-
$this->toJson($arguments['json'], $types);
65+
if ([] !== array_intersect($argv, ['help', '-h', '--help'])) {
66+
$app->printHelp(basename($command));
67+
return 0;
10168
}
102-
}
69+
$debug = [] !== array_intersect($argv, ['-d', '--debug']);
70+
$try = 0;
10371

104-
/**
105-
* @return array{xml: string, json: string, quiet: bool, sort: string}
106-
* @throws ArgumentException
107-
*/
108-
public function processArguments(string ...$arguments): array
109-
{
110-
$arguments = array_values($arguments);
111-
$xml = '';
112-
$json = '';
113-
$quiet = false;
114-
$sort = 'key';
115-
116-
while ([] !== $arguments) {
117-
$argument = (string) array_shift($arguments);
118-
if (in_array($argument, ['--xml', '-x'], true)) {
119-
$xml = (string) array_shift($arguments);
120-
} elseif (in_array($argument, ['--json', '-j'], true)) {
121-
$json = (string) array_shift($arguments);
122-
} elseif (in_array($argument, ['--sort', '-s'], true)) {
123-
$sort = (string) array_shift($arguments);
124-
if (! in_array($sort, ['key', 'name'])) {
125-
throw new ArgumentException(sprintf('Invalid sort "%s"', $sort));
72+
try {
73+
$arguments = (new ArgumentsBuilder())->build(...$argv);
74+
$debug = $arguments->debug;
75+
do {
76+
$try = $try + 1;
77+
try {
78+
$app->execute($arguments, $scraper);
79+
$serverException = null;
80+
break;
81+
} catch (HttpServerException $exception) {
82+
$serverException = $exception;
83+
usleep(1000);
12684
}
127-
} elseif (in_array($argument, ['--quiet', '-q'], true)) {
128-
$quiet = true;
129-
} else {
130-
throw new ArgumentException(sprintf('Invalid argument "%s"', $argument));
85+
} while ($try < $arguments->tries);
86+
if (null !== $serverException) {
87+
throw $serverException;
13188
}
89+
} catch (Throwable $exception) {
90+
file_put_contents($stdErrFile, 'ERROR: ' . $exception->getMessage() . PHP_EOL, FILE_APPEND);
91+
if ($debug) {
92+
file_put_contents($stdErrFile, "The procedure was executed $try times\n", FILE_APPEND);
93+
file_put_contents($stdErrFile, print_r($exception, true), FILE_APPEND);
94+
}
95+
return 1;
13296
}
97+
return 0;
98+
}
13399

134-
if ('' === $xml && '' === $json) {
135-
throw new ArgumentException('Did not specify --xml or --json arguments');
136-
}
137-
if ('-' === $xml && '-' === $json) {
138-
throw new ArgumentException('Cannot send --xml and --json result to standard output at the same time');
139-
}
140-
if ('-' === $xml) {
141-
$xml = 'php://stdout';
142-
$quiet = true;
100+
/** @throws HttpServerException|HttpException */
101+
private function execute(Arguments $arguments, ScraperInterface|null $scraper): void
102+
{
103+
$tracker = ($arguments->quiet) ? new NullGeneratorTracker() : new PrinterGeneratorTracker();
104+
$scraper ??= new Scraper(new Client());
105+
$types = (new Generator($scraper, $tracker))->generate();
106+
107+
// sort types
108+
match ($arguments->sort) {
109+
'name' => $types->sortByName(),
110+
default => $types->sortByKey(),
111+
};
112+
113+
if ('' !== $arguments->xml) {
114+
$this->toXml($arguments->xml, $types);
143115
}
144-
if ('-' === $json) {
145-
$json = 'php://stdout';
146-
$quiet = true;
116+
if ('' !== $arguments->json) {
117+
$this->toJson($arguments->json, $types);
147118
}
148-
149-
return [
150-
'xml' => $xml,
151-
'json' => $json,
152-
'quiet' => $quiet,
153-
'sort' => $sort,
154-
];
155119
}
156120

157121
public function toXml(string $output, Types $types): void

0 commit comments

Comments
 (0)