How can I implement Method Chaining in PHP 5.x? - php

I have the following class written for PHP 5.4.x. Should this work as I expect?
class SqlBuilder {
private $dbTable;
private $action;
private $data;
private $clause;
public function toString() {
// $sql = generate sql string
// [...]
return $sql;
}
[...]
public function setClause($clause) {
$this->clause = $clause;
}
public function setDbTable($dbTable) {
$this->dbTable = $dbTable;
}
public function setAction($action) {
$this->action = $action;
}
}
$sql = (new \dbal\SqlBuilder())
->setAction($this->action)
->setClause($this->clause)
->setDbTable($this->dbTable)
->toString();
I am expecting to be able to access all of my setter methods. Instead I see the following error:
Fatal error: Call to a member function toString() on a non-object )
This seems to work:
$builder= new \dbal\SqlBuilder();
$builder->setAction($this->action)
$builder->setClause($this->clause)
$builder->setDbTable($this->dbTable)
$sql = $builder->toString();
But I know that this works as well:
class Foo
{
public $a = "I'm a!";
public $b = "I'm b!";
public $c;
public function getB() {
return $this->b;
}
public function setC($c) {
$this->c = $c;
return $this;
}
public function getC() {
return $this->c;
}
}
print (new Foo)
->setC($_GET["c"])
->getC(); // I'm c!
I've used this style of syntax in Javascript before. Is there a way to make it work in PHP?

What you are asking about is called method chaining. In order for it to work the way you want, each method call needs to return a reference to the object that you are calling. So,
->setAction($this->action)
// needs to return $this; so that
->setClause($this->clause)
// knows what to operate upon and in turn needs to return $this; so that
->setDbTable($this->dbTable)
// can do the same
Try :
public function setClause($clause) {
$this->clause = $clause;
return $this;
}
public function setDbTable($dbTable) {
$this->dbTable = $dbTable;
return $this;
}
public function setAction($action) {
$this->action = $action;
return $this;
}

Related

How to create statics methods nested a class PHP [duplicate]

