How to nested method in static method PHP [duplicate] - php
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.
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.
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 dynamic class methods - scope issue
Hello I'm trying to implement a url router in php something familiar to express.js Here is the code I have so far. class Router{ private $request; private $request_params; private $params_num; private $paths; public function __construct(){ $this->paths = array(); $this->request = strtolower($this->hookRequest()); if ($this->request != false){ $this->request_params = $this->hookRequestParams(); } else { $this->request_params = array('home'); } } public function __destruct(){ foreach($this->paths as $key => $value){ if($this->getRequest() == $key){ $value(); } } } public function get($path, $func){ $this->paths[$path] = $func; } private function hookRequest(){ return isset($_GET['req']) ? rtrim($_GET['req'], '/') : false; } private function hookRequestParams(){ $params = explode('/', $this->request); $this->params_num = count($params); return $params; } public function getRequest(){ return $this->request; } public function getRequestParams(){ return $this->request_params; } public function getPage(){ return $this->request_params[0]; } public function getAction(){ if($this->params_num > 1){ return $this->request_params[1]; } return false; } public function getActionParams(){ if($this->params_num > 2){ return $this->request_params[2]; } return false; } } This is used like this as you can imagine: $router = new Router(); $router->get('index', function(){ echo 'index'; //index is being shown to the browser as expectd echo $this->getPage(); // This does not work apparently }) My issue is how could I execute $router methods from within the anonymous function? As shown in this example with $this->getPAge();
Use a closure.. $router->get('index', function() use ($router) { echo 'index'; echo $router->getPage(); }) If you define your Closure within your class, $this should be work.
How can I implement Method Chaining in PHP 5.x?
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; }
get set properties in php
I'm from the C# environment and I'm starting to learn PHP in school. I'm used to set my properties in C# like this. public int ID { get; set; } What's the equivalent to this in php? Thanks.
There is none, although there are some proposals for implementing that in future versions. For now you unfortunately need to declare all getters and setters by hand. private $ID; public function setID($ID) { $this->ID = $ID; } public function getID() { return $this->ID; } for some magic (PHP likes magic), you can look up __set and __get magic methods. Example class MyClass { private $ID; private function setID($ID) { $this->ID = $ID; } private function getID() { return $this->ID; } public function __set($name,$value) { switch($name) { //this is kind of silly example, bt shows the idea case 'ID': return $this->setID($value); } } public function __get($name) { switch($name) { case 'ID': return $this->getID(); } } } $object = new MyClass(); $object->ID = 'foo'; //setID('foo') will be called
Thanks for your answers everyone. It helped me to create something like this: In my parent class: public function __get($name){ if (ObjectHelper::existsMethod($this,$name)){ return $this->$name(); } return null; } public function __set($name, $value){ if (ObjectHelper::existsMethod($this,$name)) $this->$name($value); } ObjectHelper::existsMethod is a method which just check if given protected method exists. private $_propertyName = null; protected function PropertyName($value = ""){ if (empty($value)) // getter { if ($this-> _propertyName != null) return $this->_propertyName; } else // setter { $this-> _propertyName = $value; } return null; } So I can use something like this in any class: $class = new Class(); $class->PropertyName = "test"; echo $class->PropertyName; I was inspired by C# :) What do you think about this, guys? Here is my ObjectHelper if someone would like to use it: namespace Helpers; use ReflectionMethod; class ObjectHelper { public static function existsMethod($obj, $methodName){ $methods = self::getMethods($obj); $neededObject = array_filter( $methods, function ($e) use($methodName) { return $e->Name == $methodName; } ); if (is_array($neededObject)) return true; return false; } public static function getMethods($obj){ $var = new \ReflectionClass($obj); return $var->getMethods(ReflectionMethod::IS_PROTECTED); } }
Mchi is right, but there is another way of doing it by using single function private $ID; public function ID( $value = "" ) { if( empty( $value ) ) return $this->ID; else $this->ID = $value; } But yeah this approach is pretty much inline with what you do in c#. but this is only an alternative Or try using php's __set and __get in your class more info here http://php.net/manual/en/language.oop5.overloading.php
Another exampled using Variable function name class MyClass { private $ID; protected $ID2; private function setID($ID) { $this->ID = $ID; } private function getID() { return $this->ID; } private function setID2($ID2) { $this->ID2 = $ID2; } private function getID2() { return $this->ID2; } public function __set($name,$value) { $functionname='set'.$name; return $this->$functionname($value); } public function __get($name) { $functionname='get'.$name; return $this->$functionname(); } } $object = new MyClass(); $object->ID = 'foo'; //setID('foo') will be called $object->ID2 = 'bar'; //setID2('bar') will be called
private $ID; public function getsetID($value = NULL) { if ($value === NULL) { return $this->ID; } else { $this->ID = $value; } }
I know I am a bit late to the party on this question, but I had the same question/thought myself. As a C# developer who does PHP, when the job requires, I want to have a simple way to create properties just I would be able to in C#. I whipped up a first draft this afternoon which allows you to create the backing fields and specify their accessors or have pure accessors with no backing field. I will update my answer as the code evolves and provide a link when I get it to the state where it can be imported as a composer package. For simplicity, I created the functionality as a PHP trait so you can drop it in to any class you want instead of having to extend a base class. Eventually I hope to extend this functionality to discern between external public calls to the properties and protected/private calls. Here is the code for the trait itself: trait PropertyAccessorTrait { private static $__propertyAccessors = []; /* #property string $__propertyPrefix */ public function __get($name) { $this->__populatePropertyAcessors($name); return $this->__performGet($name); } public function __set($name, $value) { $this->__populatePropertyAcessors($name); $this->__performSet($name, $value); } public function __isset($name) { // TODO: Implement __isset() method. } public function __unset($name) { // TODO: Implement __unset() method. } protected function __getBackingFieldName($name) { if (property_exists(self::class, '__propertyPrefix')) { $prefix = $this->__propertyPrefix; } else { $prefix = ''; } return $prefix . $name; } protected function __canget($name) { $accessors = $this->__getPropertyAccessors($name); return $accessors !== null && isset($accessors['get']); } protected function __canset($name) { $accessors = $this->__getPropertyAccessors($name); return $accessors !== null && isset($accessors['set']); } protected function __performGet($name) { if (!$this->__canget($name)) { throw new \Exception('Getter not allowed for property: ' . $name); } $accessors = $this->__getPropertyAccessors($name)['get']; /* #var \ReflectionMethod $method */ $method = $accessors['method']; if (!empty($method)) { return $method->invoke($this); } return $this->{$this->__getBackingFieldName($name)}; } protected function __performSet($name, $value) { if (!$this->__canset($name)) { throw new \Exception('Setter not allowed for property: ' . $name); } $accessors = $this->__getPropertyAccessors($name)['set']; /* #var \ReflectionMethod $method */ $method = $accessors['method']; if (!empty($method)) { return $method->invoke($this, $value); } $this->{$this->__getBackingFieldName($name)} = $value; } protected function __getPropertyAccessors($name) { return isset(self::$__propertyAccessors[$name]) ? self::$__propertyAccessors[$name] : null ; } protected function __getAccessorsFromDocBlock($docblock) { $accessors = []; if (!empty(trim($docblock))) { $doclines = null; if (!empty($docblock)) { $doclines = explode("\n", $docblock); } if (!empty($doclines)) { foreach ($doclines as $line) { if (preg_match('/#(get|set)\\s+(public|private|protected)/', $line, $matches)) { $accessors[$matches[1]]['visibility'] = $matches[2]; } } } } return $accessors; } protected function __populatePropertyAcessors($name) { if ($this->__getPropertyAccessors($name) !== null) return; try { $property = new \ReflectionProperty(self::class, $this->__getBackingFieldName($name)); } catch (\ReflectionException $ex) { $property = null; } $accessors = []; if ($property != null) { $accessors = $this->__getAccessorsFromDocBlock($property->getDocComment()); } try { $methodName = 'get' . ucfirst($name); $method = new \ReflectionMethod(self::class, $methodName); $method->setAccessible(true); $accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment())); } catch (\ReflectionException $ex) { $method = null; } if ($method !== null || isset($accessors['get'])) { $accessors['get']['method'] = $method; } try { $methodName = 'set' . ucfirst($name); $method = new \ReflectionMethod(self::class, $methodName); $method->setAccessible(true); $accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment())); } catch (\ReflectionException $ex) { $method = null; } if ($method !== null || isset($accessors['set'])) { $accessors['set']['method'] = $method; } self::$__propertyAccessors[$name] = $accessors; } } Here is a quick unit test I created using the Codeception format: <?php class PropertyAssesorTraitTestClass { use PropertyAccessorTrait; private $__propertyPrefix = '_'; /** * #get public * #set public */ private $_integer = 1; /** * #get public */ private $_getonly = 100; /** * #set public */ private $_setonly; private $_customDoubler; private function getCustomDoubler() { return $this->_customDoubler * 2; } private function setCustomDoubler($value) { $this->_customDoubler = $value * 2; } public $publicField = 1234; /** * #return int * #get public */ private function getPureAccessor() { return $this->publicField; } /** * #param $value * #set public */ private function setPureAccessor($value) { $this->publicField = $value; } private $_purePrivate = 256; } $I = new UnitTester($scenario); $I->wantTo('Ensure properties are accessed correctly'); $instance = new PropertyAssesorTraitTestClass(); $I->assertSame(1, $instance->integer); $instance->integer = 2; $I->assertSame(2, $instance->integer); $instance->integer = $instance->integer + 1; $I->assertSame(3, $instance->integer); $instance->integer++; $I->assertSame(4, $instance->integer); $I->assertSame(100, $instance->getonly); $I->expectException('Exception', function () use ($instance) { $instance->getonly = 50; }); $instance->setonly = 50; $I->expectException('Exception', function () use ($instance) { $a = $instance->setonly; }); $instance->customDoubler = 100; $I->assertSame(400, $instance->customDoubler); $I->assertSame(1234, $instance->publicField); $instance->pureAccessor = 1000; $I->assertSame(1000, $instance->publicField); $instance->publicField = 1234; $I->assertSame(1234, $instance->publicField); $I->assertSame(1234, $instance->pureAccessor); $I->expectException('Exception', function () use ($instance) { return $instance->purePrivate; });
I like to use this pattern: class foo { //just add p as prefix to be different than method name. protected $pData; public funtion __construct() {} public funtion __destruct() {} public funtion __clone() {} public function Data($value == "") { if ($value != "") { $this->pData = $value; } return $this->pData; } } $myVar = new foo(); //for SET $myVar->Data("A Value"); //for GET $item = $myVar->Data();
class MyClass { private $name = null; public function __construct($name = null) { $this->name = $name; } public function __set($name, $value) { if (property_exists($this, $name)) { $this->name = $value; } return $this; } public function __get($name) { if (property_exists($this, $name)) { return $this->$name; } return null; } }
this is PHP ; you don't need get set class MyClass { public $ID; } $object = new MyClass(); $object->ID = 'foo'; echo $object->ID; will work