关于错误“尝试修改非对象的属性”

时间:2013-05-03 17:49:03

标签: php

有谁可以告诉我为什么以下代码会有不同的结果?

取消设置对象属性

$s = new StdClass;
unset($s->a->b);    //it is working fine
unset($s->x->y->z); //it is got an error: Attempt to modify property of non-object

取消设置数组索引

$a = array();
unset($a[1][2]);    //it is working fine
unset($a[3][4][5]); //it is working fine

1 个答案:

答案 0 :(得分:20)

StdClass vs Array

阵列

PHP中的数组允许从基础隐式创建多维数组。即:

$a[] = "something"; //new array created, indexed at 0
$a[] = "yep"; //adds element to end of array (array_push);

以上var_dump给出:

array(2) {
  [0]=>
  string(9) "something"
  [1]=>
  string(3) "yep"
}

在PHP 5.4.x中,没有给出任何通知。你也可以这样做:

$a[] = "something";
$a[1][2][3] = "yep";

隐式创建所有级别的数组,给出var_dump

array(2) {
  [0]=>
  string(9) "something"
  [1]=>
  array(1) {
    [2]=>
    array(1) {
      [3]=>
      string(3) "yep"
    }
  }
}

PHP旨在轻松处理这种类型的数组创建。有关如何处理数组的更多信息,请阅读Array Type文档。因此,如果访问数组中的键以进行值赋值,如果它尚不存在,则会隐式创建它。与unset一起使用时,不会抛出任何错误,因为它可以检查最终数组并看到它不存在。

一个StdClass

因为这是object,所以必须明确创建属性,而不是投射到它的内容。例如:

$s = new StdClass;
$s->a = new stdClass;
$s->a->b = new stdClass;
$s->a->b->c = "test";

将屈服:

object(stdClass)#1 (1) {
  ["a"]=>
  object(stdClass)#2 (1) {
    ["b"]=>
    object(stdClass)#3 (1) {
      ["c"]=>
      string(4) "test"
    }
  }
}

当我们创建达到该点所需的object时。但是,如果您尝试使用:

$s = new StdClass;
$s->a->b->c = "test";

您收到错误:

  

警告:从第x行的file.php中的空值创建默认对象

但是,然后创建对象,给出var_dump

object(stdClass)#1 (1) {
  ["a"]=>
  object(stdClass)#2 (1) {
    ["b"]=>
    object(stdClass)#3 (1) {
      ["c"]=>
      string(4) "test"
    }
  }
}

当你通过值a并创建一个默认对象,然后b,然后为对象属性b->c赋值“测试”。当转到unset对象中的值时,它不会将所有属性转换为默认对象。例如,

$s = new StdClass;
unset($s->a->b);

不会给您一个错误,因为a可以是$s的属性,并且会对对象执行默认类型转换,然后取消设置b。但是,它不会在列表中继续下去。

unset($s->a->b->c);

假设ba中的已创建对象,但事实并非如此,因此您尝试使用b->c来访问非对象的属性。一个更明确的例子:

$s = new stdclass;
$s->a->b->c = array();
unset($s->a->b->c->d);

unset此处不会抛出任何错误,因为它会将c输入到对象,然后unset d。尽管如此,您确实会收到警告:它会一直向c投射到stdClass。这表明,最后一个元素/类将通过类型转换为对象来访问属性(如果它不是已经存在的属性),但是在使用{{{}时它不会对所有元素进行类型转换以访问最后一个属性1}}。

结论

总之,区别在于PHP如何处理数组以及它如何处理对象。对象具有更严格的标准,并且不会隐式地类型转换为多个级别,因为它们被访问为unset,而数组则是。{1}}。对于对象,您可以隐式地将它们创建为所需的级别(像您一样获得警告),但是当取消设置时,不会发生类型转换,并且您正在访问不存在的属性。