代码之家  ›  专栏  ›  技术社区  ›  reformed Ezequiel García

如何在PHP中将父类强制转换为子类?

  •  -2
  • reformed Ezequiel García  · 技术社区  · 6 年前

    是否可以在PHP中将父类强制转换为子类?如果没有,什么是最有效的替代方法来实现这一点?

    例如,以下操作会导致错误:

    <?php
    class Foo {
        public $test;
        public static function create(): self {
            $a = new self();
            $a->test = 123;
            return $a;
        }
    }
    
    class Bar extends Foo {
        public $baz;
        public static function create(): self {
            $b = (self) parent::create(); // <--- Is this possible?
            $b->baz = 456;
            return $b;
        }
    }
    
    $bar = Bar::create();
    
    var_dump($bar); /* should output:
      object(Bar)[35]
        public 'baz' => int 456
        public 'test' => int 123 */
    
    3 回复  |  直到 6 年前
        1
  •  1
  •   Marcus Harrison    6 年前

    当你宣布 Bar 延伸 Foo ,默认值 FOO公司 构造函数调用 酒吧 构造函数以初始化其父属性(在本例中, public $test )。如果为子类定义自定义构造函数,则还应调用父构造函数。 This section of the PHP documentation describes how this works 以下内容:

    <?php
    class BaseClass {
        function __construct() {
            print "In BaseClass constructor\n";
        }
    }
    
    class SubClass extends BaseClass {
        function __construct() {
            parent::__construct();
            print "In SubClass constructor\n";
        }
    }
    
    class OtherSubClass extends BaseClass {
        // inherits BaseClass's constructor
    }
    
    // In BaseClass constructor
    $obj = new BaseClass();
    
    // In BaseClass constructor
    // In SubClass constructor
    $obj = new SubClass();
    
    // In BaseClass constructor
    $obj = new OtherSubClass();
    

    对于您示例中的特定功能,我希望将代码的逻辑放入受保护的构造函数中,并从工厂方法调用它们:

    <?php
    class Foo {
        public $test;
    
        protected function __construct() {
            $this->test = 123;
        }
    
        public static function create(): Foo {
            return new self();
        }
    }
    
    class Bar extends Foo {
        public $baz;
    
        protected function __construct() {
            parent::__construct();
            $this->baz = 456;
        }
    
        public static function create(): Foo {
            return new self();
        }
    }
    

    注意,我已经更改了函数的返回类型 create() 永远 FOO公司 .PHP不允许您更改从父类重写的函数的返回类型,并使用 self 在这两种情况下都会这样做。

    不过,这不是问题。 Bar::create() 给我们一个 酒吧 按预期设置的对象。返回类型必须与 FOO公司 从那以后 酒吧 工具 FOO公司 的接口,它被认为是类型兼容的。

    您可以使用 static 关键字:

    <?php
    class Foo {
        public $test;
    
        protected function __construct() {
            $this->test = 123;
        }
    
        public static function create(): Foo {
            return new static();
        }
    }
    
    class Bar extends Foo {
        public $baz;
    
        protected function __construct() {
            parent::__construct();
            $this->baz = 456;
        }
    }
    
    var_dump(Bar::create()); /* Outputs:
        object(Bar)#1 (2) {
        ["baz"]=>
        int(456)
        ["test"]=>
        int(123)
        }
    */
    

    可能使用受保护的构造函数不是一个选项,例如,因为已经有了一个带有必需参数或类似情况的公共构造函数。在这种情况下,您必须使用其他答案中的解释,创建一个临时的 FOO公司 并将属性值复制到新的 酒吧 对象:

    <?php
    class Foo {
        public $test;
    
        public static function create(): Foo {
            $result = new self();
            $result->test = 123;
            return $result;
        }
    }
    
    class Bar extends Foo {
        public $baz;
    
        public static function create(): Foo {
            $foo = parent::create();
            $result = new self();
            foreach($foo as $property => $value) {
                $result->$property = $value;
            }
            $result->baz = 456;
            return $result;
        }
    }
    
    var_dump(Bar::create()); /* Outputs:
        object(Bar)#2 (2) {
        ["baz"]=>
        int(456)
        ["test"]=>
        int(123)
        }
    */
    
        2
  •  0
  •   Barmar    6 年前

    我不认为有什么内在的东西能做到这一点。您可以在子类中编写一个函数,该函数接受父类的一个实例,并返回具有相同内容的子类。

    class Bar extends Foo {
        public static function FooFromBar($foo) {
            $bar = new Bar();
            $bar->prop1 = $foo->prop1;
            $bar->prop2 = $foo->prop2;
            // continue for all properties
            return $bar;
        }
    }
    
        3
  •  0
  •   reformed Ezequiel García    6 年前

    我可以使用以下策略来实现这一点:

    class Foo {
        public $test;
        public static function create(array $row): self {
            $a = new static();
            $a->test = $row['Test'];
            return $a;
        }
    }
    
    class Bar extends Foo {
        public $baz;
        public static function create(array $row): Foo {
            $b = parent::create($row);
            $b->baz = $row['Baz'];
            return $b;
        }
    }
    
    $test = Bar::create([
        'Test' => 123,
        'Baz' => 456,
    ]);
    
    var_dump($test);
    /* Outputs:
        object(Bar)#2 (2) {
            ["baz"]=> int(456)
            ["test"]=> int(123)
        } */