Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
783 views
in Technique[技术] by (71.8m points)

php - Cahining pattern

I have a class in php that works with the chainning method, but the problem is that I want to chain the methods in some order.

class Chain {
  public function foo () {
    return $this;
  }
  public function bar () {
    return $this;
  }
  public function some () {
    return $this;
  }
}

So, if I use this class, then I can chain this methods in 9 different ways (all the possible combinations of 3 elements)

But what happen if I determine that the method some always must to be chained after foo or bar and not in other way?

$chain = new Chain();
$chain->foo->bar(); //works; i.e: the method some is optional
$chain->foo()->bar()->some(); //works
$chain->bar()->foo()->some(); //works
$chain->some()->bar()->foo(); //throws an exception

I think that I can do this setting boolean values, something like: when the method foo or bar are called, then I set the value to some var to true, and when the developer calls the some function, if that var is false, then throws an exception, otherwise is allowed to continue.

But I need something more elegant, such as pattern or a built-in solution.

There is another way to do it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The very rough example I imagine will still have some lines of code in each method

<?php
class Chain {

  private $_register = array();

  public function foo () {
    $this->register(__METHOD__);
    return $this;
  }
  public function bar () {
    $this->register(__METHOD__);
    return $this;
  }
  public function some () {;
    $this->verify('foo'); // foo() should be called before some();
    $this->register(__METHOD__);
    echo 'it's ok';
    return $this;
  }
  public function verify($method) {
      if(array_key_exists($method, $this->_register) && $this->_register[$method] == true) {
          return true;
      }
      else {
          throw new Exception('Some exception');
      }
  }
  public function register($method) {
      $method = str_replace(__CLASS__.'::', '', $method);
      $this->_register[$method] = true;
  }
}

What do we do here - we have a register() and verify() methods. (they can be helpers, but for the current purpose I added them in the class.

Each method should have before it's returning value a register to itself. Calling $this->register(__METHOD__) from foo() will add in the private array 'foo' => true. The verify() method checks if foo exist as array key and if its value is true. If it is - the script will continue. Otherwise - throws exception.

In this case:

$chain = new Chain();
$chain->bar()->some()->foo(); //throws an exception

Fatal error: Uncaught exception 'Exception' with message 'Some exception' in ...

$chain = new Chain();
$chain->foo()->some()->foo(); // ok

it's ok

The problem here is that we establish a "convention". You need to pass __METHOD__ to the register function so after it replace the classname it will add only the method name in the array. So later, in the function where you need to verify if one or more functions are called before this, you need to use the method name as string i.e. $this->verify('foo');

Ofcourse you can play different scenarios without stripping and testing with strpos() or adding () after the methodname for easier recognition if you are verifying a method or smth else.

But at least it will save you from making for each method, different variable to fill i.e.

function foo() {
    $this->_foo = true;
    return $this;
}
function bar() {
    $this->_bar = true;
    return $this;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share

2.1m questions

2.1m answers

63 comments

56.6k users

...