Skip to content

Commit 88a139b

Browse files
Add Config Package
0 parents  commit 88a139b

12 files changed

Lines changed: 485 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor/
2+
composer.lock

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# HackPHP Config
2+
HackPHP Config Access.
3+
4+
## Usage
5+
6+
```bash
7+
composer require hackphp/config "1.0"
8+
```
9+
10+
```php
11+
use Hackphp\Config\Config;
12+
use Hackphp\Config\Parsers\ArrayParser;
13+
14+
$config = new Config([
15+
"app" => [
16+
"name" => "HackPHP"
17+
],
18+
"server" => [
19+
"host" => "127.0.0.1",
20+
"port" => 8000
21+
],
22+
]);
23+
24+
$configRootDir = __DIR__ . "/config";
25+
$config->addParser(new ArrayParser($configRootDir));
26+
$config->load();
27+
28+
echo $config->get("server.host");
29+
// output: 127.0.0.1
30+
31+
echo $config->get("file.key", "default value");
32+
// output: default value
33+
```

composer.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "hackphp/config",
3+
"description": "HackPHP Config Access.",
4+
"type": "library",
5+
"license": "MIT",
6+
"autoload": {
7+
"psr-4": {
8+
"Hackphp\\Config\\": "src/"
9+
}
10+
},
11+
"autoload-dev": {
12+
"psr-4": {
13+
"Hackphp\\Tests\\Config\\": "tests/"
14+
},
15+
"files": [
16+
"tests/helpers.php"
17+
]
18+
},
19+
"authors": [
20+
{
21+
"name": "Mohamed Samir",
22+
"email": "gm.mohamedsamir@gmail.com"
23+
}
24+
],
25+
"minimum-stability": "dev",
26+
"require-dev": {
27+
"phpunit/phpunit": "9.5.x-dev"
28+
},
29+
"scripts": {
30+
"test": "./vendor/bin/phpunit --do-not-cache-result --testdox"
31+
}
32+
}

phpunit.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit
4+
bootstrap = "vendor/autoload.php"
5+
backupGlobals = "false"
6+
backupStaticAttributes = "false"
7+
colors = "true"
8+
convertErrorsToExceptions = "true"
9+
convertNoticesToExceptions = "true"
10+
convertWarningsToExceptions = "true"
11+
processIsolation = "false"
12+
stopOnFailure = "false">
13+
14+
<testsuites>
15+
<testsuite name="Test Suite">
16+
<directory>tests</directory>
17+
</testsuite>
18+
</testsuites>
19+
20+
<php>
21+
<env name="APP_ENV" value="testing"/>
22+
</php>
23+
24+
</phpunit>

src/Config.php

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
3+
namespace Hackphp\Config;
4+
5+
use Hackphp\Config\Contracts\Parser;
6+
7+
class Config
8+
{
9+
/**
10+
* The parsed config.
11+
*
12+
* @var array
13+
*/
14+
protected array $config;
15+
16+
/**
17+
* The parsers classes.
18+
*
19+
* @var Parser[]
20+
*/
21+
protected array $parsers = [];
22+
23+
/**
24+
* Create new Config.
25+
*
26+
* @param array $config
27+
*/
28+
public function __construct(array $config = [])
29+
{
30+
$this->config = $config;
31+
}
32+
33+
/**
34+
* Add Config Parser.
35+
*
36+
* @param Parser $parser
37+
* @return $this
38+
*/
39+
public function addParser(Parser $parser)
40+
{
41+
if (!in_array($parser, $this->parsers)) {
42+
$this->parsers[] = $parser;
43+
}
44+
45+
return $this;
46+
}
47+
48+
/**
49+
* Load the config loader.
50+
*
51+
* @return $this
52+
*/
53+
public function load()
54+
{
55+
foreach ($this->parsers as $parser) {
56+
$this->config = array_merge($this->config, $parser->parse());
57+
}
58+
59+
return $this;
60+
}
61+
62+
/**
63+
* Get the given config value.
64+
*
65+
* @param string $key
66+
* @param mixed|null $default
67+
* @return mixed|null
68+
*/
69+
public function get($key, $default = null)
70+
{
71+
$filtered = $this->config;
72+
73+
if (isset($filtered[$key])) {
74+
return $filtered[$key];
75+
}
76+
77+
$this->catchSearchFile($filtered, $key);
78+
79+
return $this->getFilteredValue($filtered, $key, $default);
80+
}
81+
82+
/**
83+
* Catch the file that has the key we search about it.
84+
*
85+
* @param array $filtered
86+
* @param string $key
87+
* @return array
88+
*/
89+
private function catchSearchFile(array &$filtered, $key)
90+
{
91+
return array_reduce(explode(".", $key), function ($carry, $segment) use (&$filtered) {
92+
if ($carry != null) {
93+
$key = $carry . "." . $segment;
94+
} else {
95+
$key = $segment;
96+
}
97+
98+
$values = $this->filterStartsWith($filtered, $key);
99+
100+
if ($values) {
101+
$filtered = $values;
102+
}
103+
104+
return $key;
105+
});
106+
}
107+
108+
/**
109+
* Get the array items that it's key starts with the given name.
110+
*
111+
* @param array $haystack
112+
* @param string $name
113+
* @return array
114+
*/
115+
private function filterStartsWith(array $haystack, string $name)
116+
{
117+
$filtered = [];
118+
119+
foreach ($haystack as $key => $value) {
120+
if (strpos($key, $name) === 0) {
121+
$filtered[$key] = $value;
122+
}
123+
}
124+
125+
return $filtered;
126+
}
127+
128+
/**
129+
* Get the given key value from the filtered configs.
130+
*
131+
* @param array $filtered
132+
* @param string $key
133+
* @param mixed $default
134+
* @return mixed
135+
*/
136+
private function getFilteredValue($filtered, $key, $default)
137+
{
138+
if (empty($filtered)) {
139+
return $default;
140+
}
141+
142+
$file = array_keys($filtered)[0];
143+
$filtered = array_values($filtered)[0];
144+
145+
$key = str_replace($file . ".", "", $key);
146+
147+
foreach (explode(".", $key) as $segment) {
148+
$filtered = $filtered[$segment] ?? $default;
149+
}
150+
151+
return $filtered ?? $default;
152+
}
153+
}

