Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 以编程方式构造具有未知数量参数(以及某些特定要求)的对象_Php_Oop - Fatal编程技术网

Php 以编程方式构造具有未知数量参数(以及某些特定要求)的对象

Php 以编程方式构造具有未知数量参数(以及某些特定要求)的对象,php,oop,Php,Oop,我正在构建一个框架,我遇到了一个小问题。嗯,不仅仅是一个问题,我称之为挑战。在构建widget系统时,我发现我需要为我的所有widget提供一些基本功能,即build()静态方法,该方法允许我轻松实例化任何widget,然后链接下一个方法,如下所示: $code = MySpecificWidget::build()->getHTML(); 为此,我编写了以下代码: abstract class StandardWidget { public static function bui

我正在构建一个框架,我遇到了一个小问题。嗯,不仅仅是一个问题,我称之为挑战。在构建widget系统时,我发现我需要为我的所有widget提供一些基本功能,即
build()
静态方法,该方法允许我轻松实例化任何widget,然后链接下一个方法,如下所示:

$code = MySpecificWidget::build()->getHTML();
为此,我编写了以下代码:

abstract class StandardWidget {

  public static function build() {
    $className = get_called_class(); // the "late static binding" classname
    return new $className;
  }

}

class MySpecificWidget extends StandardWidget {

  public function getHTML() {
    return '<b>Some HTML</b>';
  }

}
因此,如果需要参数,只需在子类中编写
\uu construct()

不幸的是,PHP解析器告诉我不能静态调用
\uu construct()

因为我正在编写一个框架,所以我试图尽可能地轻量级。这就是挑战:

有没有办法做到这一点而不:

  • 使用
    eval()
  • 使用反射
  • 必须在每个子类中强制编写构造函数
更新

关于我为什么要这样做的更多见解

它背后的想法是,大多数时候,一个小部件会被实例化,然后被要求返回它的HTML,最后永远销毁它自己,因为它不再需要了。它们最有可能被用作关联数组中的参数(将传递给模板系统的参数),因此我认为将它们“匿名”实例化并放在一行中会非常有用并节省代码

小部件不能是静态类,因为它们可能有一个内部状态(只有在这种情况下,我才不需要链接它的构造函数),并且为了避免它变得麻烦(因为我不希望每个小部件都有一个构造函数,如果它不需要的话),我希望
build
方法位于所有小部件从中继承的抽象类中


我想最好的方法就是我上面写的方法,只要我能像那样调用神奇的构造函数…

我不知道是否有任何特殊的需要为您构建方法,但您尝试的是一种工厂模式

解决问题的一个简单方法是说“小部件构造函数必须将数组作为单个参数”。
因此,您可以使用
func\u get\u args
获取参数,并将该数组传递给小部件构造函数。

如何允许getHtml函数接受数组作为参数,然后开发人员可以根据需要使用该参数?

您确定需要此静态
构建方法吗?除了保存一个LOC,我看不出你的
build()
方法比
new
有什么好处

$widget = new MySpecificWidget($param1, $param2, $param500);
$code = $widget->getHTML();

应该可以很好地工作,并允许在构造函数中有不同的参数。我认为你的
builded
方法引入了不必要的复杂性,并解决了一个非问题。

因为
\uuu-construct
初始化了一个对象,但没有分配对象,所以你不能使用
new
ReflectionClass::newInstance
(据我所知,这是分配新对象的唯一方法)。这意味着您可以使用
new
创建一个实例,然后再次调用
\u construct
来初始化对象

<?php

class A {
    function __construct() {
        echo __CLASS__ , '/', get_called_class(), "\n";
    }

    static function build() {
        $class = get_called_class();
        $args = func_get_args();
        echo "Building $class:\n";
        $instance = new static;
        # don't bother calling constructor a second time if there are no arguments
        if ($args) {
            echo "Calling $class::__construct:\n";
            call_user_func_array(array($instance, '__construct'), $args);
        }
        echo "done\n";
        return $instance;
    }
}

class B extends A {
    function __construct($foo=Null, $bar=Null) {
        if (! is_null($foo)) {
            parent::__construct();
            echo __CLASS__ , '/', get_called_class(), "::__construct($foo, $bar)\n";
        }
    }
}
class C extends A {}

$b = B::build(1, 2);
$c = C::build();
通过检查
build
的参数计数,您可以在堆栈跟踪中进行更高级别的操作,以告知是否会有第二次调用,但这并不特别优雅

class HeavyWidget extends StandardWidget {
    function __construct($id=Null) {
        $trace = debug_backtrace();
        if (! is_null($id)                                     # there's an argument
            || count($trace) <= 2                              # not called within `build`
            || $trace[1]['function'] == 'call_user_func_array' # direct
            || ($trace[2]['function'] == 'build'               # indirect, no 2nd invocation
                && count($trace[2]['args']) == 0)
            || $trace[2]['function'] != 'build'                # indirect, not called within `build`
            )
        {
            /* direct invocation, or there aren't any arguments so there 
             * won't be a second (direct) invocation
             */ 
            parent::__construct();
            if (is_null($id)) {
                $this->id = self::generateId();
            } else {
                $this->id = $id;
            }
            ...
        }
    }
}
注意,这与前面的代码有一个类似的问题:如果第一个参数可能是(非参数)数组,而间接调用中的第二个为null,则可能无法根据参数模式区分直接调用和间接调用

class NopDelegate {
    function __get($name) {}
function __call($name,$arguments) {}
    function __callStatic($name,$arguments) {}
}

class ProblemWidget extends StandardWidget {
    function __construct($values, $delegate=Null) {
        parent::__construct();
        $this->values = $values;
        if (is_null($delegate)) {
            $this->delegate = new NopDelegate;
        }
    }
}

$works = new ProblemWidget(array('a', 'b', 'c'));
$works2 = new ProblemWidget(array('a', 'b', 'c'), new SomeDelegate());

$oops = ProblemWidget::make(array('a', 'b', 'c'));
$oops2 = ProblemWidget::make(array('a', 'b', 'c'), new SomeDelegate());
$oops_also = ProblemWidget::make('a', 'b', 'c', new SomeDelegate());

因此,唯一真正透明的选择是使用反射并接受性能影响。

反射的可能重复以及反射的错误是什么?构建方法可以允许进行其他操作,例如注册等。父构造函数不是更适合此操作的地方吗?@middus:事情是,它的整体思想是能够将构造函数和下面的方法链接在一行中。例如,如果我将小部件的HTML作为参数传递给另一个函数,或者将其放入数组中,则创建一个小部件的一个实例不会有太大问题。。。但假设我有5到10个小部件,那将是5到10行代码,这些代码只是为了把事情弄得一团糟。除非我以后想引用它们,否则保存它们是没有意义的。也许我应该在我的问题中更强调这一点。@Boro好的,明白了:这只是一个荣耀的
sprintf
!?如果只是为了扔掉而实例化,那么为什么首先要实例化对象呢?只需将其包装在一个函数中,就完成了。或者,您可能需要引入某种接受参数的
init
方法。不完全是这样,因为小部件中可能有一些复杂的功能。例如,寻呼机小部件必须解析它应该有多少页,然后才返回它的html。这不是一个坏主意,尽管有点麻烦,因为它不是透明的。在接受你的回答之前,我会等着看是否有人提出了其他的想法。谢谢是的,我想我最好还是反省一下。遗憾的是,它不能通过
调用用户函数数组()实现。
。谢谢
class HeavyWidget extends StandardWidget {
    function __construct($id=Null) {
        $trace = debug_backtrace();
        if (! is_null($id)                                     # there's an argument
            || count($trace) <= 2                              # not called within `build`
            || $trace[1]['function'] == 'call_user_func_array' # direct
            || ($trace[2]['function'] == 'build'               # indirect, no 2nd invocation
                && count($trace[2]['args']) == 0)
            || $trace[2]['function'] != 'build'                # indirect, not called within `build`
            )
        {
            /* direct invocation, or there aren't any arguments so there 
             * won't be a second (direct) invocation
             */ 
            parent::__construct();
            if (is_null($id)) {
                $this->id = self::generateId();
            } else {
                $this->id = $id;
            }
            ...
        }
    }
}
class MySpecificWidget extends StandardWidget {
    function __construct($args, $foo=Null) {
        if (is_null($foo) && is_array($args)) {
            /* called from StandardWidget::build(); $args contains all arguments.
             * Extract them.
             */
            $foo = $args[1];
            $args = $args[0];
        }
        ...
    }
}

$frob = new MySpecificWidget(42,23);
class NopDelegate {
    function __get($name) {}
function __call($name,$arguments) {}
    function __callStatic($name,$arguments) {}
}

class ProblemWidget extends StandardWidget {
    function __construct($values, $delegate=Null) {
        parent::__construct();
        $this->values = $values;
        if (is_null($delegate)) {
            $this->delegate = new NopDelegate;
        }
    }
}

$works = new ProblemWidget(array('a', 'b', 'c'));
$works2 = new ProblemWidget(array('a', 'b', 'c'), new SomeDelegate());

$oops = ProblemWidget::make(array('a', 'b', 'c'));
$oops2 = ProblemWidget::make(array('a', 'b', 'c'), new SomeDelegate());
$oops_also = ProblemWidget::make('a', 'b', 'c', new SomeDelegate());