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
取消设置当前节点并将其从父节点中删除。