Skip to content

Commit

Permalink
Add MergeConfigs command
Browse files Browse the repository at this point in the history
  • Loading branch information
natanfelles committed Jan 10, 2025
1 parent 6456a7d commit fec18b2
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 0 deletions.
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<env name="DB_USERNAME" value="root"/>
<env name="DB_PASSWORD" value="password"/>
<env name="DB_SCHEMA" value="framework-tests"/>
<const name="IS_IN_PHPUNIT" value="true"/>
</php>
<source>
<include>
Expand Down
131 changes: 131 additions & 0 deletions src/MergeConfigs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php declare(strict_types=1);
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Framework\CLI\Commands;

use Framework\CLI\CLI;
use Framework\CLI\Command;
use Framework\Helpers\Isolation;
use Framework\MVC\App;
use RuntimeException;

/**
* Class MergeConfigs.
*
* @package dev-commands
*/
class MergeConfigs extends Command
{
protected string $description = 'Merge configuration files.';
protected string $group = 'Configs';
protected array $options = [
'--extension' => 'Set a custom file extension. Default is ".php"',
'-i' => 'Ignore files that do not return an array in the correct format.',
];
protected string $usage = 'mergeconfigs [options] -- [directory]';

protected function getDirectory() : string
{
$dir = $this->console->getArgument(0);
if ($dir) {
return $this->validateDir($dir);
}
$options = [];
foreach (['config', 'configs'] as $option) {
$dir = \getcwd() . '/' . $option;
if (\is_dir($dir)) {
$options[] = $option;
}
}
$dir = \defined('IS_IN_PHPUNIT') && IS_IN_PHPUNIT
? __DIR__ . '/../tests/configs/ok'
: CLI::prompt('Config directory', $options);
return $this->validateDir($dir);
}

protected function validateDir(string $dir) : string
{
if (!\is_dir($dir)) {
throw new RuntimeException('Config directory "' . $dir . '" does not exist');
}
return $dir;
}

protected function getExtension() : string
{
$extension = $this->console->getOption('extension');
if ($extension === true || $extension === null) {
return '.php';
}
return $extension; // @phpstan-ignore-line;
}

public function run() : void
{
$dir = $this->getDirectory();
$extension = $this->getExtension();
$config = [];
$files = App::locator()->listFiles($dir);
foreach ($files as $file) {
if (!\str_ends_with($file, $extension)) {
continue;
}
\ob_start();
$contents = Isolation::require($file);
\ob_end_clean();
if (!\is_array($contents)) {
if ($this->allowIgnore()) {
continue;
}
throw new RuntimeException(
'Config file "' . $file . '" does not return an array'
);
}
if (empty($contents)) {
if ($this->allowIgnore()) {
continue;
}
throw new RuntimeException(
'Config file "' . $file . '" return an empty array'
);
}
foreach ($contents as $key => $values) {
if (!\is_string($key)) {
if ($this->allowIgnore()) {
continue 2;
}
throw new RuntimeException(
'Config file "' . $file . '" return invalid keys (must be strings)'
);
}
if (!\is_array($values)) {
if ($this->allowIgnore()) {
continue 2;
}
throw new RuntimeException(
'Config file "' . $file . '" return invalid values (must be arrays)'
);
}
}
$service = \substr($file, \strrpos($file, \DIRECTORY_SEPARATOR) + 1);
$service = \substr($service, 0, -\strlen($extension));
$config[$service] = $contents;
}
$config = \var_export($config, true);
CLI::write('<?php');
CLI::write('// Do not edit this file. It is created automatically.');
CLI::write('// Created at: ' . \gmdate('Y-m-d H:i:s') . ' UTC');
CLI::write('return ' . $config . ';');
}

public function allowIgnore() : bool
{
return (bool) $this->console->getOption('i');
}
}
144 changes: 144 additions & 0 deletions tests/MergeConfigsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Tests\CLI\Commands;

use Framework\Config\Config;
use Framework\Testing\TestCase;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

