PHP OOP about reference - php

Can one please explain with example what does $obj->$a()->$b mean? I've used PHP OOP quite a long time and have seen in some places this structure and not just this $obj->$a(); In what cases should I use it?

$a = 'someMethod';
$b = 'someProperty';
$obj->$a()->$b;
is equal to:
$obj->someMethod()->someProperty;
Read more about variable variables

$a is the name to a method. Hence, if $a = "myFunc", this is equivalent to:
$obj->myFunc()->$b;
$b appears to be a reference to a property. The method appears to return an object, so, if $b = "myProp", we can modify this to be:
$obj->myFunc()->myprop;
This is really poor form for understandability.

It means that $a() returns an object, and that $b is a member of the object $a() returns.
This is called method chaining when each method returns the original object, so various methods of the same object can be called without having to repeatedly specify $obj-> before each invocation.

The actual term is Fluent Interface, as stated is returns the original object, heres a full example class
Class Fluent {
public $var1;
public $var2;
function setVar1($value) {
$this->var1 = $value;
return $this;
}
function setVar2($value) {
$this->var2 = $value;
return $this;
}
function getVars() {
echo $this->var1 . $this->var2;
}
}
$fluent = new Fluent();
$fluent->setVar1("Foo")->setVar2("Bar")->getVars();
Which will obviously return "FooBar".
HTH

Related

Calling member function on array