Is it possible to chain static methods together using a static class? Say I wanted to do something like this:
$value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result();
. . . and obviously I would want $value to be assigned the number 14. Is this possible?
Update: It doesn't work (you can't return "self" - it's not an instance!), but this is where my thoughts have taken me:
class TestClass {
public static $currentValue;
public static function toValue($value) {
self::$currentValue = $value;
}
public static function add($value) {
self::$currentValue = self::$currentValue + $value;
return self;
}
public static function subtract($value) {
self::$currentValue = self::$currentValue - $value;
return self;
}
public static function result() {
return self::$value;
}
}
After working that out, I think it would just make more sense to simply work with a class instance rather than trying to chain static function calls (which doesn't look possible, unless the above example could be tweaked somehow).
I like the solution provided by Camilo above, essentially since all you're doing is altering the value of a static member, and since you do want chaining (even though it's only syntatic sugar), then instantiating TestClass is probably the best way to go.
I'd suggest a Singleton pattern if you want to restrict instantiation of the class:
class TestClass
{
public static $currentValue;
private static $_instance = null;
private function __construct () { }
public static function getInstance ()
{
if (self::$_instance === null) {
self::$_instance = new self;
}
return self::$_instance;
}
public function toValue($value) {
self::$currentValue = $value;
return $this;
}
public function add($value) {
self::$currentValue = self::$currentValue + $value;
return $this;
}
public function subtract($value) {
self::$currentValue = self::$currentValue - $value;
return $this;
}
public function result() {
return self::$currentValue;
}
}
// Example Usage:
$result = TestClass::getInstance ()
->toValue(5)
->add(3)
->subtract(2)
->add(8)
->result();
class oop{
public static $val;
public static function add($var){
static::$val+=$var;
return new static;
}
public static function sub($var){
static::$val-=$var;
return new static;
}
public static function out(){
return static::$val;
}
public static function init($var){
static::$val=$var;
return new static;
}
}
echo oop::init(5)->add(2)->out();
Little crazy code on php5.3... just for fun.
namespace chaining;
class chain
{
static public function one()
{return get_called_class();}
static public function two()
{return get_called_class();}
}
${${${${chain::one()} = chain::two()}::one()}::two()}::one();
With php7 you will be able to use desired syntax because of new Uniform Variable Syntax
<?php
abstract class TestClass {
public static $currentValue;
public static function toValue($value) {
self::$currentValue = $value;
return __CLASS__;
}
public static function add($value) {
self::$currentValue = self::$currentValue + $value;
return __CLASS__;
}
public static function subtract($value) {
self::$currentValue = self::$currentValue - $value;
return __CLASS__;
}
public static function result() {
return self::$currentValue;
}
}
$value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result();
echo $value;
Demo
If toValue(x) returns an object, you could do like this:
$value = TestClass::toValue(5)->add(3)->substract(2)->add(8);
Providing that toValue returns a new instance of the object, and each next method mutates it, returning an instance of $this.
This is more accurate, easier, and read-friendly (allows code-completion)
class Calculator
{
public static $value = 0;
protected static $onlyInstance;
protected function __construct ()
{
// disable creation of public instances
}
protected static function getself()
{
if (static::$onlyInstance === null)
{
static::$onlyInstance = new Calculator;
}
return static::$onlyInstance;
}
/**
* add to value
* #param numeric $num
* #return \Calculator
*/
public static function add($num)
{
static::$value += $num;
return static::getself();
}
/**
* substruct
* #param string $num
* #return \Calculator
*/
public static function subtract($num)
{
static::$value -= $num;
return static::getself();
}
/**
* multiple by
* #param string $num
* #return \Calculator
*/
public static function multiple($num)
{
static::$value *= $num;
return static::getself();
}
/**
* devide by
* #param string $num
* #return \Calculator
*/
public static function devide($num)
{
static::$value /= $num;
return static::getself();
}
public static function result()
{
return static::$value;
}
}
Example:
echo Calculator::add(5)
->subtract(2)
->multiple(2.1)
->devide(10)
->result();
result: 0.63
People are overcomplicating this like crazy.
Check this out:
class OopClass
{
public $first;
public $second;
public $third;
public static function make($first)
{
return new OopClass($first);
}
public function __construct($first)
{
$this->first = $first;
}
public function second($second)
{
$this->second = $second;
return $this;
}
public function third($third)
{
$this->third = $third;
return $this;
}
}
Usage:
OopClass::make('Hello')->second('To')->third('World');
You could always use the First method as a static and the remaining as instance methods:
$value = Math::toValue(5)->add(3)->subtract(2)->add(8)->result();
Or better yet:
$value = Math::eval(Math::value(5)->add(3)->subtract(2)->add(8));
class Math {
public $operation;
public $operationValue;
public $args;
public $allOperations = array();
public function __construct($aOperation, $aValue, $theArgs)
{
$this->operation = $aOperation;
$this->operationValue = $aValue;
$this->args = $theArgs;
}
public static function eval($math) {
if(strcasecmp(get_class($math), "Math") == 0){
$newValue = $math->operationValue;
foreach ($math->allOperations as $operationKey=>$currentOperation) {
switch($currentOperation->operation){
case "add":
$newvalue = $currentOperation->operationValue + $currentOperation->args;
break;
case "subtract":
$newvalue = $currentOperation->operationValue - $currentOperation->args;
break;
}
}
return $newValue;
}
return null;
}
public function add($number){
$math = new Math("add", null, $number);
$this->allOperations[count($this->allOperations)] &= $math;
return $this;
}
public function subtract($number){
$math = new Math("subtract", null, $number);
$this->allOperations[count($this->allOperations)] &= $math;
return $this;
}
public static function value($number){
return new Math("value", $number, null);
}
}
Just an FYI.. I wrote this off the top of my head (right here on the site). So, it may not run, but that is the idea. I could have also did a recursive method call to eval, but I thought this may be simpler. Please let me know if you would like me to elaborate or provide any other help.
Technically you can call a static method on an instance like $object::method() in PHP 7+, so returning a new instance should work as a replacement for return self. And indeed it works.
final class TestClass {
public static $currentValue;
public static function toValue($value) {
self::$currentValue = $value;
return new static();
}
public static function add($value) {
self::$currentValue = self::$currentValue + $value;
return new static();
}
public static function subtract($value) {
self::$currentValue = self::$currentValue - $value;
return new static();
}
public static function result() {
return self::$currentValue;
}
}
$value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result();
var_dump($value);
Outputs int(14).
This about same as returning __CLASS__ as used in other answer. I rather hope no-one ever decides to actually use these forms of API, but you asked for it.
In a nutshell... no. :) The resolution operator (::) would work for the TetsClass::toValue(5) part, but everything after that will just give a syntax error.
Once namespaces are implemented in 5.3, you can have "chained" :: operators, but all that'll do is drill down through the namespace tree; it won't be possible to have methods in the middle of things like this.
The best that can be done
class S
{
public static function __callStatic($name,$args)
{
echo 'called S::'.$name . '( )<p>';
return '_t';
}
}
$_t='S';
${${S::X()}::F()}::C();
No, this won't work. The :: operator needs to evaluate back to a class, so after the TestClass::toValue(5) evaluates, the ::add(3) method would only be able to evaluate on the answer of the last one.
So if toValue(5) returned the integer 5, you would basically be calling int(5)::add(3) which obviously is an error.
The most easiest way i have ever found for method chaining from new Instance or Static method of class is as below. I have used Late Static Binding here and i really loved this solution.
I have created a utility to send multiple User Notification on next page using tostr in Laravel.
<?php
namespace App\Utils;
use Session;
use Illuminate\Support\HtmlString;
class Toaster
{
private static $options = [
"closeButton" => false,
"debug" => false,
"newestOnTop" => false,
"progressBar" => false,
"positionClass" => "toast-top-right",
"preventDuplicates" => false,
"onclick" => null,
"showDuration" => "3000",
"hideDuration" => "1000",
"timeOut" => "5000",
"extendedTimeOut" => "1000",
"showEasing" => "swing",
"hideEasing" => "linear",
"showMethod" => "fadeIn",
"hideMethod" => "fadeOut"
];
private static $toastType = "success";
private static $instance;
private static $title;
private static $message;
private static $toastTypes = ["success", "info", "warning", "error"];
public function __construct($options = [])
{
self::$options = array_merge(self::$options, $options);
}
public static function setOptions(array $options = [])
{
self::$options = array_merge(self::$options, $options);
return self::getInstance();
}
public static function setOption($option, $value)
{
self::$options[$option] = $value;
return self::getInstance();
}
private static function getInstance()
{
if(empty(self::$instance) || self::$instance === null)
{
self::setInstance();
}
return self::$instance;
}
private static function setInstance()
{
self::$instance = new static();
}
public static function __callStatic($method, $args)
{
if(in_array($method, self::$toastTypes))
{
self::$toastType = $method;
return self::getInstance()->initToast($method, $args);
}
throw new \Exception("Ohh my god. That toast doesn't exists.");
}
public function __call($method, $args)
{
return self::__callStatic($method, $args);
}
private function initToast($method, $params=[])
{
if(count($params)==2)
{
self::$title = $params[0];
self::$message = $params[1];
}
elseif(count($params)==1)
{
self::$title = ucfirst($method);
self::$message = $params[0];
}
$toasters = [];
if(Session::has('toasters'))
{
$toasters = Session::get('toasters');
}
$toast = [
"options" => self::$options,
"type" => self::$toastType,
"title" => self::$title,
"message" => self::$message
];
$toasters[] = $toast;
Session::forget('toasters');
Session::put('toasters', $toasters);
return $this;
}
public static function renderToasters()
{
$toasters = Session::get('toasters');
$string = '';
if(!empty($toasters))
{
$string .= '<script type="application/javascript">';
$string .= "$(function() {\n";
foreach ($toasters as $toast)
{
$string .= "\n toastr.options = " . json_encode($toast['options'], JSON_PRETTY_PRINT) . ";";
$string .= "\n toastr['{$toast['type']}']('{$toast['message']}', '{$toast['title']}');";
}
$string .= "\n});";
$string .= '</script>';
}
Session::forget('toasters');
return new HtmlString($string);
}
}
This will work as below.
Toaster::success("Success Message", "Success Title")
->setOption('showDuration', 5000)
->warning("Warning Message", "Warning Title")
->error("Error Message");
Fully functional example of method chaining with static attributes:
<?php
class Response
{
static protected $headers = [];
static protected $http_code = 200;
static protected $http_code_msg = '';
static protected $instance = NULL;
protected function __construct() { }
static function getInstance(){
if(static::$instance == NULL){
static::$instance = new static();
}
return static::$instance;
}
public function addHeaders(array $headers)
{
static::$headers = $headers;
return static::getInstance();
}
public function addHeader(string $header)
{
static::$headers[] = $header;
return static::getInstance();
}
public function code(int $http_code, string $msg = NULL)
{
static::$http_code_msg = $msg;
static::$http_code = $http_code;
return static::getInstance();
}
public function send($data, int $http_code = NULL){
$http_code = $http_code != NULL ? $http_code : static::$http_code;
if ($http_code != NULL)
header(trim("HTTP/1.0 ".$http_code.' '.static::$http_code_msg));
if (is_array($data) || is_object($data))
$data = json_encode($data);
echo $data;
exit();
}
function sendError(string $msg_error, int $http_code = null){
$this->send(['error' => $msg_error], $http_code);
}
}
Example of use:
Response::getInstance()->code(400)->sendError("Lacks id in request");
Here's another way without going through a getInstance method (tested on PHP 7.x):
class TestClass
{
private $result = 0;
public function __call($method, $args)
{
return $this->call($method, $args);
}
public static function __callStatic($method, $args)
{
return (new static())->call($method, $args);
}
private function call($method, $args)
{
if (! method_exists($this , '_' . $method)) {
throw new Exception('Call undefined method ' . $method);
}
return $this->{'_' . $method}(...$args);
}
private function _add($num)
{
$this->result += $num;
return $this;
}
private function _subtract($num)
{
$this->result -= $num;
return $this;
}
public function result()
{
return $this->result;
}
}
The class can be used as following:
$res1 = TestClass::add(5)
->add(3)
->subtract(2)
->add(8)
->result();
echo $res1 . PHP_EOL; // 14
$res2 = TestClass::subtract(1)->add(10)->result();
echo $res2 . PHP_EOL; // 9
Also works as:
ExampleClass::withBanners()->withoutTranslations()->collection($values)
Using new static(self::class);
public static function withoutTranslations(): self
{
self::$withoutTranslations = true;
return new static(self::class);
}
public static function withBanners(): self
{
return new static(self::class);
}
public static function collection(values): self
{
return $values;
}
Use PHP 7! If your web provider cannot --> change provider! Don't lock in past.
final class TestClass {
public static $currentValue;
public static function toValue($value) {
self::$currentValue = $value;
return __CLASS__;
}
public static function add($value) {
self::$currentValue = self::$currentValue + $value;
return __CLASS__;
}
public static function subtract($value) {
self::$currentValue = self::$currentValue - $value;
return __CLASS__;
}
public static function result() {
return self::$currentValue;
}
}
And very simple use:
$value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result();
var_dump($value);
Return (or throw error):
int(14)
completed contract.
Rule one: most evolved and maintainable is always better.

