配置对象中的属性链和 isset

发布于 2024-11-06 01:51:05 字数 4163 浏览 1 评论 0原文

我找不到与我的问题非常相似的问题,但如果您能找到一个问题,请随时告诉我..

我正在尝试找出如何有效地创建一个简洁的配置对象。

我希望对象(或某种配置管理器)能够将数组或 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

若有似无的小暗淡 2024-11-13 01:51:06

从 PHP 7 开始,可以使用

$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

这是一个在线示例

Since PHP 7 it's possible to use a not well documented feature of null coalesce operator for this purpose.

$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

Here is an online example in action

夏尔 2024-11-13 01:51:06

不幸的是,没有 isset 版本可以正确检查您的财产链。即使编写您自己的方法也无济于事,因为如果 somecategory 已未设置,则将链作为参数传递给您的方法已经失败。


您可以实现用于访问未设置属性的魔术方法(可能在配置对象通用的基类中)。这将创建一个 UnsetProperty 类的虚拟对象并返回该对象。

您的 $config->someCategory->key 类将为 $config->someCategory 传递一个 UnsetProperty。该对象还将为 $obj->key 传递一个新的 UnsetProperty。如果您在 UnsetProperty 中实现方法 IsSet() 返回 false,而在其他属性中返回 true,您可以简单地检查:

if($config->someCategory->key->IsSet()) ...

这将需要很多工作,所以我不确定您是否不喜欢使用链接的 isset 调用。

if((isset($config->someCategory)) and (isset($config->someCategory->key))) ...

取决于样式和您有多少嵌套级别。

希望您能理解这种可能性背后的想法。

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 if somecategory 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 a UnsetProperty for $config->someCategory. This object will also delivery a new UnsetProperty for $obj->key. If you implement a method IsSet() in UnsetProperty returning false and in other properties returning true you can simplyfy your check to:

if($config->someCategory->key->IsSet()) ...

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.

if((isset($config->someCategory)) and (isset($config->someCategory->key))) ...

Depends on style and how many nested levels you have.

Hope you get the idea behind the possibility.

风吹雨成花 2024-11-13 01:51:06

看一下 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.

乖不如嘢 2024-11-13 01:51:06

也许是这样的?

唯一的问题是,您必须调用 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());
}

示例:

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, and get to get the final value. (Like can be seen in the 3 test cases at the bottom)

<?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());
}

Example:

http://codepad.org/9EZ2Hqf8

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文