print_r() 向 DateTime 对象添加属性 [重复]

Posted

技术标签:

【中文标题】print_r() 向 DateTime 对象添加属性 [重复]【英文标题】:print_r() adds properties to DateTime objects [duplicate] 【发布时间】:2013-06-08 14:19:41 【问题描述】:

考虑以下代码示例:

$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() 将这些属性“添加”到对象中?请注意,它们未定义为 manual page 上的 DateTime 类的一部分。

【问题讨论】:

@Jessica 同时运行这两个代码块。为第二个而不是第一个生成警告。唯一的区别是 print_r 在第二个中被注释掉了 多哈。我当时想,这似乎很明显。 :-P 我明白你的意思,我运行了代码 - 不知道。 我的猜测是,这是某种内部私有/受保护的属性,在转储时被放入对象/公开。 这不是错误。阅读手册,您会发现没有公共属性“日期”。 @vascowhite 不过,这不是问题所在;这就是为什么它出现在print_r()之后;授予,它可能首先是由于一个错误:) 【参考方案1】:

DateTime 中没有 date 属性;这就是为什么你会收到(Undefined property: DateTime::$date).

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

你需要像$m_oDate->format('m-d-Y'); 这样的东西。

【讨论】:

为什么投反对票?这是绝对正确的。虽然它没有解释为什么在包含 print_r() 时不会看到错误 @DaveRandom 它没有回答问题。 print_r 打印对象,echo 需要一个字符串才有意义。 @DaveRandom:不是我的 dv,但即使它是真的,它也完全没有抓住问题的重点,所以当被判断为答案时它是非常糟糕的(最近的编辑证实了这一点)。我们不会判断答案的技术价值。 @Jon 我知道你来自哪里,但在某种程度上它绝对回答了这个问题:DateTime 的接口在 PHP 手册中定义,因此在主题 不包括->date 属性。所以问题实际上是“为什么我在尝试访问不属于接口的属性时会出错”,而用这种方式表达的答案是“因为它不是接口的一部分”。我确实理解它并不能完全回答所问问题的观点,但我会反驳 OP 提出了错误的问题 ;-)【参考方案2】:

这在 PHP 中是 reported as Bug #49382。

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

使用reflection访问这些属性也可以达到同样的效果,如果你需要访问这些属性,那么使用反射就可以了,所以你不会引发错误。

但是,应该注意的是,你不应该真正使用这些属性——因为它们没有被定义为对象的成员,所以不能保证它们在未来的 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 持有的字符串类型——即上面代码示例中获取时区信息的三种方法之一。为了完整起见,它的可能值为defined,方式如下:

价值 |类型 |用户级等价物 ------+------------------------+-------- --------------- 1 |时间偏移 | DateTimeZone::getOffset() 2 |时区缩写 | DateTimeZone::listAbbreviations() 3 |时区标识符 | DateTimeZone::getName()

【讨论】:

这不是错误 - 这是一项功能! 这不是错误。阅读manual,您会看到没有公共属性“日期”。 这是一个错误。 @Danack 在他的回答中解释了这一点。 @vascowhite:你还没有理解这个问题。 现在您可以将其用作功能:$x = new DateTime(); ob_start(); print_r($x); ob_end_clean(); echo $x->date;【参考方案3】:

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

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

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

【讨论】:

谢谢。这是最能回答我的问题的回答(就我的 - 原始 - 象征性问题而言)【参考方案4】:

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

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

Source

【讨论】:

【参考方案5】:

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

$m_oDate = new DateTime('NOW');
echo $m_oDate->format("r");

【讨论】:

【参考方案6】:

问题lies here:

static HashTable *date_object_get_properties(zval *object TSRMLS_DC)

    // ...
    zend_hash_update(props, "date", 5, &zv, sizeof(zval), NULL);
    // ...

当进行任何数据转储(print_rvar_dumpvar_export)时,将调用函数date_object_get_properties。哈希表更新了数据表示,不幸的是,这是公开的。

【讨论】:

【参考方案7】:

考虑以下代码示例:

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

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

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

这个名字是 crazy,它肯定不是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 已经有了关联数组集合的概念,而新的 critter 只不过是一种访问它们的简洁的新方法。然而,随着时间的推移,事实证明,这种新语法对 PHP 的影响比最初预期的要深远得多。

-- Zeev Suraski about the standard object 自 PHP 3 (archived copy) - 通过 Why return object instead of array?

这也是为什么在 PHP 中完全常见函数可以添加之前在类中未定义的成员变量的简单解释。这些总是公开的。

因此,当您对某些 对象 是否具有属性进行假设时,请查看它的来源。它不能是类的一部分,但可能是以后添加的。

请记住以下几点:

不要在生产代码中使用print_rvar_dump

【讨论】:

而且它还表明使用 PHP 中的 any 对象完全可以轻松实现。 不是这样:codepad.viper-7.com/vwm2qp @PLB: codepad.viper-7.com/vwm2qp - 不太好。那个网站挂了。但肯定我的那句话有点拗口。 我的意思是定义 __set 方法和自定义实现的对象。例如:3v4l.org/7EUhl @PLB:您的示例不完整;)。即使对于这些对象, 也可以设置变量属性。请参阅此代码:3v4l.org/itpSn。 不,它并不完整。该示例背后的士气是对象可能具有这样的 __get 和/或 __set 实现,无法像那样添加公共属性,这意味着您所说的对于 any 对象是正确的,但是这些。这类实现确实很少见,但它们仍然存在。

以上是关于print_r() 向 DateTime 对象添加属性 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

java日期类型对象通过mybatis向数据库中的存取

向文本块 WPF 添加倒数计时器

RLMException,对象类型需要迁移

将TIME添加到DATETIME值

将 varchar 转换为 datetime 并添加秒

pandas使用to_datetime函数将时间字符串转化为时间对象使用dt.tz_localize为转化后的时间对象添加时区信息(timezone)