I don't truly understand how chaining functions work on the values that are returned.
Let's say I have a function that returns a string or array
public static $query;
public static function getArray($arr) {
Database::$query = $arr;
return Database::$query;
}
public function single() {
return Database::$query[0];
}
Why, when I call it can I then not chain a function onto this to affect the string (In this example I was to append ' test' and how would I go about doing this?
Why can I simply not call
Database::getArray(array("test","test2"))->single();
Without getting a Call to a member function single() on array error. But instead, make it return only the first value of the array.
How would I go append doing what I'm trying to achieve here? Why is my logic wrong?
When you call a method, the return value is whatever that method decides to return; the return value doesn't have any automatic relationship with the object you called the method on. For instance:
class A {
public function foo() {
return 'Hello, World!';
}
}
$a = new A;
echo $a->foo();
The value returned is just an ordinary string, just as if foo was a global function not attached to any object.
In PHP, strings (and other "basic types" like arrays) are not objects, so you can't call any methods on them. Even if you could, those methods would be built into the language, and you couldn't just decide that ->single() could be called on any array.
What may be confusing is that some people write methods with the convention that they return an object, known as a "fluent interface", or more generally "chained methods". This is not a feature of the language, just a natural consequence of returning an object from a method:
class A {
public function foo() {
return new B;
}
}
class B {
public function bar() {
return 'Hello, World!';
}
}
$a = new A;
$b = $a->foo(); // $b is a B object
echo $b->bar();
// We can combine this into one line:
echo (new A)->foo()->bar();
There is nothing special about this chaining; it's just that wherever you have an object, you can call appropriate methods on it, just as wherever you have a number, you can do maths with it. Compare with a simple addition:
function foo() {
return 1;
}
$a = foo();
$a = $a + 2;
echo $a;
// We can combine this into one line:
echo foo() + 2;
// Or keep the assignment:
$a = foo() + 2;
echo $a;
The object doesn't know it's being chained - in fact, it shouldn't need to know anything about the code around it, and that's an important part of structured programming.
A common pattern is then to have modifying methods which return the object they just modified, so you can make a series of modifications in one go:
class A {
private $words = [];
public function addWord($word) {
$this->words[] = $word;
// $this is the current object, which is an instance of class A
return $this;
}
public function getString() {
return implode(' ', $this->words);
}
}
$a = new A;
// Calling $a->addWord(...) gives us back the same object
$a = $a->addWord('Hello');
$a = $a->addWord('World');
// Calling $a->getString() gives us back a string
echo $a->getString();
// We can combine this into one line:
echo (new A)->addWord('Hello')->addWord('World')->getString();
Note that you can only refer to $this if you have created an instance of the object (with the new keyword), not in a method declared as static. A static method can still have this kind of pattern, but it will need to return some other object, like new self (a new instance of the current class) or self::$foo (an object created earlier).
it's called fluent interface, if you want to chain methods from same class you have to return this from each of them which you want to call fluently, so your code should look like:
public static $query;
public function getArray($arr) {
Database::$query = $arr;
return $this;
}
public function single() {
return Database::$query[0];
}
after applying changes, the construct Database::getArray(array("test","test2"))->single(); will work, however you may consider renaming method getArray, because as its name suggests, it shouldn't be returning $this, but array
#EDIT
you should change the type of function getArray from public static function to public function to make it work, also your final statement will change to something like:
(new Database())->getArray(array("test","test2"))->single();
however, in this case, I would consider redesigning your class and creating some kind of singleton so that you instantiate Database class only once and store the object somewhere

php chain method return variable and method calls another method

I saw different articles about chain method, but I still don't understand the difference between "return $this" and "return $this->SomeVariable".I also want to know how a method call another method within and without the class too.
Could someone kindly explain it ?
thanks you!
My example, it echo "bca", but I dont get why "a" is the last to display...
class validation {
public function __construct($a) {
$this->a = $a;
}
public function one($a) {
echo $a = "b";
return $this;
}
public function two($a) {
echo $a = "c";
return $this->a;
}
}
$a = "a";
$NameErr = new validation($a);
echo $NameErr->one($a)->two($a);
It is returned from two($a) since it returns $this->a that is set in constructor as "a", and method one($a) returns instance of the object on which is then called function two.
$this refers to the object instance. So difference is that return $this->SomeVariable it just returns the variable.
Also just a nice coding tip. Declare $a in class as private variable like this:
class Validation
{
private $a;
}
First let me say
$this refers to the class you are in.
This way of coding is called fluent interface. return $this returns the current object,
$NameErr->one($a)->two($a);
is same as
$NameErr->one($a);
$NameErr->two($a);
And in this case
First the method one() is called,
thus the value b is printed and the object of the class is returned.
Now method two() is called,
the value c is echoed out and the property is returned, which is echoed out side the class.
ps: Declaring the variable $a as private would be a nice practice.

How to call a method after another method in one line

I was looking to some php codes, and I saw an object that will call multiple methods in the same line.
I've tried to understand how to do it, and why we need to use it?
$object->foo("Text")->anotherFoo()->bar("Aloha")
What this styling called? and what is the best way to use it in php applications.
This syntax is called method chaining, and it's possible because each method returns the object itself ($this). This is not necessarily always the case, it's also used to retrieve an object's property that in turn also can be an object (which can have properties that are objects, and so on).
It is used to reduce the amount of lines that you need to write code on. Compare these two snippets:
Without chaining
$object->foo("Text");
$object->anotherFoo();
$object->->bar("Aloha");
Using method chaining
$object->foo("Text")->anotherFoo()->bar("Aloha");
this is used when the first function returns an object that will contains the second function that will return another object and so on...
class X
{
public function A()
{
echo "A";
}
public function B()
{
echo "B";
}
}
class Y
{
public function A()
{
echo "Y";
}
public function B()
{
return $this;
}
}
$y = new Y();
$y->B()->A();//this will run
$x = new X();
$x->A()->B();//this won't run, it will output "A" but then A->B(); is not valid

PHP object method doesn't behave as I expect

I can't quite understand why the output of this code is '1'.
My guess is that php is not behaving like most other OO languages that I'm used to, in that the arrays that php uses must not be objects. Changing the array that is returned by the class does not change the array within the class. How would I get the class to return an array which I can edit (and has the same address as the one within the class)?
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function getArr()
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = $t->getArr();
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
EDIT: I expected the output to be 0
You have to return the array by reference. That way, php returns a reference to the array, in stead of a copy.
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function & getArr() //Returning by reference here
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = &$t->getArr(); //Reference binding here
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
This returns 0.
From the returning references subpage:
Unlike parameter passing, here you have to use & in both places - to
indicate that you want to return by reference, not a copy, and to
indicate that reference binding, rather than usual assignment, should
be done
Note that although this gets the job done, question is if it is a good practice. By changing class members outside of the class itself, it can become very difficult to track the application.
Because array are passed by "copy on write" by default, getArr() should return by reference:
public function &getArr()
{
return $this->arr;
}
[snip]
$tobj_arr = &$t->getArr();
For arrays that are object, use ArrayObject. Extending ArrayObject is probably better in your case.
When you unset($tobj_arr[0]); you are passing the return value of the function call, and not the actual property of the object.
When you call the function again, you get a fresh copy of the object's property which has yet to be modified since you added 5 to it.
Since the property itself is public, try changing:
unset($tobj_arr[0]);
To: unset($t->arr[0]);
And see if that gives you the result you are looking for.
You are getting "1" because you are asking PHP how many elements are in the array by using count. Remove count and use print_r($tobj_arr_fresh)

member variable and member function have the same name

class test {
public $isNew;
public function isNew(){}
}
$ins = new test();
$ins->isNew
Here,how does php parse $ins->isNew?
PHP is not a functional programming language where functions are also data. So $ins->isNew wouldn’t be ambiguous to either refer to the method isNew or the attribute isNew. $ins->isNew is always an attribute and $ins->isNew() a method call.
$ins->isNew is the variable.
$ins->isNew() is the function.
See the chapter on Class Basic in the PHP manual:
$ins->isNew // class member
$ins->isNew() // class method
Used a, b, c for easier differentiation. As you can see, you call each of them in a different way, and that is how PHP knows which one you want. You can rename c() to a() or b(), but you cannot rename $b to $a, because multiple properties with the same variable names are not allowed.
class Class
{
public $a = true;
public $b = function () {return true;};
public function c() {return true;}
}
$class = new Class();
$class->a;
($class->b)();
$class->c();
That being said, I wouldn't name methods and properties with the same name, unless the method is the actual getter for the property.
class Class
{
private string $name;
public function name() {
// Null coalesce assignment operator is available since PHP 7.4
$this->name ??= $this->fetchNameFromSender();
$this->name ??= $this->fetchNameFromDocument();
$this->name ??= $this->fetchNameFromWhatever();
return $this->name ?? '[Could not fetch name]';
}
...
}
$class = new Class();
$class->name();
See the following section, Variable functions, in the PHP manual.
PHP supports the concept of variable functions. This means that if a variable name has parentheses appended to it, PHP will look for a function with the same name as whatever the variable evaluates to, and will attempt to execute it. Among other things, this can be used to implement callbacks, function tables, and so forth.

Categories