diff --git a/composer.json b/composer.json index 2c3c043..c91fdde 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "ext-simplexml": "*", "ext-yaml": "*", "aplus/database": "^4.0", + "aplus/debug": "^4.3", "aplus/helpers": "^4.0" }, "require-dev": { diff --git a/src/Config.php b/src/Config.php index 126701d..96b91fa 100644 --- a/src/Config.php +++ b/src/Config.php @@ -9,6 +9,7 @@ */ namespace Framework\Config; +use Framework\Config\Debug\ConfigCollector; use Framework\Helpers\Isolation; use LogicException; use SensitiveParameter; @@ -30,6 +31,7 @@ class Config */ protected array $persistence = []; protected string $suffix; + protected ConfigCollector $debugCollector; /** * Config constructor. @@ -243,6 +245,22 @@ public function getDir() : ?string * @return static */ public function load(string $name) : static + { + if (isset($this->debugCollector)) { + $start = \microtime(true); + $this->loadFile($name); + $end = \microtime(true); + $this->debugCollector->addData([ + 'start' => $start, + 'end' => $end, + 'name' => $name, + ]); + return $this; + } + return $this->loadFile($name); + } + + protected function loadFile(string $name) : static { $filename = $this->configsDir . $name . $this->suffix; $filename = \realpath($filename); @@ -253,4 +271,11 @@ public function load(string $name) : static $this->setMany([$name => $configs]); return $this; } + + public function setDebugCollector(ConfigCollector $debugCollector) : static + { + $this->debugCollector = $debugCollector; + $this->debugCollector->setConfig($this); + return $this; + } } diff --git a/src/Debug/ConfigCollection.php b/src/Debug/ConfigCollection.php new file mode 100644 index 0000000..13793ae --- /dev/null +++ b/src/Debug/ConfigCollection.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Framework\Config\Debug; + +use Framework\Debug\Collection; + +class ConfigCollection extends Collection +{ + protected string $iconPath = __DIR__ . '/icons/config.svg'; +} diff --git a/src/Debug/ConfigCollector.php b/src/Debug/ConfigCollector.php new file mode 100644 index 0000000..9573017 --- /dev/null +++ b/src/Debug/ConfigCollector.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Framework\Config\Debug; + +use Framework\Config\Config; +use Framework\Debug\Collector; +use Framework\Debug\Debugger; +use Framework\Helpers\ArraySimple; + +class ConfigCollector extends Collector +{ + protected Config $config; + + public function setConfig(Config $config) : static + { + $this->config = $config; + return $this; + } + + public function getActivities() : array + { + $activities = []; + foreach ($this->getData() as $data) { + $activities[] = [ + 'collector' => $this->getName(), + 'class' => static::class, + 'description' => 'Load config file ' . \htmlentities($data['name']), + 'start' => $data['start'], + 'end' => $data['end'], + ]; + } + return $activities; + } + + public function getContents() : string + { + if (!isset($this->config)) { + \ob_start(); + echo '

This collector has not been added to a Config instance.

'; + return \ob_get_clean(); // @phpstan-ignore-line + } + $count = \count($this->getConfigs()); + \ob_start(); + $dir = $this->config->getDir(); + if ($dir !== null): + ?> +

Config directory:

+ +

configuration have been set.

+ getTable(); + return \ob_get_clean(); // @phpstan-ignore-line + } + + protected function getTable() : string + { + \ob_start(); + ?> + + + + + + + + + + + getConfigs() as $config): ?> + + + + + + + + + + + + + + + + + +
NameInstancesValuesTime to Load
+ + + + + + + + + + $value): ?> + + + + + + +
KeyType
+ +
+ getData() as $value) { + if ($value['name'] === $config['name']) { + echo Debugger::roundSecondsToMilliseconds($value['end'] - $value['start']); + $found = true; + break; + } + } + if (!$found) { + echo 0; + } + ?> +
+ + + + + + + + + + $value): ?> + + + + + + +
KeyType
+ +
+ + */ + protected function getConfigs() : array + { + $result = []; + foreach ($this->config->getAll() as $name => $instances) { + $count = \count($result); + $result[$count]['name'] = $name; + $result[$count]['instances'] = []; + $counter = 0; + foreach ($instances as $instance => $values) { + $result[$count]['instances'][$counter]['name'] = $instance; + $result[$count]['instances'][$counter]['values'] = ArraySimple::convert($values); + foreach ($result[$count]['instances'][$counter]['values'] as &$value) { + $value = \get_debug_type($value); + } + unset($value); + $counter++; + } + } + return $result; + } +} diff --git a/src/Debug/icons/config.svg b/src/Debug/icons/config.svg new file mode 100644 index 0000000..26db604 --- /dev/null +++ b/src/Debug/icons/config.svg @@ -0,0 +1 @@ + diff --git a/tests/Config/ConfigCollectionTest.php b/tests/Config/ConfigCollectionTest.php new file mode 100644 index 0000000..9ff9bf8 --- /dev/null +++ b/tests/Config/ConfigCollectionTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Tests\Config\Config; + +use Framework\Config\Debug\ConfigCollection; +use PHPUnit\Framework\TestCase; + +final class ConfigCollectionTest extends TestCase +{ + protected ConfigCollection $collection; + + protected function setUp() : void + { + $this->collection = new ConfigCollection('Config'); + } + + public function testIcon() : void + { + self::assertStringContainsString('collection->getIcon()); + } +} diff --git a/tests/Config/ConfigCollectorTest.php b/tests/Config/ConfigCollectorTest.php new file mode 100644 index 0000000..a48bf7f --- /dev/null +++ b/tests/Config/ConfigCollectorTest.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Tests\Config\Config; + +use Framework\Config\Config; +use Framework\Config\Debug\ConfigCollector; +use PHPUnit\Framework\TestCase; + +final class ConfigCollectorTest extends TestCase +{ + protected Config $config; + protected ConfigCollector $collector; + + protected function setUp() : void + { + $this->config = new Config(); + $this->collector = new ConfigCollector(); + } + + protected function makeConfig() : Config + { + return $this->config->setDebugCollector($this->collector); + } + + public function testNoConfig() : void + { + self::assertStringContainsString( + 'This collector has not been added to a Config instance', + $this->collector->getContents() + ); + } + + public function testNoDir() : void + { + $this->makeConfig(); + self::assertStringNotContainsString( + 'Config directory:', + $this->collector->getContents() + ); + } + + public function testDir() : void + { + $dir = __DIR__ . '/config'; + $this->makeConfig()->setDir($dir); + $contents = $this->collector->getContents(); + self::assertStringContainsString( + 'Config directory:', + $contents + ); + self::assertStringContainsString( + $dir, + $contents + ); + } + + public function testNoConfigs() : void + { + $this->makeConfig(); + self::assertStringContainsString( + '0 configurations have been set.', + $this->collector->getContents() + ); + } + + public function testLoadConfigs() : void + { + $this->makeConfig() + ->setDir(__DIR__ . '/config') + ->load('foo'); + self::assertStringContainsString( + '1 configuration have been set.', + $this->collector->getContents() + ); + $this->config->load('bar')->load('baz'); + self::assertStringContainsString( + '3 configurations have been set.', + $this->collector->getContents() + ); + self::assertStringContainsString( + 'foo[bar]', + $this->collector->getContents() + ); + } + + public function testManyConfigs() : void + { + $this->makeConfig() + ->setDir(__DIR__ . '/config') + ->setMany([ + 'database' => [ + 'default' => [ + 'host' => 'localhost', + 'failover' => [ + 'host' => '192.168.1.1', + 'port' => 3306, + ], + ], + 'replica' => [ + 'host' => '192.168.1.100', + ], + ], + 'cache' => [ + 'default' => [ + 'host' => '127.0.0.1', + ], + ], + ]); + self::assertStringContainsString( + '2 configurations have been set.', + $this->collector->getContents() + ); + $this->config->load('foo'); + self::assertStringContainsString( + '3 configurations have been set.', + $this->collector->getContents() + ); + self::assertStringContainsString( + 'foo[bar]', + $this->collector->getContents() + ); + self::assertStringContainsString( + 'failover[host]', + $this->collector->getContents() + ); + } + + public function testActivities() : void + { + $this->makeConfig(); + $this->config->setDir(__DIR__ . '/config'); + self::assertEmpty($this->collector->getActivities()); + $this->config->load('foo'); + self::assertSame( + [ + 'collector', + 'class', + 'description', + 'start', + 'end', + ], + \array_keys($this->collector->getActivities()[0]) // @phpstan-ignore-line + ); + $this->config->load('bar'); + self::assertCount(2, $this->collector->getActivities()); + } +} diff --git a/tests/Config/config/bar.php b/tests/Config/config/bar.php new file mode 100644 index 0000000..bd817cf --- /dev/null +++ b/tests/Config/config/bar.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +return [ + 'default' => [ + 'foo' => 'bar', + ], +]; diff --git a/tests/Config/config/baz.php b/tests/Config/config/baz.php new file mode 100644 index 0000000..bd817cf --- /dev/null +++ b/tests/Config/config/baz.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +return [ + 'default' => [ + 'foo' => 'bar', + ], +]; diff --git a/tests/Config/config/foo.php b/tests/Config/config/foo.php new file mode 100644 index 0000000..0b58202 --- /dev/null +++ b/tests/Config/config/foo.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +return [ + 'default' => [ + 'foo' => [ + 'bar' => 'baz', + ], + 'int' => 3, + ], + 'other' => [ + 'foo' => 'bar', + 'int' => 3, + ], +];