【问题标题】:Return child class from parent's static method从父类的静态方法返回子类
【发布时间】:2013-09-11 16:02:38
【问题描述】:

我刚刚了解了 PHP 5.4 的这个奇特的新特性。 JsonSerializable!这非常适合我的应用。

我的应用使用 DateTime 对象,当我 json_encode 它们时,我得到以下信息(通过运行 json_encode([new DateTime])):

[{"date":"2013-09-11 15:39:22","timezone_type":3,"timezone":"UTC"}]

根据timezone_type 是什么,timezone 的值可能会有所不同。我还没有找到在 JavaScript 中解析这个对象的好方法。

因此,我决定创建自己的 DateTime 类,并按照我的意愿将其序列化为 JSON。

class SerialDateTime extends DateTime implements JsonSerializable{

    public function jsonSerialize(){
        return ['timestamp' => $this->getTimestamp()];
    }
}

当我现在运行 json_encode([new SerialDateTime]) 时,我得到了这个:

[{"timestamp":1378914190}]

这在 JavaScript 中更容易解析。

所以,我认为这是一个很好的解决方案,但我发现了一个问题。静态方法! SerialDateTime::createFromFormat 返回一个 DateTime 对象!

如果我这样做:json_encode([SerialDateTime::createFromFormat('m/d/Y', '10/31/2011')]),我会得到:

[{"date":"2011-10-31 15:46:07","timezone_type":3,"timezone":"UTC"}]

为什么会这样?为什么SerialDateTime::createFromFormat 不给我返回SerialDateTime 对象?!

我该如何解决这个问题,或者我是否需要在SerialDateTime 中覆盖所有来自DateTime 的静态方法?如果我这样做,我什至如何从 createFromFormat 方法中创建一个新的 SerialDateTime ?如何将DateTime 对象“转换”为SerialDateTime

我想到了一个解决方法,但必须有更好的方法:

public static function createFromFormat($f, $t, $tz=NULL){
    $dateTime = call_user_func(
        array('SerialDateTime', 'parent::createFromFormat'),
        $f, $t, $tz
    );
    $ret = new self();
    return $ret->setTimestamp($dateTime->getTimestamp());
}

我可以使用__callStaticreturn call_user_func_array(array(__CLASS__ , 'parent::'.__FUNCTION__), func_get_args()); 什么的吗?

太糟糕了,我不能神奇地将DateTime 转换为使用late static bindings

【问题讨论】:

  • 如果你重写createFromFormat方法,这不是很好吗?以及为什么DateTime 会返回与子类有关的东西,而在 OOP 的原则中,父类必须对子类一无所知。你总是可以做new parent(),而不是像new child() IMO,覆盖方法很好,它被称为多态。
  • @Jari:我认为SerialDateTime::createFromFormat 会返回一个SerialDateTime 对象,而无需我手动覆盖它。
  • @RocketHazmat - 不,这不是它的工作方式。所有你没有重写的方法都将保持不变。

标签: php datetime static-methods


【解决方案1】:

就像您已经说过并尝试过的那样,覆盖静态方法。方法createFromFormat默认返回DateTime对象,所以你只需要修复返回部分,它会返回你的对象SerialDateTime而不是DateTime

class SerialDateTime extends DateTime implements JsonSerializable {

    public function jsonSerialize()
    {
        return ['timestamp' => $this->getTimestamp()];
    }

    public static function createFromFormat($format, $time, $timezone = null)
    {
        if ($timezone) {
            $dt = parent::createFromFormat($format, $time, $timezone);
        } else {
            $dt = parent::createFromFormat($format, $time);
        }

        return new self($dt->format(self::W3C));
    }

}

echo json_encode(new SerialDateTime);
echo json_encode(SerialDateTime::createFromFormat('Y', '2013'));

不管你怎么调用静态方法createFromFormat,它总会返回DateTime对象;所以你所有自动重写静态方法的想法都会失败,因为你需要用新的逻辑修改方法(返回其他对象的实例),而这不能用 auto-call-method-magic-什么的。

如果在 DateTime::createFromFormat 方法中实现后期静态绑定会很棒:

public static function createFromFormat($format, $time, $timezone = null)
{
    // logic of converting $time from format $format to some default format 
    return new static($newTime);
}

...但事实并非如此;(Source code

【讨论】:

    【解决方案2】:

    所以,我将在这里发布我的答案。

    在我看来,覆盖静态函数 createFromFormat 是解决您的问题的最佳方式。

    因为:

    • 您的代码将保持干净(没有任何不必要的call_user_func
    • 重写父类的方法,把类的逻辑保留在类里面就好了。
    • 您的课程SerialDateTime 将进一步可重用。 (如果你只想导入类代码)

    虽然没有必要重写所有方法(除非你实现了一个接口)。仅覆盖您需要的那些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多