25

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 these static constructors?

I've been hesitating over the following options:

Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();
Emanuil Rusev
  • 34,563
  • 55
  • 137
  • 201
  • But what's the point of creating an instance, if you don't store the instance. You could just directly call a static function then can't you? Or otherwise, maybe the Singleton pattern might interest you? – CharlesLeaf Mar 20 '11 at 11:34
  • 2
    I'm not saying that I'm not storing the instance. – Emanuil Rusev Mar 20 '11 at 11:37
  • If you in theory want to do `new Foo()->bar();` you are not storing the instance. That's why I assumed this. Still, I think the Singleton Pattern is right up your alley. – CharlesLeaf Mar 20 '11 at 11:45
  • 2
    Here's a real world line of code: `echo ORM::factory('article')->find(1)->title;` - it doesn't store an instance, it is not a Singleton and it still makes sense. Wouldn't you agree? – Emanuil Rusev Mar 20 '11 at 11:54

8 Answers8

15

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/

Florian Klein
  • 8,692
  • 1
  • 32
  • 42
  • Definitely the most informative and relevant answer to the original question. Provides both an explanation of when/why to use static constructors and the means to a one-liner without having to go static (albeit 5.4+). – Dennis G. May 05 '17 at 10:57
11

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.

koen
  • 13,349
  • 10
  • 46
  • 51
8

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();
Jon
  • 428,835
  • 81
  • 738
  • 806
6

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
Pascal MARTIN
  • 395,085
  • 80
  • 655
  • 663
  • That's quite elegant. Do you know of any open source projects that use this style? I'd love to see that implemented in a real world project. – Emanuil Rusev Mar 20 '11 at 12:04
  • No, I've never seen this used in large scale in any project I've worked on, actually : I just know it's possible, and might have used once or twice, but not often. – Pascal MARTIN Mar 20 '11 at 12:09
  • What is benefits of using this style? The only benefit that I can see - there is no requirment to type 'new' keyword – kirugan May 13 '16 at 14:27
  • don't really know, if i shall up- or downvote. i would definitely say, that this is a bad practice, because it makes the code unclean, but on the other hand, this is such a handy thing, i can't resist trying to use it at least once. i can imagine, that this may be useful for querybuilders. – emfi Jan 29 '19 at 15:35
  • This is beautiful. I understand why people say it's ugly - because it's innovative and everything new is ugly to whoever is afraid to introduce new trends. But this IS a nice solution that makes absolute sense, improves readability and solves a problem that needed some solving. Upvoted. – dkellner Jul 11 '19 at 21:32
4

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

Community
  • 1
  • 1
NikiC
  • 100,734
  • 37
  • 191
  • 225
4

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');
David Harkness
  • 35,992
  • 10
  • 112
  • 134
1

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();
Hezuo
  • 23
  • 5
-2

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

Jose Ananio
  • 666
  • 6
  • 5