配置对象中的属性链和 isset
我找不到与我的问题非常相似的问题,但如果您能找到一个问题,请随时告诉我..
我正在尝试找出如何有效地创建一个简洁的配置对象。
我希望对象(或某种配置管理器)能够将数组或 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' 不存在...
想法?
I couldn't find a question that was quite like mine, but if you can find one feel free to let me know..
I'm trying to figure out how to effectively create an neat configuration object.
I want the object (or a config manager of some sort) to be able to convert an array or INI file into a parent/child grouping of objects.
Eg:
$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'
I have effectively done this with the following code:
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 ] );
}
}
The Problem
Most of this works perfectly, but I am having some trouble figuring out how I can use isset effectively. With property chaining, isset only works on the last value in the chain, eg:
if ( isset ( $config->somecategory->key ) ) {
Which uses the object returned by $config->somecategory and checks whether it holds an object called 'key'
This means that if $config->somecategory doesn't exist, an exception is thrown. The user would have to do this to check effectively:
if ( isset ( $config->somecategory ) and isset ( $config->somecategory->key ) ) {
But that seems quite annoying.
An array on the other hand doesn't need to be checked at each level; PHP can check the entire thing:
if ( isset ( $config[ 'somecategory' ][ 'key' ] ) ) { // No error/exception
What I'm looking for is a way to implement my class so I can treat my objects sort of the same way I'd treat an array:
if ( isset ( $config->somecategory->key ) ) {
In a way that wouldn't throw an exception if 'somecategory' doesn't exist...
Ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
从 PHP 7 开始,可以使用
这是一个在线示例
Since PHP 7 it's possible to use a not well documented feature of null coalesce operator for this purpose.
Here is an online example in action
不幸的是,没有
isset
版本可以正确检查您的财产链。即使编写您自己的方法也无济于事,因为如果somecategory
已未设置,则将链作为参数传递给您的方法已经失败。您可以实现用于访问未设置属性的魔术方法(可能在配置对象通用的基类中)。这将创建一个
UnsetProperty
类的虚拟对象并返回该对象。您的
$config->someCategory->key
类将为$config->someCategory
传递一个UnsetProperty
。该对象还将为$obj->key
传递一个新的UnsetProperty
。如果您在UnsetProperty
中实现方法IsSet()
返回 false,而在其他属性中返回 true,您可以简单地检查:这将需要很多工作,所以我不确定您是否不喜欢使用链接的 isset 调用。
取决于样式和您有多少嵌套级别。
希望您能理解这种可能性背后的想法。
Unfortunately there is not version of
isset
which checks your property chain correctly. Even writing your own method will not help as passing the chain as parameter to your method already fails ifsomecategory
is already unset.You can implement the magic method for accessing unset properties (maybe in a base class common to your config objects). This will create a dummy object of class
UnsetProperty
and return that.Your class to
$config->someCategory->key
will deliver aUnsetProperty
for$config->someCategory
. This object will also delivery a newUnsetProperty
for$obj->key
. If you implement a methodIsSet()
inUnsetProperty
returning false and in other properties returning true you can simplyfy your check to:This will need a lot of to do so I am not sure if you do not like to go with the chained isset-calls.
Depends on style and how many nested levels you have.
Hope you get the idea behind the possibility.
看一下 Zend_Config。它的运作方式几乎与您所描述的完全一样。
您可以直接使用它,也可以简单地用作构建您自己的构建的指导指南。
Take a look at Zend_Config. It operates almost exactly as you describe.
You could use it directly or simply as an instructional guide to build your own.
也许是这样的?
唯一的问题是,您必须调用
isEmpty
来检查是否给出了配置,并调用get
来获取最终值。 (如底部的 3 个测试用例所示)示例:
http://codepad.org/9EZ2Hqf8
Maybe something like this?
The only problem is, you would have to call
isEmpty
to check if a configuration is given, andget
to get the final value. (Like can be seen in the 3 test cases at the bottom)Example:
http://codepad.org/9EZ2Hqf8