Сделать DateTime:: createFromFormat() вернуть дочерний класс вместо родительского

Я расширяю DateTime, добавляю полезные методы и константы.

При использовании new для создания нового объекта все в порядке, но при использовании статического метода createFromFormat он всегда возвращает исходный объект DateTime, и, конечно, ни один из дочерних методов не доступен.

Я использую следующий код, чтобы обойти эту проблему. Это лучший подход?

namespace NoiseLabs\DateTime;

class DateTime extends \DateTime
{
    static public function createFromFormat($format, $time)
    {
        $ext_dt = new self();

        $ext_dt->setTimestamp(parent::createFromFormat($format, time)->getTimestamp());

        return $ext_dt;
    }
}

Ответ 1

Это путь. Однако, поскольку вы хотите сделать класс DateTime расширяемым, я бы предложил использовать static вместо self:

namespace NoiseLabs\DateTime;

class DateTime extends \DateTime
{
    static public function createFromFormat($format, $time)
    {
        $ext_dt = new static();
        $parent_dt = parent::createFromFormat($format, $time);

        if (!$parent_dt) {
            return false;
        }

        $ext_dt->setTimestamp($parent_dt->getTimestamp());
        return $ext_dt;
    }
}

Это не обязательно, если вы не планируете расширять класс, но если кто-то это сделает, это не позволит ему снова сделать то же обходное решение.

Ответ 2

Я думаю, ваше решение в порядке. Альтернативный способ (просто реорганизованный бит) заключается в следующем:

public static function fromDateTime(DateTime $foo)
{
  return new static($foo->format('Y-m-d H:i:s e')); 
}

public static function createFromFormat($f, $t, $tz)
{
  return static::fromDateTime(parent::createFromFormat($f, $t, $tz));
}

Я не уверен, что лучший способ реализовать fromDateTime. Вы даже можете взять то, что у вас есть, и поместить его туда. Просто не теряйте часовой пояс.

Обратите внимание, что вы даже можете реализовать __callStatic и использовать немного отражения, чтобы сделать это будущим доказательством.

Ответ 3

class DateTimeEx extends DateTime
{
    static public function createFromFormat($format, $time, $object = null)
    {
        return new static(DateTime::createFromFormat($format, $time, $object)->format(DateTime::ATOM));
    }
}

Ответ 4

Предыдущие решения пренебрегают часовыми поясами и микросекундами, поэтому мое небольшое улучшение уже здесь. Я предпочитаю вариант 1, но с точки зрения производительности 2 может быть немного быстрее на старых PHP.

class NDateTimeImmutable extends \DateTimeImmutable
{
    public static function createFromFormat1($format, $time)
    {
        $parent = parent::createFromFormat($format, $time);
        if (!$parent) {
            return false;
        }
        //Seting timezone like this and not by format to preserve timezone format
        $static = new static($parent->format('Y-m-d\TH:i:s.u'), $parent->getTimezone());
        return $static;
    }

    public static function createFromFormat2($format, $time)
    {
        $parent = parent::createFromFormat($format, $time);
         if (!$parent) {
            return false;
        }
        $serialized = serialize($parent);
        // numbers can be computed by strlen() 
        // eg. strlen(parent::class) = 17 but it is slow
        $serialized = strtr($serialized, ['17:"'.parent::class => '18:"'.static::class]);
        return unserialize($serialized);
    }
}