src/Contracts/Parser.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Hackphp\Config\Contracts;
4+
5+
interface Parser
6+
{
7+
/**
8+
* Parse the config files.
9+
*
10+
* @return void
11+
*/
12+
public function parse();
13+
}

src/Parsers/ArrayParser.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace Hackphp\Config\Parsers;
4+
5+
use Hackphp\Config\Contracts\Parser;
6+
use Throwable;
7+
8+
class ArrayParser implements Parser
9+
{
10+
/**
11+
* The config files directory path.
12+
*
13+
* @var string
14+
*/
15+
protected string $directory;
16+
17+
/**
18+
* Create new ArrayParser
19+
*
20+
* @param string $directory The config files directory path.
21+
*/
22+
public function __construct(string $directory)
23+
{
24+
$this->directory = $directory;
25+
}
26+
27+
/**
28+
* @inheritDoc
29+
*/
30+
public function parse()
31+
{
32+
$parsed = [];
33+
34+
$files = $this->getConfigFiles($this->directory);
35+
36+
foreach ($files as $namespace => $filePath) {
37+
try {
38+
$parsed[$namespace] = require $filePath;
39+
} catch (Throwable $e) {
40+
//
41+
}
42+
}
43+
44+
unset($files);
45+
46+
return $parsed;
47+
}
48+
49+
/**
50+
* Get the config files.
51+
*
52+
* @param string $dir
53+
* @return array
54+
*/
55+
protected function getConfigFiles($dir)
56+
{
57+
$files = [];
58+
59+
foreach (getDirFiles($dir) as $file) {
60+
$path = $dir . "/" . $file;
61+
62+
if (is_dir($path)) {
63+
$files = array_merge($files, $this->getConfigFiles($path));
64+
continue;
65+
}
66+
67+
$configPath = str_replace($this->directory . "/", "", $path);
68+
69+
$parts = explode("/", $configPath);
70+
$parts[count($parts) - 1] = str_replace(".php", "", end($parts));
71+
72+
$files[implode(".", $parts)] = $path;
73+
}
74+
75+
return $files;
76+
}
77+
}

tests/ConfigTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Hackphp\Config\Tests;
4+
5+
use Hackphp\Config\Config;
6+
use Hackphp\Config\Parsers\ArrayParser;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class ConfigTest extends TestCase
10+
{
11+
/** @test */
12+
public function it_can_create_instance_with_default_configs()
13+
{
14+
$config = new Config([
15+
"creator" => "Mohamed Samir",
16+
"email" => "gm.mohamedsamir@gmail.com"
17+
]);
18+
19+
$creator = $config->get("creator");
20+
21+
$this->assertEquals("Mohamed Samir", $creator);
22+
}
23+
24+
/** @test */
25+
public function it_loads_and_parse_all_directory_files_successfully()
26+
{
27+
$parser = new ArrayParser(configPath());
28+
29+
$config = new Config();
30+
$config->addParser($parser);
31+
$config->load();
32+
33+
$value = $config->get("app.name");
34+
$this->assertEquals("HackPHP", $value);
35+
}
36+
37+
/** @test */
38+
public function it_loads_the_default_value_if_the_key_not_found()
39+
{
40+
$parser = new ArrayParser(configPath());
41+
42+
$config = new Config();
43+
$config->addParser($parser);
44+
$config->load();
45+
46+
$value = $config->get("app.test");
47+
$this->assertNull($value);
48+
49+
$value = $config->get("app.test", true);
50+
$this->assertTrue($value);
51+
}
52+
}

0 commit comments

Comments
 (0)