#[RunTestsInSeparateProcesses]
final class MergeConfigsTest extends TestCase
{
protected function prepareDefaults() : void
{
$this->config = new Config([
'console' => [
'default' => [
'directories' => [
__DIR__ . '/../src',
],
],
],
]);
parent::prepareDefaults();
}

protected function makeDir(string $relativePath) : string
{
$dir = __DIR__ . \DIRECTORY_SEPARATOR . $relativePath;
return \str_replace(' ', '\ ', $dir);
}

protected function runCli(string $relativePath, string $append = '') : void
{
$dir = $this->makeDir($relativePath);
$append = $append !== '' ? ' ' . $append : '';
$this->app->runCli('mergeconfigs ' . $dir . $append);
}

public function testNoDir() : void
{
$this->app->runCli('mergeconfigs');
self::assertStdoutContains('<?php');
}

public function testCustomExtension() : void
{
$this->app->runCli('mergeconfigs --extension=.php');
self::assertStdoutContains('<?php');
}

public function testInvalidDir() : void
{
$this->expectException(\RuntimeException::class);
$path = $this->makeDir('foo');
$this->expectExceptionMessage('Config directory "' . $path . '" does not exist');
$this->runCli('foo');
}

public function testEmptyDir() : void
{
$this->runCli('configs/empty-dir');
self::assertStdoutContains('<?php');
self::assertStdoutContains('// Do not edit this file. It is created automatically.');
self::assertStdoutContains('return array (');
self::assertStdoutContains(');');
self::assertStdoutNotContains("'bar' =>");
}

public function testOk() : void
{
$this->runCli('configs/ok');
self::assertStdoutContains('<?php');
self::assertStdoutContains('// Do not edit this file. It is created automatically.');
self::assertStdoutContains('return array (');
self::assertStdoutContains("'bar' =>");
self::assertStdoutContains("'key' => 'value'");
self::assertStdoutContains("'other' =>");
self::assertStdoutContains("'foo' => 'baz'");
self::assertStdoutContains(');');
}

public function testNotArray() : void
{
$this->expectException(\RuntimeException::class);
$path = \realpath(__DIR__ . '/configs/not-array/foo.php');
$this->expectExceptionMessage('Config file "' . $path . '" does not return an array');
$this->runCli('configs/not-array');
}

public function testNotArrayWithIgnore() : void
{
$this->runCli('configs/not-array', '-i');
self::assertStdoutContains('<?php');
}

public function testEmptyArray() : void
{
$this->expectException(\RuntimeException::class);
$path = \realpath(__DIR__ . '/configs/empty-array/foo.php');
$this->expectExceptionMessage('Config file "' . $path . '" return an empty array');
$this->runCli('configs/empty-array');
}

public function testEmptyArrayWithIgnore() : void
{
$this->runCli('configs/empty-array', '-i');
self::assertStdoutContains('<?php');
}

public function testInvalidKeys() : void
{
$this->expectException(\RuntimeException::class);
$path = \realpath(__DIR__ . '/configs/invalid-keys/foo.php');
$this->expectExceptionMessage('Config file "' . $path . '" return invalid keys (must be strings)');
$this->runCli('configs/invalid-keys');
}

public function testInvalidKeysWithIgnore() : void
{
$this->runCli('configs/invalid-keys', '-i');
self::assertStdoutContains('<?php');
}

public function testInvalidValues() : void
{
$this->expectException(\RuntimeException::class);
$path = \realpath(__DIR__ . '/configs/invalid-values/foo.php');
$this->expectExceptionMessage('Config file "' . $path . '" return invalid values (must be arrays)');
$this->runCli('configs/invalid-values');
}

public function testInvalidValuesWithIgnore() : void
{
$this->runCli('configs/invalid-values', '-i');
self::assertStdoutContains('<?php');
}
}
10 changes: 10 additions & 0 deletions tests/configs/empty-array/foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [];
Empty file.
13 changes: 13 additions & 0 deletions tests/configs/invalid-keys/foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
1 => [],
3 => [],
];
12 changes: 12 additions & 0 deletions tests/configs/invalid-values/foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
'foo' => 'bar',
];
9 changes: 9 additions & 0 deletions tests/configs/not-array/foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
14 changes: 14 additions & 0 deletions tests/configs/ok/bar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
'default' => [
'key' => 'value',
],
];
17 changes: 17 additions & 0 deletions tests/configs/ok/foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of Aplus Framework Dev Commands Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
'default' => [
'key' => 'value',
],
'other' => [
'foo' => 'baz',
],
];

0 comments on commit fec18b2

Please sign in to comment.