配置对象中的属性链接和isset

问题描述 投票:2回答:4

我找不到与我的问题相当的问题,但是如果您可以找到一个随时让我知道的问题。

我试图弄清楚如何有效地创建一个简洁的配置对象。

我希望对象(或某种类型的配置管理器)能够将数组或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'不存在,以不会引发异常的方式...

想法?

php arrays oop configuration chaining
4个回答
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());
}

示例:

http://codepad.org/9EZ2Hqf8


0
投票

很遗憾,没有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))) ...

取决于样式以及您具有多少个嵌套级别。

希望您能理解这种可能性。


0
投票

看看Zend_Config。它的运行几乎与您描述的完全一样。

您可以直接或简单地将其用作构建自己的说明性指南。


0
投票

自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

© www.soinside.com 2019 - 2024. All rights reserved.