开发者

Best practices for static constructors

I want to create an instance of a class and call a method on that instance, in a single line of code.

PHP won't allow calling a method on a regular constructor:

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

So I'm using, if I can call it that, a static constructor:

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

Here's my question:

Is using static constructors like that considered a good practice and if yes, how would you recommend naming the methods for th开发者_JS百科ese static constructors?

I've been hesitating over the following options:

Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();


Static constructors (or "named constructors") are only beneficial to prove an intention, as @koen says.

Since 5.4 though, someting called "dereferencing" appeared, which permits you to inline class instantiation directly with a method call.

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

So, static constructors are only useful if you have multiple ways to instantiate your objects. If you have only one (always the same type of arguments and number of args), there is no need for static constructors.

But if you have multiple ways of instantiations, then static constructors are very useful, as it avoids to pollute your main constructor with useless argument checking, weakening languages constraints.

Example:

<?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;
}

}

As you can see in oneDayStartingToday, the static method can access private fields of the instance! Crazy isn't it ? :)

For a better explanation, see http://verraes.net/2014/06/named-constructors-in-php/


The naming of any method should be with intention revealing names. I can't tell what 'Foo::factory' does. Try to build to a higher level language:

User::with100StartingPoints();

This would be the same as:

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

You could also easily test whether User::with100StartingPoints() is equal to this.


If you don't need a reference to the newly constructed Foo, why don't you simply make set_sth a static function (and have it create a new Foo internally if required)?

If you do need to get hold of the reference, how would you do it? return $this in set_sth? But then set_sth can be made into a factory function anyway.

The only situation I can think of is if you want to call chainable methods (like in a fluent interface) on a newly constructed instance all in one expression. Is that what you are trying to do?

Anyway, you can use a general-purpose factory function for all types of objects, e.g.

function create_new($type) {
    return new $type;
}

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


It's probably not quite a best practice, but you could use the fact that functions and classes have two different namespaces : you can have a function that have the same name as a class.

This allows one to write this kind of code, for example :

function MyClass() {
    return new MyClass();
}

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

Note that I have defined a function called MyClass, and a class with the same name.


Then, you can write this :

MyClass()->test();

Which will work perfectly, and not get you any error -- here, you'll get the following output :

plop


Addition to Jon's answer: To allow constructor arguments use the following:

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

Documentation: ReflectionClass->newInstanceArgs


These are called creation methods, and I typically name them createXXX() such as createById() or createEmptyCatalog(). Not only do they provide a nice way to reveal the different intentions of an object's constructors, but they enable immediate method chaining in a fluent interface.

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


Propel uses a static method "create". I'd go with that. This method makes the code easier to test rather than just using static methods to perform business logic.

<?php 
class MyClass
{
  public static function create()
  {
    return new MyClass();
  }
  public function myMethod()
  {
  }
}

Besides, you can also pass parameters to the constructor. For instance:

<?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()
  {
  }
}

In either case, you'd be able to invoke myMethod right after the create method

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


A bit late to the party but I think this might help.

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
       }
    }
}

this can then be used as

$result = MyClass::myMethod();

optional parameters can be passed through either the __constructor or myMethod.
This is my first post and I hope I got the gimmicks right

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