让一个子级在PHP中扩展已初始化的父级

让一个子级在PHP中扩展已初始化的父级,php,inheritance,oop,Php,Inheritance,Oop,我一直很难想出解决这个问题的办法。我希望你们都能帮助我 最好用一个例子来描述: class Parent { public $nationality; function __construct($nationality) { $this->nationality = $nationality } } class Child extends Parent { function __construct() { echo

我一直很难想出解决这个问题的办法。我希望你们都能帮助我

最好用一个例子来描述:

class Parent {
    public $nationality;

    function __construct($nationality)
    {
        $this->nationality = $nationality
    }
}

class Child extends Parent {
    function __construct() {
        echo $this->nationality; // hispanic
    }
}

// Usage:
$parent = new Parent('hispanic');
$child = new Child();
我希望子对象从已初始化的父对象继承属性和方法


编辑:谢谢大家的回复-让我给你一些背景。我正在尝试制作一个模板系统。我有两个类——比如Tag.php和Form.php

我希望它看起来像这样:

class Tag {
    public $class_location;
    public $other_tag_specific_info;
    public $args;

    function __construct($args)
    {
        $this->args = $args;
    }

    public function wrap($wrapper) {
        ...
    }

    // More public methods Form can use.
}

class Form extends Tag {
    function __construct() {
        print_r($this->args()) // 0 => 'wahoo', 1 => 'ok'
        echo $this->class_location; // "/library/form/form.php"
        $this->wrap('form');
    }

    function __tostring() {
        return '<input type = "text" />';
    }
}

// Usage:
$tag = new Tag(array('wahoo', 'ok'));
$tag->class_location = "/library/form/form.php";
$tag->other_tag_specific_info = "...";
$form = new Form();
class标签{
公共$class_位置;
公共$other_标签_特定_信息;
公费$args;
函数构造($args)
{
$this->args=$args;
}
公共函数包装($wrapper){
...
}
//表单可以使用更多的公共方法。
}
类窗体扩展标记{
函数_u构造(){
打印($this->args())//0=>'wahoo',1=>'ok'
echo$this->class_location;//“/library/form/form.php”
$this->wrap('form');
}
函数u_tostring(){
返回“”;
}
}
//用法:
$tag=新标记(数组('wahoo','ok'));
$tag->class_location=“/library/form/form.php”;
$tag->other_tag_specific_info=“…”;
$form=新表单();
我不喜欢复合图案的原因是它不适合 我能感觉到为什么我要将标记的实例传递给构造函数吗 在它的一个子类中,Form毕竟是一种标记,对吗

谢谢!
马特·穆勒(Matt Mueller)

你想做的可以说是糟糕的设计。如果创建多个具有不同国籍的
父实例,然后创建一个新的
子实例,会发生什么情况。那么这个孩子是哪国人呢

为什么不让子类成为复合类,在构造函数中给它一个父类呢

class Child
{
    private $parent;

    function __construct($parent)
    {
        $this->parent = $parent;
    }

    function getNationality()
    {
        return $this->parent->nationality;
    }
}
然后用

$parent = new Parent('hispanic');
$child = new Child($parent);
或者使用父级的工厂方法。。。剩下的就看你的想象了



注意:我忽略了这样一个事实:我没有在父类上使用国籍的getter,也没有为Child提供超类(可能是父类?没有真正意义)。这些都是与设计相关的要点,但不在这个问题的范围内。

这不应该被认为是好的做法,我只是将函数作为概念证明来编写,看看是否可以完成

class MyClass
{
    public    $_initial    = NULL;

    public function setInitial($value) {
        $this->_initial = $value;
    }

}


class MyClass2 extends MyClass
{
    private    $_reversed = NULL;

    private function reverseInitial() {
        $this->_reversed = strrev($this->_initial);
    }

    public function getReversed() {
        $this->reverseInitial();
        return $this->_reversed;
    }

}


$class1 = new MyClass();
$class1->setInitial('rekaB kraM');

inherit($class1,'MyClass','MyClass2');

echo $class1->getReversed();

function inherit(&$object, $oldClassName, $newClassName) {
    $oldClass = new ReflectionClass($oldClassName);
    $newClass = new ReflectionClass($newClassName);

    $wrkSpace = serialize($object);
    $wrkTmp = explode('{',$wrkSpace);
    $startBlock = array_shift($wrkTmp);

    $oldClassProperties = $oldClass->getProperties();
    $oldClassPropertyNames = array();
    foreach($oldClassProperties as $oldClassProperty) {
        if (!$oldClassProperty->isStatic()) {
            if ($oldClassProperty->isPrivate()) {
                $oldClassPropertyNames[] = "\x0".$oldClassName."\x0".$oldClassProperty->getName();
            } elseif($oldClassProperty->isProtected()) {
                $oldClassPropertyNames[] = "\x0*\x0".$oldClassProperty->getName();
            } else {
                $oldClassPropertyNames[] = $oldClassProperty->getName();
            }
        }
    }

    $newClassProperties = $newClass->getProperties();
    $newClassPropertyValues = $newClass->getDefaultProperties();

    $newClassPropertyNames = array();
    foreach($newClassProperties as $newClassProperty) {
        $propertyName = $newClassProperty->getName();
        if (!$newClassProperty->isStatic()) {
            if ($newClassProperty->isPrivate()) {
                $newPropertyName = "\x0".$newClassName."\x0".$propertyName;
            } elseif($newClassProperty->isProtected()) {
                $newPropertyName = "\x0*\x0".$propertyName;
            } else {
                $newPropertyName = $propertyName;
            }
            $newClassPropertyNames[] = $newPropertyName;

            if (isset($newClassPropertyValues[$propertyName])) {
                $newClassPropertyValues[$newPropertyName] = $newClassPropertyValues[$propertyName];
            }
        }
    }


    $newClassPropertySet = array_diff($newClassPropertyNames,$oldClassPropertyNames);

    $toClassName = 'O:'.strlen($newClassName).':"'.$newClassName.'":'.(count($newClassPropertySet)+count($oldClassPropertyNames)).':';

    $newPropertyEntries = array_fill_keys($newClassPropertySet,NULL);
    foreach($newPropertyEntries as $newPropertyName => $newClassProperty) {
        if (isset($newClassPropertyValues[$newPropertyName])) {
            $newPropertyEntries[$newPropertyName] = $newClassPropertyValues[$newPropertyName];
        }
    }
    $newPropertyEntries = substr(serialize($newPropertyEntries),4+strlen(count($newClassPropertySet)),-1);

    $newSerialized = $toClassName.'{'.$newPropertyEntries.implode('{',$wrkTmp);

    $object = unserialize($newSerialized);
}

它不能完全满足你的要求。。。它接受父类的一个实例并将其更改为子类的一个实例,但可能会对其进行修改以创建一个新的子类,同时保持父类不变。

我们已经在这里编辑了很多内容,因此我将发布一个新的答案,希望没问题。为了它,我会把我以前的答案保留下来

无论如何,我们从您最新的示例(
formextends-Tag
)开始

似乎你在试图模仿某种形式,所以请容忍我,让我们用一个简短的例子:

class TagBuilder
{
    protected $args;

    protected $className;

    protected $classLocation;

    function __construct()
    {

    }

    public function setClass($file, $class)
    {
        /* @todo Use reflection maybe to determine if $class is a Tag subclass */

        if (file_exists($file))
        {
            require_once $file; // Or use the autoloader
        }

        $this->classLocation = $file;
        $this->className = $class;
    }

    /**
     * @return Tag the built tag (or tag subclass) instance
     */
    public function getTag()
    {
        $fullyQualifiedClassName = $this->getFullyQualifiedClassName();

        $newTag = new $fullyQualifiedClassName($this->args);
        $newTag->class_location = $this->classLocation;

        return $newTag;
    }

    protected function getFullyQualifiedClassName()
    {
        return $this->className;
    }

    public function setArgs($args)
    {
        $this->args = $args;
    }
}
此外,为了传递
$args
依赖项,您必须修改表单构造函数以调用它的
父::\u construct($args)
方法

然后运行它

$builder = new TagBuilder();
$builder->setClass('/library/form/form.php', 'Form');
$builder->setArgs(array('wahoo', 'ok'));

$form = $builder->getTag();
这是一个有点长的示例,它意味着更改OP的一些API,但如果我正确理解了这个问题,那么这就是OP希望在其应用程序中模仿的期望行为(或部分行为)



注意:我知道这不是构建器模式的经典示例,对于一些纯粹主义者来说,
getTag
方法可能有点模棱两可。

好吧,其他人告诉你这是一个糟糕的设计想法。我将提出类似的异议,并告诉您,您可能遇到了一个问题域,其中基于类的OO层次结构无法为您提供您想要的所有好处。如果你对这个想法感到好奇,我建议你阅读史蒂夫·耶格的《通用设计模式》(The Universal Design Pattern)()

但是他有点冗长,所以在JavaScript中,你所说的话可能是微不足道的:
t = new Tag({arg1: val1, arg2: val2})
// ...
// later, when you decide t is a Form
t.prototype = Form.prototype. 
使用JavaScript的大部分原因与基于原型的OO有关,但也有很多原因是函数是可以分配给变量并传递的一流值。PHP有一些用于动态调用函数的功能(5.3中用于分配/传递函数)。我想如果你有兴趣并且愿意走出基于课堂的语言设施,你可能会做很多类似的事情

首先,你要做很多人在没有类的语言中编写面向对象代码时所做的事情:你的“类”可以是散列/关联数组(AAs)

您的方法只能是以AA引用作为第一个参数的函数

function quasiClass_method1Name($this,$args = array()) {
   $this['prop1'] = $args[1] * $args[2]; 
}
如果需要独立于类的方法调用,可以将其与包装函数一起使用:

function method($this,$methodName,$args) {
   $fullMethodName = "{$this['_class']}_$methodName";
   $fullMethodName($this,$args);
}

// and now, the invocation
method($quasiObj,'method1Name',array(6,7));
在这个系统下,当您想将
$quasiObject
标签
升级到
表单
时,只需更改数组的
\u class
键即可。如果需要进行内部状态更改,您可以编写一个函数/方法来处理


你确实失去了为你记录事情的口译员,有些人真的为此而烦恼。大多数人使用Java是因为PHP对他们来说太快太松散了。这是一种动态语言。利用这一事实

我认为这是不可能的,因为PHP不进行分层对象转换(向上/向下转换)。什么是
子对象
父对象
?这样建模有意义吗?通常,您使用继承使某个类或组功能更具体。像鸭子一样,鸟也是动物。你的例子似乎选得不好。在这种情况下,两个对象都应该来自class
Person
,并且都有一个属性
parent
。这一点很好。我将添加一些信息,尝试更好地解释它。新:签出“con”
function method($this,$methodName,$args) {
   $fullMethodName = "{$this['_class']}_$methodName";
   $fullMethodName($this,$args);
}

// and now, the invocation
method($quasiObj,'method1Name',array(6,7));