我找不到与我的问题相当的问题,但是如果您可以找到一个随时让我知道的问题。
我试图弄清楚如何有效地创建一个简洁的配置对象。
我希望对象(或某种类型的配置管理器)能够将数组或INI文件转换为对象的父/子组。
例如:
$config_array = array (
'somecategory' => array ( 'key' => 'somevalue' ),
'anothercategory' => array ( 'key' => 'anothervalue', 'key2' => 'anothervalue2' ),
);
$config = new Configuration_Array( $config_array );
echo $config->somecategory->key; // prints: 'somevalue'
我已经使用以下代码有效地做到了这一点:
class Configuration_Array extends Configuration
{
protected $_child_class = 'Configuration_Array';
protected $_objects = null;
protected $_config = null;
public function __construct( $config_array = null, $child_class = null ) {
if ( null !== $config_array ) {
$this->setConfig( $config_array );
}
if ( null !== $child_class ) {
$this->setChildClass( $child_class );
}
}
public function __get( $name ) {
$name = strtolower( $name );
if ( ! isset ( $this->_objects[ $name ] ) ) {
$this->_createObject( $name );
}
return $this->_objects[ $name ];
}
public function __isset( $name ) {
$name = strtolower( $name );
return ( ( isset ( $this->_objects[ $name ] ) ) or $this->_can_create_object( $name ) );
}
public function reset() {
$this->_objects = null;
}
public function toArray() {
if ( ! is_array ( $this->_config ) ) {
throw new Exception( 'No configuration has been set' );
}
return $this->_config;
}
public function setConfig( $config ) {
if ( null === $config ) {
return $this->reset();
}
if ( ! is_array ( $config ) ) {
throw new Exception( 'Configuration is not a valid array' );
}
$this->_config = $config;
}
public function loadConfig( $path ) {
if ( ! is_string ( $path ) ) {
throw new Exception( 'Configuration Path "' . $path . '" is not a valid string' );
}
if ( ! is_readable ( $path ) ) {
throw new Exception( 'Configuration file "' . $path . '" is not readable' );
}
$this->setConfig( include( $path ) );
}
public function setChildClass( $class_name ) {
if ( ! is_string ( $class_name ) ) {
throw new Exception( 'Configuration Child Class is not a valid string' );
}
if ( ! class_exists ( $class_name ) ) {
throw new Exception( 'Configuration Child Class does not exist' );
}
$this->_child_class = $class_name;
}
public function getChildClass() {
if ( ! isset ( $this->_child_class ) ) {
throw new Exception( 'Configuration Child Class has not been set' );
}
return $this->_child_class;
}
protected function _createObject( $name ) {
$name = strtolower( $name );
if ( ! isset ( $this->_config[ $name ] ) ) {
throw new Exception( 'No configuration has been set for object "' . $name . '"' );
}
$child_class = $this->getChildClass();
if ( is_array ( $this->_config[ $name ] ) ) {
$child = new $child_class( $this->_config[ $name ], $child_class );
} else {
$child = $this->_config[ $name ];
}
return ( $this->_objects[ $name ] = $child );
}
protected function _can_create_object( $name ) {
$name = strtolower( $name );
return isset ( $this->_config[ $name ] );
}
}
问题
大多数功能都能完美运行,但是我在弄清楚如何有效使用isset时遇到了一些麻烦。使用属性链,isset仅对链中的最后一个值起作用,例如:
if ( isset ( $config->somecategory->key ) ) {
哪个使用$ config-> somecategory返回的对象,并检查它是否包含名为'key'的对象
这意味着如果$ config-> somecategory不存在,则会引发异常。用户必须这样做才能有效检查:
if ( isset ( $config->somecategory ) and isset ( $config->somecategory->key ) ) {
但是这似乎很烦人。
另一方面,不需要在每个级别检查数组; PHP可以检查整个内容:
if ( isset ( $config[ 'somecategory' ][ 'key' ] ) ) { // No error/exception
[我正在寻找的是一种实现类的方法,因此我可以像对待数组一样对待对象:
if ( isset ( $config->somecategory->key ) ) {
如果'somecategory'不存在,以不会引发异常的方式...
想法?
也许是这样?
唯一的问题是,您必须调用isEmpty
来检查是否给出了配置,而必须调用get
来获得最终值。 (就像在底部的3个测试用例中一样)
<?php
// set debug
error_reporting(E_ALL ^ E_STRICT);
ini_set('display_errors', 'on');
class Config
{
protected $data;
public function __construct($data = null) {
$this->data = $data;
}
public function __get($name) {
return isset($this->data[$name]) ? new Config($this->data[$name]) : new Config();
}
public function isEmpty() {
return empty($this->data);
}
public function get() {
return $this->data;
}
}
$test = new Config(array(
'foo' => array(
'bar' => array(
'barfoo' => 1
)
)
));
// test 1
if (!$test->foo->bar->isEmpty()) {
print_r($test->foo->bar->get());
}
// test 2
if (!$test->foo->bar->foobar->isEmpty()) {
print_r($test->foo->bar->foobar->get());
}
// test 3
if (!$test->foo->bar->barfoo->isEmpty()) {
print_r($test->foo->bar->barfoo->get());
}
示例:
很遗憾,没有isset
版本可以正确检查您的财产链。即使编写自己的方法也无济于事,因为如果somecategory
未设置,则将链作为参数传递给方法已经失败。
您可以实现magic方法来访问未设置的属性(可能在配置对象通用的基类中)。这将创建类UnsetProperty
的虚拟对象并返回该对象。
您到$config->someCategory->key
的课程将为UnsetProperty
分配一个$config->someCategory
。该对象还将为UnsetProperty
传递一个新的$obj->key
。如果在IsSet()
中返回false并在其他属性中返回true的情况下实现方法UnsetProperty
,则可以简单地对以下内容进行检查:
if($config->someCategory->key->IsSet()) ...
这将需要做很多事情,所以我不确定您是否不喜欢使用链式isset-calls。
if((isset($config->someCategory)) and (isset($config->someCategory->key))) ...
取决于样式以及您具有多少个嵌套级别。
希望您能理解这种可能性。
看看Zend_Config。它的运行几乎与您描述的完全一样。
您可以直接或简单地将其用作构建自己的说明性指南。
自PHP 7起,可以为此目的使用a not well documented feature of null coalesce operator。
$config_array = [
'somecategory' => [ 'key' => 'somevalue' ],
'anothercategory' => [ 'key' => 'anothervalue', 'key2' => 'anothervalue2' ],
];
// Quickly convert to object
$json = json_encode($config_array);
$config = json_decode($json);
echo $config->somecategory->key ?? null; // prints: 'somevalue'
echo $config->somecategory->missing_key ?? null; // no errors but also doesn't print anything
echo $config->somecategory->missing_key->go->crazy->with->chains ?? null; // no errors but also doesn't print anything
这里是一个正在运行的online example