关于php:静态构造函数的最佳实践

Best practices for static constructors

我想创建一个类的实例,并在该实例上用一行代码调用一个方法。

PHP不允许对常规构造函数调用方法:

1
new Foo()->set_sth(); // Outputs an error.

因此,如果我可以称之为静态构造函数:

1
Foo::construct()->set_sth();

我的问题是:

使用这样的静态构造函数是一个好的实践吗?如果是,您将如何建议为这些静态构造函数命名方法?

我一直在犹豫以下选择:

1
2
3
4
5
Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();


任何方法的命名都应具有显示名称的意图。我不知道"工厂"是干什么的。尝试建立更高级别的语言:

1
User::with100StartingPoints();

这将与以下内容相同:

1
2
$user = new User();
$user->setPointsTo(100);

您还可以很容易地测试user::with100startingpoints()是否等于这个值。


正如@koen所说,静态构造器(或"命名构造器")只对证明意图有益。

不过,从5.4开始,出现了一个名为"取消引用"的方法,它允许您直接用方法调用内联类实例化。

1
(new MyClass($arg1))->doSomething(); // works with newer versions of php

因此,只有当您有多种方法来实例化对象时,静态构造函数才有用。如果只有一个参数(参数类型和参数数目总是相同),则不需要静态构造函数。

但是,如果您有多种实例化方法,那么静态构造函数非常有用,因为它避免了用无用的参数检查污染主构造函数,从而削弱了语言约束。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

class Duration
{
private $start;
private $end;

// or public depending if you still want to allow direct instantiation
private function __construct($startTimeStamp = null, $endTimestamp = null)
{
   $this->start = $startTimestamp;
   $this->end   = $endTimestamp;
}

public static function fromDateTime(\DateTime $start, \DateTime $end)
{
    return new self($start->format('U'), $end->format('U'));
}

public static function oneDayStartingToday()
{
    $day = new self;
    $day->start = time();
    $day->end = (new \DateTimeImmutable)->modify('+1 day')->format('U');

    return $day;
}

}

正如您在oneDayStartingToday中看到的,静态方法可以访问实例的私有字段!太疯狂了,不是吗?:)

有关更好的解释,请参阅http://verraes.net/2014/06/named-constructors-in-php/


如果您不需要参考新构建的Foo,为什么不简单地将set_sth设置为static函数(如果需要,让它在内部创建一个新的Foo)?

如果你真的需要得到参考资料,你会怎么做?return $thisset_sth中?但这样一来,set_sth就可以变成工厂功能了。

我能想到的唯一情况是,如果您想在一个新构建的实例上调用一个表达式中所有的可链接方法(比如在一个流畅的接口中)。这就是你想做的吗?

无论如何,您可以对所有类型的对象使用通用的工厂函数,例如

1
2
3
4
5
function create_new($type) {
    return new $type;
}

create_new('Foo')->set_sth();


这可能不是一个很好的实践,但是您可以使用这样一个事实:函数和类有两个不同的名称空间:您可以有一个与类同名的函数。

这样就可以编写这种代码,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
function MyClass() {
    return new MyClass();
}

class MyClass {
    public function __construct() {
        $this->a ="plop";
    }
    public function test() {
        echo $this->a;
    }
    protected $a;
}

注意,我定义了一个名为MyClass的函数和一个同名的类。

然后,你可以写下:

1
MyClass()->test();

这将很好地工作,不会给您带来任何错误——这里,您将得到以下输出:

1
plop


这些被称为创建方法,我通常将它们命名为createXXX(),例如createById()createEmptyCatalog()。它们不仅提供了一种很好的方法来揭示对象构造函数的不同意图,而且还支持在一个流畅的接口中立即链接方法。

1
2
3
4
echo Html_Img::createStatic('/images/missing-image.jpg')
        ->setSize(60, 90)
        ->setTitle('No image for this article')
        ->setClass('article-thumbnail');

除了jon的答案:要允许构造函数参数,请使用以下内容:

1
2
3
4
5
6
function create($type) {
    $args = func_get_args();
    $reflect = new ReflectionClass(array_shift($args));
    return $reflect->newInstanceArgs($args);
}
create('Foo', 'some', 'args')->bar();

文件:ReflectionClass->newInstanceArgs


推进使用静态方法"创建"。我会同意的。这种方法使代码更容易测试,而不仅仅是使用静态方法来执行业务逻辑。

1
2
3
4
5
6
7
8
9
10
11
<?php
class MyClass
{
  public static function create()
  {
    return new MyClass();
  }
  public function myMethod()
  {
  }
}

此外,还可以将参数传递给构造函数。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class MyClass
{
  public function __construct($param1, $param2)
  {
   //initialization using params
  }

  public static function create($param1, $param2)
  {
    return new MyClass($param1, $param2); // return new self($param1, $param2); alternative ;)
  }

  public function myMethod()
  {
  }
}

无论哪种情况,您都可以在create方法之后立即调用mymethod

1
2
3
4
<?php
MyClass::create()->myMethod();
// or
MyClass::create($param1, $param2)->myMethod();

参加聚会有点晚,但我想这可能会有帮助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyClass
{

    function __construct() {
       // constructor initializations here
    }

    public static myMethod($set = null) {

       // if myclass is not instantiated
       if (is_null($set)) {
           // return new instance
           $d = new MyClass();
           return $d->Up('s');
       } else {
           // myclass is instantiated
           // my method code goes here
       }
    }
}

然后可以将其用作

1
$result = MyClass::myMethod();

可选参数可以通过uu构造函数或mymethod传递。这是我的第一个帖子,我希望我的花招是对的。