selene / config
Selene 配置组件
Requires
- php: >=5.4.0
- selene/filesystem: dev-development
- selene/xml: dev-development
Requires (Dev)
- league/phpunit-coverage-listener: dev-master
- mikey179/vfsstream: ~1.2.0
- selene/testsuite: dev-development
This package is not auto-updated.
Last update: 2015-12-05 10:10:40 UTC
README
安装
可以通过composer安装此组件。
{
"require":{
"selene/config":"dev-development"
}
}
然后运行
$ composer install
加载器
配置包包含三种不同的基础加载器:PhpFileLoader、XmlFileLoader和CallableLoader。两者都扩展了FileLoader,并需要一个资源定位器实例。
加载实现取决于你。例如,假设你有一个名为Config的类,它将所有配置值存储为一个关联数组。
<?php
namespace Acme\Config;
class Config
{
protected $config;
public function __construct(array $config = [])
{
$this->config = $config;
}
public function addValues(array $values)
{
$this->config = array_merge((array)$this->config, $values);
}
public function get($key, $default = null)
{
if (…) {
return $value;
}
return $default;
}
public function all()
{
return $this->config;
}
}
你的PHP加载器可能看起来像这样
<?php
namespace Acme\Config;
use \Selene\Components\Config\Loader\PhpFileLoader;
use \Selene\Components\Config\Resource\LocatorInterface;
class PhpLoader extends PhpFileLoader
{
public function __construct(Config $config, LocatorInterface $locator)
{
$this->config = $config;
parent__construct($locator);
}
protected function doLoad($file)
{
$values = parent::doLoad($file);
$this->config->addValues($values);
}
}
加载文件
使用你的新加载器,你现在可以像这样加载配置文件
<?php
use \Acme\Config\Config;
use \Acme\Config\PhpLoader;
use \Selene\Components\Config\Resource\Locator;
$config = new Config;
$locator = new Locator(['path/config/a', 'path/config/b']);
$loader = new PhpLoader($config, $locator;
$loader->load('config.php');
注意,定位器接受一个路径数组,用于定位配置文件。默认情况下,使用第一个结果,其余路径被丢弃。然而,如果你将布尔值(true)传递给load()方法的第二个参数,将定位和加载所有路径中的文件。
<?php
$loader->load('config.php', PhpLoader::LOAD_ALL);
加载不同类型的资源
可以使用DelegatingLoader类加载不同类型的配置。
<?php
use \Acme\Config\PhpLoader;
use \Acme\Config\JsonLoader;
use \Selene\Components\Config\Loader\Resolver;
use \Selene\Components\Config\Loader\DelegatingLoader;
$loader = new DelegatingLoader(new Resolver(
new PhpLoader($config, $locator),
new JsonLoader($config, $locator)
));
$loader->load('config.php', PhpLoader::LOAD_ALL);
$loader->load('config.json', PhpLoader::LOAD_ALL);
自定义加载器
理论上,你可以创建各种加载器,例如JSON加载器。
<?php
namespace Acme\Config;
use \Selene\Components\Config\Loader\FileLoader;
class JsonFileLoader extends FileLoader
{
protected $extension = 'json';
/**
* {@inheritdoc}
*/
protected function doLoad($file)
{
$contents = json_decode(file_get_contents($file), true);
// setter logic goes here.
}
}
监听加载器事件
每次加载资源时,加载器将通知已注册在加载器上的监听器。监听器必须实现Selene\Components\Config\Loader\LoaderListener。
例如,你可能想要将加载事件记录到你的应用程序日志文件中。
<?php
namespace Acme\Config;
use \Psr\Log;
use \Selene\Components\Config\Loader\LoaderListener;
class ConfigLoaderLogger extends LoaderListener
{
public function __construct(Log $logger)
{
$this->logger = $logger;
}
public function onLoaded($resource)
{
$this->logger->info(sprintf('Loaded resource "%s"', $resource));
}
}
只需使用addListener()方法将监听器添加到你的配置加载器中。
<?php $configLoaderLogger = new ConfigLoaderLogger($logger); $loader->addListener($configLoaderLogger);
如果你需要删除监听器,可以使用removeListener方法。
<?php $loader->removeListener($configLoaderLogger);
缓存
根据情况,缓存配置可能很有用,例如,每次请求都解析xml文件将影响性能。相反,你可能会解析配置文件一次,并将内容存储在PHP数组中。
<?php
use \Acme\Config\Config;
use \Acme\Config\XmlLoader;
use \Selene\Components\Config\Cache;
use \Selene\Components\Config\Resource\Locator;
$cache = new Cache($file);
if ($cache->isValid()) {
$config = new Config(include $file);
} else {
$config = new Config;
$loader = new XmlLoader($config, new Locator($paths));
$loader->load('config.xml');
// …
$cache->write("<?php\n return ".var_export($config->all()).';');
}
上述解决方案适用于加载单个文件。如果给定的缓存文件自上次请求以来已修改,则Cache::isValid()将报告false。然而,实际配置文件不会被考虑。
缓存能够进行资源检查。只需将true传递给构造函数作为第二个参数,并在写入缓存时提供要跟踪的资源列表。
这就是加载器的事件能力发挥作用的地方。让我们通过实现LoaderListener接口来修改Acme\Config\Config类。
<?php
namespace Acme\Config;
use \Selene\Components\Config\Loader\PhpFileLoader;
use \Selene\Components\Config\Loader\LoaderListener;
use \Selene\Components\Config\Resource\FileResource;
use \Selene\Components\Config\Resource\ObjectResource;
use \Selene\Components\Config\Resource\LocatorInterface;
class PhpLoader extends PhpFileLoader implements LoaderListener
{
private $resources;
//…
public function onLoaded($resource)
{
if (is_object($resource)) {
$this->resources[] = new ObjectResource($resource);
} elseif(is_string($file)) {
$this->resources[] = new FileResource($resource);
}
}
public function getResources()
{
return $this->resources;
}
}
现在,正在加载的每个资源都将推送到一个数组中,我们可以将其用作缓存将用于跟踪更改的文件列表。
<?php
$cache = new Cache($file, true);
// do the loading proceedure.
// write the cache file content and pass in the resources to be tracked.
$cache->write(
"<?php\n return ".var_export($config->all()).';',
$config->getResources()
);
验证
验证器允许您验证关联数组。在加载之前验证用户定义的配置非常有用。
验证器附带一个Builder类,允许您将配置结构定义为树。然后树将与输入数组进行验证。
<?php
use \Selene\Components\Config\Validator\Builder;
$builder = new Builder('config');
$builder
->getRoot()
// build the tree
$validator = $builder->getValidator();
$validator->load($config);
$validator->validate();
节点类型
有两种不同的节点类型。标量节点和数组节点。
它们都共享用于在验证时定义其行为的公共方法。
optional()
标记节点为必填。notEmpty()
标记节点不为空。空值的定义取决于节点类型。例如,在字符串节点中,空字符串是空值。defaultValue($value)
如果节点是可选的且缺失或为空,则使用此值。condition()
开始一个条件块。请参阅下文中的条件。end()
在构建器上下文中,end()将返回当前节点的父节点。
标量节点表示标量值,如字符串、布尔值、整数等。
与数组节点不同,标量节点不能有子节点。
布尔值表示布尔值。
<?php
$builder
->getRoot()
->boolean('required')
->end();
字符串表示字符串值。
<?php
$builder
->getRoot()
->string('name')
->end();
整数表示整数。
<?php
$builder
->getRoot()
->integer('port')
->end();
浮点数表示浮点值。
<?php
$builder
->getRoot()
->float('precission')
->end();
数组节点数组节点可以包含类型为标量和数组的子节点。
字典表示关联数组。
<?php
$builder
->getRoot()
->dict('memcached_server')
->string('host')->end()
->integer('port')->end()
->integer('weight')->end()
->end();
值表示索引数组。
注意,你只能在值节点上定义一个子节点。
子节点表示应该在索引数组中存在的值类型。
<?php
$builder
->getRoot()
->values('paths')
->string('path')->end()
->end();
节点条件节点条件作为简单的if/then块。只有当“if”部分返回true时,“then”部分才会执行。
<?php
$root
->string()
->condition()
->when(function ($value) {…})
->then(function ($value) {…})
->end()
->end();
有几个预定义的“if”条件。
always()
总是执行。when(Closure $expression)
评估闭包中的表达式以返回true或false。ifTrue(Closure $expression)
与when相同。ifMissing()
只有当值(指输入的键)缺失时才会触发。ifEmpty()
只有当值为空时才会触发。ifNull()
只有当值为null时才会触发。ifArray()
与when相同。只有当输入值是数组时才会触发。ifNotArray()
只有当输入值不是数组时才会触发。ifInArray(array $values)
只有当输入值在$values数组中时才会触发。ifNotInArray()
与when相同。只有当输入值不在$values数组中时才会触发。ifString()
只有当输入值是字符串时才会触发。
还有几个预定义的“then”结果。
ifInt()
只有当输入值是整数时才会触发。ifFloat()
只有当输入值是浮点值时才会触发。thenMarkInvalid
标记节点无效。thenRemove
取消设置当前节点并将其从父节点中删除。