19

考虑以下代码示例:

$m_oDate = new DateTime('2013-06-12 15:54:25');
print_r($m_oDate);
echo $m_oDate->date;

从 PHP 5.3 开始,这会产生(类似于)以下输出:

DateTime Object
(
    [date] => 2013-06-12 15:54:25
    [timezone_type] => 3
    [timezone] => Europe/Amsterdam
)
2013-06-12 15:54:25

但是下面的代码:

$m_oDate = new DateTime('2013-06-12 15:54:25');
echo $m_oDate->date;

...只是发出一个错误:

Notice: Undefined property: DateTime::$date in ...

为什么要将print_r()这些属性“添加”到对象中?请注意,它们没有被定义为手册页DateTime上类的一部分。

4

7 回答 7

14

这已在 PHP 中报告为 Bug #49382

在 PHP 5.3 中,添加了内部功能以允许print_r()显示 的实例所持有的底层时间戳值的详细信息DateTime,以帮助调试。此更改的副作用是,当对象转储为文本时,这些幻像公共属性将添加到实例中。

使用反射访问这些属性可以达到相同的效果,如果您需要访问这些属性,那么使用反射将是可行的方法,因此您不会引发错误。

然而,应该注意的是,你不应该真正使用这些属性——因为它们没有被定义为对象的成员,所以不能保证它们在未来的 PHP 版本中会继续携带相同的数据(甚至存在)。如果您需要访问信息,请改用以下方法(定义为 API 的一部分):

// $obj->date
$obj->format('Y-m-d H:i:s');

// $obj->timezone
$obj->getTimezone()->getName();
// or...
$obj->getTimezone()->getOffset();
// or...
$obj->getTimezone()->listAbbreviations(); // returns an array, so may need 
                                          // further processing to be of use

注意:timezone_type无法通过 PHP API 访问该属性。它是一个内部值,在用户空间中没有用,因为它描述了在timezone转储对象时保存的字符串类型——即上面代码示例中获取时区信息的三种方法之一。为了完整起见,它的可能值按以下方式定义:

价值 | 类型 | 用户级等价物
------+------------------------+-------- ---------------
  1 | 时间偏移 | DateTimeZone::getOffset() 
  2 | 时区缩写 | DateTimeZone::listAbbreviations() 
  3 | 时区标识符 | DateTimeZone::getName()
于 2013-06-12T13:01:42.763 回答
9

中没有date财产DateTime;这就是为什么你得到(Undefined property: DateTime::$date).

print_r()对对象进行一些自省以显示其内容;这会导致对象神奇地创建::date属性。虽然这没有记录,所以将来使用它可能会破坏您的代码。

你需要类似的东西$m_oDate->format('m-d-Y');

于 2013-06-12T12:58:33.817 回答
9

发生了一些魔术,但这很简单。

DateTime类没有您要访问的公共变量“日期”。但是,作为 PHP 工作方式的副作用,当您在该类上调用 print_r 或 var_dump 时会创建一个变量。

魔术发生后,“日期”可用,但不应该。您应该只使用 getTimestamp 函数来使您的代码可靠地工作。

于 2013-06-12T13:03:44.310 回答
5

问题出在这里

static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
{
    // ...
    zend_hash_update(props, "date", 5, &zv, sizeof(zval), NULL);
    // ...

date_object_get_properties当进行任何数据转储时调用该函数( print_r, var_dump, var_export)。哈希表为数据表示更新,不幸的是这是公开的。

于 2013-06-12T13:18:55.143 回答
2

考虑以下代码示例:

$m_oDate = new DateTime('2013-06-12 15:54:25');
some_func($m_oDate);
echo $m_oDate->{'ROXXOR_IS_BACK!!'};

与您最明显print_r的区别是调用不同的函数而不是函数some_func。期望可能会有所不同,因为您知道print_r但您不知道some_func,但这只是为了提高您的感官。让我们等一下,直到我显示该函数的定义。

第二个区别是正在回显的属性的名称。在这里我选择了一个非常特别的名字:{'ROXXOR_IS_BACK!!'},再次让你的感官更加敏锐。

这个名字太疯狂DateTime了,尽管在上面的例子执行时,它显然不是它的一部分,但很明显这个 roxxor 属性必须存在。程序输出:

PHP never lets you down.

那怎么来?是的,您肯定已经知道它是如何工作的。该some_func()功能必须添加它。那么让我们看一下函数定义:

function some_func($m_oDate) {
    $m_oDate->{'ROXXOR_IS_BACK!!'} = 'PHP never lets you down.';
}

是的,现在可以清楚地看到这个函数已经向对象添加了一个属性。它还表明,使用 PHP 中的任何对象都可以轻松实现这一点。

与 PHP 中的数组相比,您还可以根据需要添加新键。

这个例子不是无中生有的,因为这是 PHP 中的对象的来源:它们只是数组周围的语法糖,这与 PHP 3 中引入 PHP 中的对象的时间有关:

在 PHP 3.0 的源代码树中引入类时,它们被添加为访问集合的语法糖。PHP 已经有了关联数组集合的概念,而新的小动物只不过是一种访问它们的简洁的新方法。然而,随着时间的推移,事实证明,这种新语法对 PHP 的影响比最初预期的要深远得多。

-- Zeev Suraski about the standard object since PHP 3 (archived copy) - via Why return object instead of array?

This is also the simple explanation why it's totally common in PHP that functions can add member variables which have not been defined earlier in the class. Those are always public.

So when you do assumptions about some object having a property or not, take a look where it comes from. It must not be part of the class but might have been added later.

And keep the following in mind:

Do not use print_r and var_dump in production code.

于 2013-07-09T11:05:53.130 回答
1

为了它的乐趣,这就是你如何使它工作,使用Reflection

$m_oDate = new DateTime('NOW');
$o = new ReflectionObject($m_oDate);
$p = $o->getProperty('date');
echo $p->getValue($m_oDate);

来源

于 2013-06-12T13:06:15.367 回答
0

您应该使用DateTime::formatDateTimeImmutable::format视情况而定

$m_oDate = new DateTime('NOW');
echo $m_oDate->format("r");
于 2013-06-12T13:12:18.507 回答