Viewed   131 times

I would like to know whether there's a way to chain methods on a newly created object in PHP?

Something like:

class Foo {
    public function xyz() { ... return $this; }
}

$my_foo = new Foo()->xyz();

Anyone know of a way to achieve this?

 Answers

2

In PHP 5.4+, the parser's been modified so you can do something like this

(new Foo())->xyz();

Wrap the instantiation in parenthesis, and chain away.

Prior to PHP 5.4, when you're using the

new Classname();

syntax, you can't chain a method call off the instantiation. It's a limitation of PHP 5.3's syntax. Once an object is instantiated, you can chain away.

One method I've seen used to get around this is a static instantiation method of some kind.

class Foo
{
    public function xyz()
    {
        echo "Called","n";
        return $this;
    }

    static public function instantiate()
    {
        return new self();
    }
}


$a = Foo::instantiate()->xyz();

By wrapping the call to new in a static method, you can instantiate a class with method call, and you're then free to chain off that.

Saturday, October 1, 2022
3

You could keep a list of things that needs to be initialised and whether they have been so in this instance or not. Then check the list each time you use one of the initialisation methods. Something like:

class O {
    private $init = array
        ( 'red' => false
        , 'green' => false
        , 'blue' => false
        );

    private function isInit() {
        $fin = true;
        foreach($this->init as $in) {
            $fin = $fin && $in;
        }
        return $fin;
    }

    public function green($n) {
        $this->init['green'] = true;
        if($this->isInit()) {
            $this->insert();
        }
    }

    public function red($n) {
        $this->init['red'] = true;
        if($this->isInit()) {
            $this->insert();
        }
    }

    public function blue($n) {
        $this->init['blue'] = true;
        if($this->isInit()) {
            $this->insert();
        }
    }

    private function insert() {
        echo "wheen";
    }
}

But personally I think this would be more hassle then it's worth. Better imo to expose your insert method and let the user of you code tell when the initialisation is finished. So something that should be used like:

$o->red(1)->green(2)->blue(0)->insert();

-update-

If it's the case that it's impossible to predict what functions need to be called you really do need to be explicit about it. I can't see a way around that. The reason is that php really can't tell the difference between

$o1 = new A();
$o2 = $o1->stuff();

and

$o2 = (new A())->stuff();

In a language that allows overloading = I guess it would be possible but really really confusing and generally not a good idea.

It is possible to move the explicit part so that it's not at the end of the call chain, but I'm not sure if that would make you happier? It would also go against your desire to not use another instance. It could look something like this:

class O {
    public function __construct(InitO $ini) {
        // Do stuff
        echo "Wheen";
    }
}

class InitO {
    public function red($n) {
        return $this;
    }
    public function green($n) {
        return $this;
    }
    public function blue($n) {
        return $this;
    }
}

$o = new O((new InitO())->red(10)->red(9)->green(7));

You can of course use just one instance by using some other way of wrapping but the only ways I can think of right now would look a lot uglier.

Thursday, August 11, 2022
3

We have an extension method to do exactly this in MoreLINQ. You can look at the implementation there, but basically it's a case of iterating through the data, remembering the maximum element we've seen so far and the maximum value it produced under the projection.

In your case you'd do something like:

var item = items.MaxBy(x => x.Height);

This is better (IMO) than any of the solutions presented here other than Mehrdad's second solution (which is basically the same as MaxBy):

  • It's O(n) unlike the previous accepted answer which finds the maximum value on every iteration (making it O(n^2))
  • The ordering solution is O(n log n)
  • Taking the Max value and then finding the first element with that value is O(n), but iterates over the sequence twice. Where possible, you should use LINQ in a single-pass fashion.
  • It's a lot simpler to read and understand than the aggregate version, and only evaluates the projection once per element
Saturday, November 19, 2022
 
2

In general, if something is documented as being a constructor, use new with it. But in this case, RegExp has a defined "factory" behavior for the situation where you've called it as a function instead. See Section 15.10.3 of the ECMAScript (JavaScript) specification (that links to the outgoing spec; the section number is the same in the new spec, which you can download from the ECMA front page [on the right-hand side]; I don't want to directly link to a ~4MB PDF file):

15.10.3 The RegExp Constructor Called as a Function
15.10.3.1 RegExp(pattern, flags)
If pattern is an object R whose [[Class]] property is "RegExp" and flags is undefined, then return R unchanged. Otherwise call the RegExp constructor (15.10.4.1), passing it the pattern and flags arguments and return the object constructed by that constructor.

You can actually define your own JavaScript constructor functions to allow omitting the new keyword (by detecting they've been called as a function instead, and turning around and calling themselves correctly), but I wouldn't suggest it as it leads to misleading code.

Wednesday, December 21, 2022
 
2

This was an issue with saving the user. I updated my create action in the Dwellings controller to to force a save of the user using user.save! to break the application and get an explicit error. Rails complained of a missing password (and password length) and password_confirmation. I am enforcing a presence validation on both password and password_confirmation as well as a length validation on password in my User model. I updated these validations to be enforced only on create. This solved the issue. With the validations no longer being enforced on user.save, the dwelling_id was successfully added to the user record. The newly implemented code is below.

#dwellings_controller.rb
...
def create
  @dwelling = current_user.properties.build(params[:dwelling])

  if @dwelling.save
    current_user.dwelling = @dwelling
    if current_user.save
      flash[:success] = "Woohoo! Your dwelling has been created. Welcome home!"
    else
      flash[:notice] = "You have successfully created a dwelling, but something prevented us from adding you as a roomie. Please email support so we can try to correct this for you."
    end
    redirect_to current_user
    else
      render 'new'
    end
  end
...

--

#user.rb
...
  validates :password, presence: { :on => :create }, length: { minimum: 6, :on => :create }
  validates :password_confirmation, presence: { :on => :create }
Tuesday, November 1, 2022
 
cgp
 
cgp
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :