【问题标题】:PHP array with default value for nonexisting indices具有不存在索引的默认值的 PHP 数组
【发布时间】:2010-11-30 11:18:56
【问题描述】:

我喜欢 Ruby 的 Hash 实现,您可以在其中使用默认值初始化 Hash 对象。目前我正在努力在 PHP 中实现一个类似的对象。这是我的第一个(非工作)镜头。

class DefaultArray extends ArrayObject {

  protected $_defaultValue;

  public function setDefault($defaultValue) {
    $this->_defaultValue  = $defaultValue;
  }

  public function offsetExists($index) {
    return true;
  }

  public function offsetGet($index) {
    if(!parent::offsetExists($index)) {
      if(is_object($this->_defaultValue))
        $default = clone $this->_defaultValue;
      else 
        $default = $this->_defaultValue;

      parent::offsetSet($index, $default);
    }
    return parent::offsetGet($index);
  }
}

$da = new DefaultArray();
assert($da["dummy"] == null);
$da->setDefault = 1;
assert($da["dummy2"] == 1);

第二个断言将失败。单步执行代码显示调用了 offsetGet 并执行了 if 子句。然而,任何数组值都是空的。任何替代实现的想法?

写累了

if(!isset($myarr['value']))
    $myarr['value'] = new MyObj();
$myarr['value']->myVal=5;

而不仅仅是写

$myarr['value']->myVal=5;

【问题讨论】:

  • 当访问一个不存在的键时,offsetGet 会将该键设置为默认值(参见offsetSet)。这意味着下一次它会存在。如果您不希望这种行为并且始终获取不存在键的当前默认值,请删除 offsetSet 调用。
  • 感谢 Gumbo 的重要澄清,这种行为在我的第一次测试中让我感到困惑,但这是预期的行为。现在已经回答了问题,我将保留错误的测试代码。

标签: php arrays hash


【解决方案1】:
$da->setDefault(1);

您还可以使用 __construct 魔术函数:

class DefaultArray extends ArrayObject
{
    public function __construct($value = null){
        if(is_null($value))
        {
            $this->value = 'default';
        } else {
            $this->value = $value;
        }
    }
}

【讨论】:

  • 谢谢,您发现了我的错误!它是 $da->setDefault(1) 而不是 $da->setDefault = 1!哦,尴尬!通过此修改,我的示例按预期工作。我也想过在构造函数中传递默认值,但想保留 ArrayObject 的原始构造函数参数。
  • 你可以使用我的小库 ValueResolver 来使它更紧凑。
【解决方案2】:

试试magic methods __get

class DefaultArray extends ArrayObject {
    protected $_defaultValue;

    public function setDefault($defaultValue) {
        $this->_defaultValue  = $defaultValue;
    }

    public function __get($index) {
        return $this->offsetGet($index);
    }

    public function offsetGet($index) {
        if(!parent::offsetExists($index)) {
            if (is_object($this->_defaultValue)) {
                $default = clone $this->_defaultValue;
            } else {
                $default = $this->_defaultValue;
            }
            parent::offsetSet($index, $default);
        }
        return parent::offsetGet($index);
    }
}

现在您只需要使用不同的键,因为读取访问将初始化该数组项:

$da = new DefaultArray();
assert($da['foo'] == null);
$da->setDefault(1);
assert($da['bar'] == 1);

【讨论】:

    【解决方案3】:

    在这种情况下,您可以使用我的小型库 ValueResolver,例如:

    class DefaultArray extends ArrayObject
    {
        public function __construct($value = null){
            $this->value = ValueResolver::resolve($value, 'default'); // returns 'default' if $value is null
        }
    }
    

    别忘了使用命名空间use LapaLabs\ValueResolver\Resolver\ValueResolver;

    还有类型转换的能力,例如如果你的变量的值应该是integer,那么使用这个:

    $id = ValueResolver::toInteger('6 apples', 1); // returns 6
    $id = ValueResolver::toInteger('There are no apples', 1); // returns 1 (used default value)
    

    查看docs 了解更多示例

    【讨论】:

      【解决方案4】:

      为什么这么复杂?

      function initVal($value) {
          global $myarr;
          if(!isset($myarr['value']))
              $myarr['value'] = new MyObj();
      }
      

      现在你只需要打电话:

      initVal('bla');
      $myarr['bla']->bla = 'bla';
      

      但我明白了,你的要整洁得多。

      【讨论】:

      • 使用 ArrayObject 类将所有变量和支持方法保存在一个位置。而使用 global 函数更有可能导致将来出现维护问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-17
      • 1970-01-01
      • 2022-01-07
      • 2011-05-23
      • 2020-06-14
      • 1970-01-01
      相关资源
      最近更新 更多