Method chaining a get function to return specific $this properties

I want to be able to use an object like below, to retrieve new orders and new invoices. I feel like it is most readable, but I am having trouble writing the PHP class to work this way.
$amazon = new Amazon();
$amazon->orders('New')->get();
$amazon->invoices('New')->get();
In my PHP class, how would my get() method be able to distinguish whether to return orders or invoices?
<?php
namespace App\Vendors;
class Amazon
{
private $api_key;
public $orders;
public $invoices;
public function __construct()
{
$this->api_key = config('api.key.amazon');
}
public function orders($status = null)
{
$this->orders = 'orders123';
return $this;
}
public function invoices($status = null)
{
$this->invoices = 'invoices123';
return $this;
}
public function get()
{
// what is the best way to return order or invoice property
// when method is chained?
}
}
A couple of ways, if you want it dynamic and don't do any logic in the methods, use something like __call
<?php
class Amazon {
public $type;
public $method;
public function get()
{
// do logic
// ...
return 'Fetching: '.$this->method.' ['.$this->type.']';
}
public function __call($method, $type)
{
$this->method = $method;
$this->type = $type[0];
return $this;
}
}
$amazon = new Amazon();
echo $amazon->orders('New')->get();
echo $amazon->invoices('New')->get();
If you want to do logic in the methods, do something like:
<?php
class Amazon {
public $type;
public $method;
public function get()
{
return 'Fetching: '.$this->method.' ['.$this->type.']';
}
public function orders($type)
{
$this->method = 'orders';
$this->type = $type;
// do logic
// ...
return $this;
}
public function invoices($type)
{
$this->method = 'invoices';
$this->type = $type;
// do logic
// ...
return $this;
}
}
$amazon = new Amazon();
echo $amazon->orders('New')->get();
echo $amazon->invoices('New')->get();
As orders and invoices are set methods, I would suggest to do as follows:
public function get(array $elements)
{
$result = [];
foreach($elements as $element) {
$result[$element] = $this->$element;
}
return $result;
}
So, you can call get method as:
$amazon = new Amazon();
$amazon->orders('New')->invoices('New')->get(['orders', 'invoices']);
** You need to validate the element's availability within the get method.

Cloning $this to do chaining. Is it a bad idea?

I am having the following class:
class StuffDoer{
public function __construct(Dep1 $dep, Dep2 $dep2, array $array){
$this->dep = $dep;
$this->dep2 = $dep2;
$this->array = $array;
}
public function genericDoStuff($param){
// Do stuff here...
}
public function doStuffForMark(){
return $this->genericDoStuff('Mark');
}
public function doStuffForTim(){
return $this->genericDoStuff('Tim');
}
public function doStuffForAlice(){
return $this->genericDoStuff('Alice');
}
}
After some months, I am asked to make the method genericDoStuff($param), along with all the methods that depend on it, use an extra parameter in a single part of the application. Instead of changing the signature on every single method that depends on genericDoStuff, I ended up with the following:
class StuffDoer{
public function __construct(Dep1 $dep, Dep2 $dep2, array $array){
$this->dep = $dep;
$this->dep2 = $dep2;
$this->array = $array;
}
public function forParameter($param){
$self = clone $this;
$this->param = $param;
return $self;
}
public function genericDoStuff($param){
if($this->param !== null){
// Do stuff by taking param into account
} else {
// Do stuff stuffdoer does
}
}
public function doStuffForMark(){
return $this->genericDoStuff('Mark');
}
public function doStuffForTim(){
return $this->genericDoStuff('Tim');
}
public function doStuffForAlice(){
return $this->genericDoStuff('Alice');
}
}
That way, I am able to do this in the single point of the application:
$myStuffDoer = $serviceContainer->get('stuff_doer');
$myStuffDoer->forParameter('AAAARGHITBURNSGODHELPME')->doStuffForMark();
// Future usages of $myStuffDoer are unaffected by this!
So my question is this: Is this considered a bad practice for any reason?

