Added loaders

This commit is contained in:
R. Eric Wheeler 2017-11-07 13:30:35 -08:00
parent 4b81fc105e
commit a9e7b22989
14 changed files with 508 additions and 21 deletions

View File

@ -4,29 +4,37 @@
"type": "project", "type": "project",
"require": { "require": {
"php": ">=7.1", "php": ">=7.1",
"dflydev/dot-access-data": "1.1", "dflydev/dot-access-data": "^1.1",
"symfony/filesystem": "^3.3", "symfony/filesystem": "^3.3"
"symfony/yaml": "^3.3"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Sikofitt\\Configuration\\": "src/Sikofitt/Configuration" "Sikofitt\\Config\\": "src/Sikofitt/Config"
} }
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.6", "friendsofphp/php-cs-fixer": "^2.6",
"nette/neon": "^2.4",
"phpro/grumphp": "^0.12.0", "phpro/grumphp": "^0.12.0",
"phpstan/phpstan": "^0.8.5", "phpstan/phpstan": "^0.8.5",
"phpunit/phpunit": "~5.7|~6.3", "phpunit/phpunit": "~5.7|~6.3",
"sensiolabs/security-checker": "^4.1", "sensiolabs/security-checker": "^4.1",
"squizlabs/php_codesniffer": "^3.1", "squizlabs/php_codesniffer": "^3.1",
"symfony/var-dumper": "^3.3" "symfony/var-dumper": "^3.3",
"symfony/yaml": "^3.3",
"webmozart/json": "^1.2"
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"Sikofitt\\Tests\\": "tests/Sikofitt/Tests" "Sikofitt\\Tests\\": "tests/Sikofitt/Tests"
} }
}, },
"suggest": {
"webmozart/json": "For using the webmozart json parser. Instead of the default php one.",
"nette/neon": "For parsing .neon files.",
"symfony/yaml": "For parsing yaml files."
},
"license": "GPL-3.0", "license": "GPL-3.0",
"authors": [ "authors": [
{ {

View File

@ -29,10 +29,10 @@ parameters:
phpversion: phpversion:
project: '7.1' project: '7.1'
phpcs: phpcs:
standard: PSR2 standard: 'phpcs.xml.dist'
show_warnings: true show_warnings: true
encoding: UTF8 encoding: UTF8
triggered_by: [php] triggered_by: ['php']
securitychecker: securitychecker:
run_always: false run_always: false
xmllint: xmllint:

View File

@ -20,7 +20,15 @@
namespace Sikofitt\Config; namespace Sikofitt\Config;
use Dflydev\DotAccessData\Data; use Dflydev\DotAccessData\Data;
use Symfony\Component\Config\FileLocatorInterface; use Sikofitt\Config\Loader\IniFileLoader;
use Sikofitt\Config\Loader\JsonFileLoader;
use Sikofitt\Config\Loader\NeonFileLoader;
use Sikofitt\Config\Loader\PhpFileLoader;
use Sikofitt\Config\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\GlobFileLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Filesystem\Exception\FileNotFoundException; use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
@ -33,26 +41,60 @@ class DotConfig
{ {
private $config; private $config;
private $arrayConfig; private $arrayConfig;
private $delegateLoader;
/** /**
* Configuration constructor. * Configuration constructor.
* *
* @param \Symfony\Component\Config\FileLocatorInterface $fileLocator * @param string|array $configResource
* @param string $configDirectory
* *
* @throws \InvalidArgumentException * @throws \Exception
* @throws \Symfony\Component\Yaml\Exception\ParseException
* @throws \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException
*/ */
public function __construct( public function __construct(
FileLocatorInterface $fileLocator, $configResource
string $configDirectory
) { ) {
$file = $fileLocator->locate('config.yaml', $configDirectory); $this->arrayConfig = [];
$contents = file_get_contents($file);
$data = Yaml::parse($contents); $locator = new FileLocator();
$this->arrayConfig = $data; $loaderResolver = new LoaderResolver();
$this->config = new Data($data); $loaderResolver->addLoader(new PhpFileLoader($locator));
$loaderResolver->addLoader(new JsonFileLoader($locator));
$loaderResolver->addLoader(new IniFileLoader($locator));
$loaderResolver->addLoader(new GlobFileLoader($locator));
if (class_exists('Symfony\Component\Yaml\Yaml')) {
$loaderResolver->addLoader(new YamlFileLoader($locator));
}
if (class_exists('Nette\Neon\Neon')) {
$loaderResolver->addLoader(new NeonFileLoader($locator));
}
$this->delegateLoader = new DelegatingLoader($loaderResolver);
if (is_array($configResource)) {
$configResource = $this->loadDirectories($configResource);
$configResource = $this->loadFiles($configResource);
$configResource = array_shift($configResource);
if (!empty($configResource)) {
$this->arrayConfig = array_merge_recursive(
$configResource,
$this->arrayConfig
);
}
} else {
if (is_file($configResource)) {
$this->arrayConfig = $this->delegateLoader->load($configResource);
}
}
$this->config = new Data($this->arrayConfig);
}
public function getArrayConfig(): array
{
return $this->arrayConfig;
} }
/** /**
@ -70,7 +112,7 @@ class DotConfig
$data[$key] = (array)$value; $data[$key] = (array)$value;
} }
} }
$data = Yaml::dump($data, 4, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); $data = Yaml::dump($data, 4, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
return (bool)file_put_contents($fileName, $data); return (bool)file_put_contents($fileName, $data);
} }
@ -245,4 +287,54 @@ class DotConfig
{ {
return $this->config->has($key); return $this->config->has($key);
} }
/**
* @param array $directories
*
* @throws \Exception
* @return array
*/
private function loadDirectories(array $directories): array
{
foreach ($directories as $key => $config) {
if (is_string($config) && is_dir($config)) {
$this->arrayConfig = array_merge_recursive(
$this->arrayConfig,
$this->delegateLoader->load($config . '/**', 'glob')
);
unset($directories[$key]);
$this->arrayConfig = $this->mergeGlob($this->arrayConfig);
}
}
return $directories;
}
/**
* @param array $files
*
* @throws \Exception
* @return array
*/
private function loadFiles(array $files): array
{
foreach ($files as $key => $config) {
if (is_string($config) && is_file($config)) {
$this->arrayConfig = array_merge_recursive($this->arrayConfig, $this->delegateLoader->load($config));
unset($files[$key]);
}
}
return $files;
}
private function mergeGlob(array $globResource): array
{
$result = [];
foreach ($globResource as $glob) {
$result = array_merge_recursive($result, $glob);
}
return $result;
}
} }

View File

@ -0,0 +1,46 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Config\Loader\Exception;
use Throwable;
/**
* An Error Exception.
*
* @link http://php.net/manual/en/class.errorexception.php
*/
class JsonDecodingException extends \ErrorException
{
public function __construct(
$severity = E_RECOVERABLE_ERROR,
$fileName = __FILE__,
$line = __LINE__,
Throwable $previous
) {
parent::__construct(
json_last_error_msg(),
json_last_error(),
$severity,
$fileName,
$line,
$previous
);
}
}

View File

@ -0,0 +1,57 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Config\Loader;
use Symfony\Component\Config\Loader\FileLoader;
class IniFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $resource The resource
* @param string|null $type The resource type or null if unknown
*
* @throws \Exception If something went wrong
* @return array|bool
*
*/
public function load($resource, $type = null)
{
$data = parse_ini_file($resource, true);
return false === $data ? [] : $data;
}
/**
* Returns whether this class supports the given resource.
*
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return bool True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return
$type === 'ini' ||
pathinfo($resource, PATHINFO_EXTENSION) === 'ini';
}
}

View File

@ -0,0 +1,62 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Config\Loader;
use Symfony\Component\Config\Loader\FileLoader;
use Webmozart\Json\JsonDecoder;
class JsonFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $resource The resource
* @param string|null $type The resource type or null if unknown
*
* @throws \Exception If something went wrong
* @return mixed
*
*/
public function load($resource, $type = null)
{
if (class_exists('Webmozart\Json\JsonDecoder')) {
$decoder = new JsonDecoder();
return (array)$decoder->decodeFile($resource);
}
return \json_decode(file_get_contents($resource), true);
}
/**
* Returns whether this class supports the given resource.
*
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return bool True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null): bool
{
return
$type === 'json' ||
pathinfo($resource, PATHINFO_EXTENSION) === 'json';
}
}

View File

@ -0,0 +1,56 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Config\Loader;
use Nette\Neon\Neon;
use Symfony\Component\Config\Loader\FileLoader;
class NeonFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $resource The resource
* @param string|null $type The resource type or null if unknown
*
* @throws \Exception If something went wrong
* @return mixed
*
*/
public function load($resource, $type = null)
{
return Neon::decode(file_get_contents($resource));
}
/**
* Returns whether this class supports the given resource.
*
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return bool True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return
$type === 'neon' ||
pathinfo($resource, PATHINFO_EXTENSION) === 'neon';
}
}

View File

@ -0,0 +1,60 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Config\Loader;
use Symfony\Component\Config\Loader\FileLoader;
class PhpFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $resource The resource
* @param string|null $type The resource type or null if unknown
*
* @throws \Exception If something went wrong
* @return array
*
*/
public function load($resource, $type = null): array
{
return
require $resource;
}
/**
* Returns whether this class supports the given resource.
*
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return bool True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null): bool
{
return
$type === 'php' ||
in_array(
pathinfo($resource, PATHINFO_EXTENSION),
['php', 'inc'],
true
);
}
}

View File

@ -0,0 +1,55 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Config\Loader;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Yaml\Yaml;
class YamlFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $resource The resource
* @param string|null $type The resource type or null if unknown
*
* @throws \Exception If something went wrong
* @return mixed
*/
public function load($resource, $type = null)
{
return Yaml::parse(file_get_contents($resource));
}
/**
* Returns whether this class supports the given resource.
*
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return bool True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return
is_string($resource) &&
in_array(pathinfo($resource, PATHINFO_EXTENSION), ['yaml', 'yml'], true);
}
}

4
tests/fixtures/config.ini vendored Normal file
View File

@ -0,0 +1,4 @@
[config]
value = ini
ini_test[] = testing1
ini_test[] = testing2

9
tests/fixtures/config.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"config": {
"value": "json",
"json_test": [
"testing1",
"testing2"
]
}
}

5
tests/fixtures/config.neon vendored Normal file
View File

@ -0,0 +1,5 @@
config:
value: neon
neon_test:
- testing1
- testing2

28
tests/fixtures/config.php vendored Normal file
View File

@ -0,0 +1,28 @@
<?php declare(strict_types=1);
/*
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
return [
'config' => [
'value' => 'php',
'php_test' => [
'testing1',
'testing2',
],
],
];

5
tests/fixtures/config.yaml vendored Normal file
View File

@ -0,0 +1,5 @@
config:
value: 'yaml'
yaml_test:
- testing
- testing1