我找不到一个与我相似的问题,但如果你能找到一个让我随时告诉我的问题......
我正在试图找出如何有效地创建一个整洁的配置对象。
我希望对象(或某种类型的配置管理器)能够将数组或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'不存在,那么就不会抛出异常......
想法?
答案 0 :(得分:1)
从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的行动
答案 1 :(得分:0)
不幸的是,没有isset
的版本可以正确检查您的属性链。即使编写自己的方法也无济于事,因为如果somecategory
已经取消设置,则将链作为参数传递给您的方法已经失败。
您可以实现访问未设置属性的魔术方法(可能在您的配置对象通用的基类中)。这将创建一个类UnsetProperty
的虚拟对象并返回该对象。
$config->someCategory->key
的课程将为UnsetProperty
提供$config->someCategory
。此对象还会为UnsetProperty
提供新的$obj->key
。如果您在IsSet()
中实现方法UnsetProperty
返回false,而在其他属性中返回true,则只需将您的支票设置为:
if($config->someCategory->key->IsSet()) ...
这需要做很多事情我不确定你是否不喜欢使用链接的isset-calls。
if((isset($config->someCategory)) and (isset($config->someCategory->key))) ...
取决于样式和您拥有的嵌套级别数。
希望你明白这种可能性背后的想法。
答案 2 :(得分:0)
也许是这样的?
唯一的问题是,您必须调用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());
}
示例:
答案 3 :(得分:0)
看看Zend_Config。它几乎与您描述的完全一致。
您可以直接使用它,也可以仅仅作为教学指南来构建自己的。