return data with chain static method

i'm trying to return data with chain static method , but i can't because the method return one thing only .
class Input
{
public static function set($input)
{
$data = $input;
$class = get_class();
return $data;
return self::$class = new $class;
}
public static function get()
{
echo ' - get method';
}
}
Input::set('ahmed')->get();
but it's only print " -get method "
I think you want
class Input
{
private static $data;
public static function set($input)
{
self::$data = $input;
return self;
}
public static function get()
{
echo self::$data.' - get method';
}
}
Input::set('ahmed')->get(); // ahmed - get method
but this you can use only once better is set name for value
class Input
{
private static $data = array();
public static function set($name, $input)
{
self::$data[$name] = $input;
return self;
}
public static function get($name)
{
echo self::$data[$name].' - get method';
}
}
Input::set('name', 'ahmed')->get('name'); // ahmed - get method

PHP Method Chaining: Calling one method before allowing other methods to be chained

Consider the following class
class myClass {
private $model;
public function update($input) {
return $this->model->update($input);
}
public function find($id) {
$this->model = ORMfind($id);
}
}
How do I prevent
$myClass = new myClass;
$myClass->update($input);
The problem isn't HOW to use the above code but how to make update() a method only callable after find().
EDIT: I changed what my method does so it was more clearly understood that I need to do one method (find()) before another (update())
You could add a flag to your code like so:
class myClass {
private $model;
private $canUpdate = 0;
public function update($input) {
if ($canUpdate === 0) return; // or throw an exception here
return $this->model->update($input);
}
public function find($id) {
$this->model = ORMfind($id);
$canUpdate = 1;
}
}
Setting the flag $canUpdate will caution the update() method to react accordingly. If update() is called, you can throw an exception or exit out of the method if the flag is still 0.
To prevent from returning null value by get :
public function get() {
if (isset($this->value)) return $this->value;
else echo "please give me a value ";
}
You can also create a construct:
function __construct($val){
$this->value=$val;
}
and then give a value to your $value without using set() method:
$myClass=new myClass(10);
Outputting text, returning void, I think all of this is wrong. When you do not expect something to happen, you should throw an exception:
class MyClass {
private $canUpdate = false;
public function find($id) {
// some code...
$this->canUpdate = true;
}
public function canUpdate() {
return $this->canUpdate;
}
private function testCanUpdate() {
if (!$this->canUpdate()) {
throw new Exception('You cannot update');
}
}
public function update($inpjut) {
$this->testCanUpdate();
// ... some code
}
}
Now you can do:
$obj = new MyClass();
try {
$obj->update($input);
} catch (Exception $e) {
$obj->find($id);
$obj->update($input);
}
The proper way to make sure ->update() can only be called when the model has been initialized is to turn it into a dependency:
class myClass
{
private $model;
public function __construct($id)
{
$this->model = ORMfind($id);
}
public function update($input) {
return $this->model->update($input);
}
}
$x = new myClass('123');
Alternatively, if you have multiple find operations, you could introduce them as static constructor methods:
class myClass
{
private $model;
private function __construct($model)
{
$this->model = $model;
}
public function update($input) {
return $this->model->update($input);
}
public static function find($id)
{
return new self(ORMfind($id));
}
}
$x = myClass::find('123');
Update
Tackling your immediate problem can be done by a simple check:
public function update($input) {
return $this->model ? $this->model->update($input) : null;
}

Categories