PHP clone keyword [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
what is Object Cloning in php?
I am studying a existing framework which uses a "clone" keyword a lot, not sure whether this is a good idea to do this ? i dont really understand the need to use the 'clone' keyword.
for example have look at this coding
i.e
public function getStartDate ()
{
return clone $this->startDate;
}
to me this function should be like below, i dont see the need of the clone.
public function getStartDate ()
{
return $this->startDate;
}

Reason for using clone is that PHP when working with object always returns object as a reference, not as a copy.
That is why when passing object to a function you don't need to specify it with & (reference):
function doSomethingWithObject(MyObject $object) { // it is same as MyObject &object
...
}
So in order to get object copy you have to use clone keyword
This is an example on how objects are handled by php and what clone does:
class Obj {
public $obj;
public function __construct() {
$this->obj = new stdClass();
$this->obj->prop = 1; // set a public property
}
function getObj(){
return $this->obj; // it returns a reference
}
}
$obj = new Obj();
$a = $obj->obj; // get as public property (it is reference)
$b = $obj->getObj(); // get as return of method (it is also a reference)
$b->prop = 7;
var_dump($a === $b); // (boolean) true
var_dump($a->prop, $b->prop, $obj->obj->prop); // int(7), int(7), int(7)
// changing $b->prop didn't actually change other two object, since both $a and $b are just references to $obj->obj
$c = clone $a;
$c->prop = -3;
var_dump($a === $c); // (boolean) false
var_dump($a->prop, $c->prop, $obj->obj->prop); // int(7), int(-3), int(7)
// since $c is completely new copy of object $obj->obj and not a reference to it, changing prop value in $c does not affect $a, $b nor $obj->obj!

Perhaps startDate is an object.
Then. When you return clone $this->startDate - you get a full copy of the object. You can play with it, change values, call functions. And, until they affect database or filesystem - it's safe and the actual startDate object will not be modified.
But, if you just return object as is - you return only a reference. And any operation with object you do - you do this operation with the original object. Any change you make - it will affect that startDate.
This is only for objects and doesn't affect arrays, strings and numbers as they are value-type variables.
You should read more about value-type variables and reference-type variables.

despite it is perfectley explained in another question (thanks for pointing this #gerald)
just a quick answer:
without a clone the function is returning a reference to the startDate Object. With clone its returning a copy.
if the returned object would be changed later, it only changes the copy and not the original, which might be used somewhere else also.

Related

Class-Wide accessible static array, trying to push to it, but keeps coming back empty

class UpcomingEvents {
//Variable I'm trying to make accessible and modify throughout the class methods
private static $postObjArr = array();
private static $postIdArr = array();
private static $pinnedPost;
//My attempt at a get method to solve this issue, it did not
private static function getPostObjArr() {
$postObjArr = static::$postObjArr;
return $postObjArr;
}
private static function sortByDateProp($a, $b) {
$Adate = strtotime(get_field('event_date',$a->ID));
$Bdate = strtotime(get_field('event_date',$b->ID));
if ($Adate == $Bdate) {
return 0;
}
return ($Adate < $Bdate) ? -1 : 1;
}
private static function queryDatesAndSort($args) {
$postQuery = new WP_Query( $args );
if( $postQuery->have_posts() ) {
while( $postQuery->have_posts() ) {
$postQuery->the_post();
//Trying to push to the array, to no avail
array_push(static::getPostObjArr(), get_post());
}
}
//Trying to return the array after pushing to it, comes back empty
return(var_dump(static::getPostObjArr()));
//Trying to sort it
usort(static::getPostObjArr(), array(self,'sortByDateProp'));
foreach (static::getPostObjArr() as $key => $value) {
array_push(static::$postIdArr, $value->ID);
}
}
}
I'm trying to access $postObjArr within the class, and push to it with the queryDatesAndSort(); method. I've tried a couple of things, most recent being to use a get method for the variable. I don't want to make it global as it's bad practice I've heard. I've also tried passing by reference I.E
&static::$postObjArr;
But when it hits the vardump, it spits out an empty array. What would be the solution and best practice here? To allow the class methods to access and modify a single static array variable.
static::$postObjArr[] = get_post()
I didn't think it would of made a difference, but it worked. Can you explain to me why that worked but array.push(); Did not?
Arrays are always copy-on-write in PHP. If you assign an array to another variable, pass it into a function, or return it from a function, it's for all intents and purposes a different, new array. Modifying it does not modify the "original" array. If you want to pass an array around and continue to modify the original array, you'll have to use pass-by-reference everywhere. Meaning you will have to add a & everywhere you assign it to a different variable, pass it into a function, or return it from a function. If you forget your & anywhere, the reference is broken.
Since that's rather annoying to work with, you rarely use references in PHP and you either modify your arrays directly (static::$postObjArr), or you use objects (stdClass or a custom class) instead which can be passed around without breaking reference.

Is is possible to store a reference to an object method?

Assume this class code:
class Foo {
function method() {
echo 'works';
}
}
Is there any way to store a reference to the method method of a Foo instance?
I'm just experimenting and fiddling around, my goal is checking whether PHP allows to call $FooInstance->method() without writing $FooInstance-> every time. I know I could write a function wrapper for this, but I'm more interested in getting a reference to the instance method.
For example, this pseudo-code would theoretically store $foo->method in the $method variable:
$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method
$method();
Apparently, as method is a method and I'm not calling it with () the interpreter thinks I'm looking for a property thus this doesn't work.
I've read through Returning References but the examples only show how to return references to variables, not methods.
Therefore, I've adapted my code to store an anonymous function in a variable and return it:
class Foo {
function &method() {
$fn = function() {
echo 'works';
};
return $fn;
}
}
$foo = new Foo();
$method = &$foo->method();
$method();
This works, but is rather ugly. Also, there's no neat way to call it a single time, as this seems to require storing the returned function in a variable prior to calling it: $foo->method()(); and ($foo->method())(); are syntax errors.
Also, I've tried returning the anonymous function directly without storing it in a variable, but then I get the following notice:
Notice: Only variable references should be returned by reference
Does this mean that returning/storing a reference to a class instance method is impossible/discouraged or am I overlooking something?
Update: I don't mind adding a getter if necessary, the goal is just getting a reference to the method. I've even tried:
class Foo {
var $fn = function() {
echo 'works';
};
function &method() {
return $this->fn;
}
}
But from the unexpected 'function' (T_FUNCTION) error I'd believe that PHP wisely doesn't allow properties to store functions.
I'm starting to believe that my goal isn't easily achievable without the use of ugly hacks as eval().
It is. You have to use an array, with two values: the class instance (or string of the class name if you are calling a static method) and the method name as a string. This is documented on the Callbacks Man page:
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Demo (Codepad):
<?php
class Something {
public function abc() {
echo 'called';
}
}
$some = new Something;
$meth = array($some, 'abc');
$meth(); // 'called'
Note this is also works with the built-ins that require callbacks (Codepad):
class Filter {
public function doFilter($value) {
return $value !== 3;
}
}
$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'
And for static methods -- note the 'Filter' instead of an instance of a class as the first element in the array (Codepad):
class Filter {
public static function doFilter($value) {
return $value !== 3;
}
}
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3
Yes, you can. PHP has a "callable" pseudo-type, which is, in fact, either just a string or an array. Several functions (usort comes to mind) accept a parameter of the "callback" type: in fact, they just want a function name, or an object-method pair.
That's right, strings are callable:
$fn = "strlen";
$fn("string"); // returns 6
As mentioned, it's possible to use an array as a callback, too. In that case, the first element has to be an object, and the second argument must be a method name:
$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()
Previously, you had to use call_user_func to call them, but syntax sugar in recent versions make it possible to perform the call straight on variables.
You can read more on the "callable" documentation page.
No, as far as I know it's not possible to store a reference to a method in PHP. Storing object / class name and a method name in an array works, but it's just an array without any special meaning. You can play with the array as you please, for example:
$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1;
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
this is prone to error as you loose syntax checking due to storing the method name as string.
you can't pass reliably a reference to a private or a protected method this way (unless you call the reference from a context that already has proper access to the method).
Personally I prefer to use lambdas:
$ref = function() use($my_object) { $my_object->x(); }
If you do this from inside $my_object it gets less clunky thanks to access to $this:
$ref = function() { $this->x(); }
this works with protected / private methods
syntax checking works in IDE (less bugs)
unfortunately it's less concise

Function chaining with different return values?

Is it possible to have a method return different values depending on context (how the return value is used)? For example, could a method return $this when it's then used with the arrow operator to call another method (i.e. chaining method calls), but return a scalar when the return value isn't used this way?
Case 1:
$result = $test->doSomething1(); // returns 4
// $result returns 4
Case 2:
$result = $test->doSomething1()->doSomething2();
// doSomething1() returns $this
// doSomething2() returns 8
Is there anyway to perform such a behaviour?
If I understand the question correctly, you want a method (doSomething1) to return a value based on what the rest of the call chain looks like. Unfortunately, there is absolutely no way you can do this.
Common programming paradigms shared across "all" languages (how methods, operators and such work in the context of the grammar) dictate that the result of the expression $this->doSomething1() has to be worked out before the result of possibly calling ->doSomething2() on it can be considered. Statically typed and dynamically typed languages do this in different ways, but the common factor is that the expression $this->doSomething1() has to be considered independently of what follows or does not follow.
In a nutshell: $this->doSomething1() has to return a specific type of value in both cases. And in PHP there is no way to have a type of value that can behave like a number in one context and like an object with methods to call in another.
No it's not possible to have a function return different values depending on if another function is called on the returned value or not. You can emulate this with toString() (where conversion to string is applicable, or another function that you call at the end of every chain to get the value instead of the object:
$test = new foo();
echo $test->doSomething1(); // Outputs 1
$test = new foo();
echo $test->doSomething1()->doSomething2(); // Outputs 3
$test = new foo();
$result = $test->doSomething1()->done(); // $result === 1
$test = new foo();
$result = $test->doSomething1()->doSomething2()->done(); // $result === 3
class foo {
private $val;
function __construct($val = 0){
$this->val = $val;
}
function doSomething1(){
$this->val += 1;
return $this;
}
function doSomething2(){
$this->val += 2;
return $this;
}
function done(){
return $this->val;
}
function __toString(){
return (string)$this->val;
}
}
Codepad Example
This answer suggests creating an internal call stack on the class, so you can keep track of where you are on the method chain. This could be used to return $this or something else, depending on the context.

Method chaining without final method

I came across plenty of examples of method chaining in PHP, but couldn't find anything about this one, so I'm asking for help you guys;)
My problem is - can I in some way find out if the method in chain is the last one? In most cases people are using some sort of final method (execute, send,..) to tell when the chain ends and return the corresponding result. But I wonder if there is some hidden magic method or technique than can check all the methods in chain and detect if there is no next method?
Without final method it works fine for strings (in the very simple example), but not if I want to return array.
Here is my snippet :
class Chain {
private $strArray;
function __call($name, $args) {
$this->strArray[] = $args[0];
return $this;
}
function __toString() {
return implode('-', $this->strArray);
}
}
// example 1
$c = new Chain();
$x = $c->foo('hi')->bar('stack'); // need array('hi', 'stack')
// example 2
$c = new Chain();
$x = $c->foo('hi')->bar('stack')->foobar('overflow'); // need array('hi', 'stack', 'overflow')
// example 3
$c = new Chain();
echo $c->foo('hi')->foobar('overflow'); // prints 'hi-overflow'
// example 4
$c = new Chain();
echo $c->foo('hi')->bar('stack')->foobar('overflow'); // prints 'hi-stack-overflow'
You see, when I want to print the result of chain, I can modify the result in the __toString method, but what if I need an array (example 1, 2)? Is there any way to achieve that without calling some additional "final" method?
Thanks a lot for help and let me know if you need more info.
EDIT: After feedback from #bandi I tried to extend ArrayObject like this.
class Chain extends ArrayObject {
function __call($name, $args) {
$this->append($args[0]);
return $this;
}
function __toString() {
return implode('-', $this->getIterator()->getArrayCopy());
}
}
// returned ref. to object, works fine in loop or accessing offset
$obj = new Chain;
$x = $obj->foo('hi')->bar('stack')->foobar('overflow'); // need array('hi', 'stack', 'overflow')
foreach ($x as $y) {
echo $y, "\n";
}
var_dump($x[0], $x[1], $x[2]);
// returned String
$c = new Chain;
echo $c->foo('hi')->foobar('overflow'); // prints 'hi-overflow'
It does what I wanted, however I don't feel so good about the $this->getIterator()->getArrayCopy() part. Is there some simple way of accessing the array (internally in ["storage":"ArrayObject":private])?
Thanks
Method chaining is using the return value of a function, in this case this is the reference to the object. Predicting the use of a returned value is generally not possible.
You have to tell the called method that you want to do something different. You can use the second argument for this, e.g. you return a different result if the second argument is defined. The other option might be to modify the class so that it behaves like an array except if it is printed.

Object to return property?

I have a class called Type:
class Type { ... }
Which has a property named $value:
class Type {
protected $value;
//More code here...
}
I wish that when I attempt to use a function, when I pass the object, the value of $obj->value will be passed. For example:
$obj = new Type("value");
echo $obj; //Desired output: value
I've tried many things, and searched everywhere, but I can't seem to find this one. Is it even possible?
Thanks in advance. :)
EDIT: Maybe you misunderstood (or narrowed) my question a bit. I want it for all value types, not just the string ones, including int float and boolean
$obj = new Type(true);
echo !$obj; //Desired output: false
$obj2 = new Type(9);
echo ($obj + 1); //Desired output: 10
See: Magic Methods
class Type {
public function __toString() {
return (string)$this->value;
}
}
If your usage of the object won't trigger the magic you can allways use:
$someVar = (string)$obj;
Update:
Beyond strings or arrays (see ArrayAccess interface), it's, at the moment, not possible to have php handle objects like predefined data types.

Categories