Objects comparison in PHP - php

SO,
The problem
It's not well-known, but PHP allows to compare objects - and not just on equality == - but on < and > too. But - how it works? So if I want to create comparable objects - what restrictions/rules they should follow?
Most useful case is with DateTime() objects - they hold certain timestamp and they could be compared (and this has logical sense). On lxr there's some explanation for DateTime . But what about common case?
I have:
class C
{
protected $holder;
protected $mirror;
public function __construct($h = null)
{
$this->holder=$h;
$this->mirror=-1*$h;
}
}
$one = new C(1);
$two = new C(2);
//false, false, true: used $holder
var_dump($one>$two, $one==$two, $one<$two);
-if I'll change properties declaration order, it will use $mirror:
class C
{
//only order changed:
protected $mirror;
protected $holder;
public function __construct($h = null)
{
$this->holder=$h;
$this->mirror=-1*$h;
}
}
$one = new C(1);
$two = new C(2);
//true, false, false: used $mirror
var_dump($one>$two, $one==$two, $one<$two);
So it seems one of the 'rules' is that it will use first declared property. But why is it using protected property at all is not clear to me too.
Now, more complex sample:
class Test
{
protected $a;
protected $b;
function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
}
$x = new Test(1, 2);
$y = new Test(1, 3);
// true, false, false
var_dump($x < $y, $x == $y, $x > $y);
$x = new Test(3, 1);
$y = new Test(2, 1);
// false, false, true
var_dump($x < $y, $x == $y, $x > $y);
-so it will use first not-equal property for comparison. But code snippets above are only some cases. I want to know exactly how it's happening and why. Thus,
Question
Is: how it works? I mean, more detailed:
Can I rely on fact, that PHP will use first not-equal property for comparison?
What will be done if count of properties isn't equal? (i.e. some property was dynamically added to instance during code execution)
Can I treat protected/private properties as to be counted for such comparison always?
e.t.c. - so if there are some additional conditions/restrictions/rules that will affect result - please, post. Documentation states only for ==/=== comparison. Also, comparison of instances of different classes is out of the issue since it'll return false (obviously).

PHP compares sequentially (in the order of declaration) the object properties and stops at the first inequal property found. This behavior is not documented, so there's not much to be said about it, sadly, other than looking at the source of PHP.
Not documented is usually a synonym of "don't rely on it".

Each class in php has an associated structure (in the c code) of handler functions, it looks like
struct _zend_object_handlers {
/* general object functions */
zend_object_add_ref_t add_ref;
zend_object_del_ref_t del_ref;
[...]
zend_object_compare_t compare_objects;
[...]
};
compare_objects points to a function that "takes two objects" and returns -1,0,1 according to whatever this comparator defines as the order (just like strcmp() does for strings).
This function is used only when both operands (objects) point to the same comparision function - but let's just stick with this case.
That's where e.g. DateTime "adds" its feature to compare two DateTime instances, it just defines another, DateTime-specific compare_objects function and puts it in the structure describing its class.
static void date_register_classes(TSRMLS_D)
{
[...]
INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date);
ce_date.create_object = date_object_new_date;
[...]
date_object_handlers_date.compare_objects = date_object_compare_date;
So if you want to know (exactly) how two DateTime instances are compared, take a look at date_object_compare_date.
The comparision described in the manual (at least for the case cmp(o1,o2)==0) seems to be implemented in zend_std_compare_objects. And it's used by both StdClass and a simple user defined class like e.g.
<?php
class Foo { }
$a = new StdClass;
$b = new Foo;
$a > $b;
But other classes (in php extensions) do set other functions. DateTime, ArrayObject, PDOStatement, even Closures use different functions.
But I haven't found a way to define a comparision function/method in script code (but haven't looked too hard/long)

The exact behavior is defined in the PHP language specification, thus, you can rely on it.
[…] if the objects are of different types, the comparison result is FALSE. If the objects are of the same type, the properties of the objects are compares [sic] using the array comparison described above.
And the array comparison is defined as follows:
[…] For arrays having the same numbers of elements, the keys from the left operand are considered one by one, if the next key in the left-hand operand exists in the right-hand operand, the corresponding values are compared. If they are unequal, the array containing the lesser value is considered less-than the other one, and the comparison ends; otherwise, the process is repeated with the next element. […] If all the values are equal, then the arrays are considered equal.
Simply exchange every mention of array with object and key with property in your mind and you have the exact description of how this works. I omitted useless array specifics in above quote.

Related

Performing an asort on a collection of objects [duplicate]

SO,
The problem
It's not well-known, but PHP allows to compare objects - and not just on equality == - but on < and > too. But - how it works? So if I want to create comparable objects - what restrictions/rules they should follow?
Most useful case is with DateTime() objects - they hold certain timestamp and they could be compared (and this has logical sense). On lxr there's some explanation for DateTime . But what about common case?
I have:
class C
{
protected $holder;
protected $mirror;
public function __construct($h = null)
{
$this->holder=$h;
$this->mirror=-1*$h;
}
}
$one = new C(1);
$two = new C(2);
//false, false, true: used $holder
var_dump($one>$two, $one==$two, $one<$two);
-if I'll change properties declaration order, it will use $mirror:
class C
{
//only order changed:
protected $mirror;
protected $holder;
public function __construct($h = null)
{
$this->holder=$h;
$this->mirror=-1*$h;
}
}
$one = new C(1);
$two = new C(2);
//true, false, false: used $mirror
var_dump($one>$two, $one==$two, $one<$two);
So it seems one of the 'rules' is that it will use first declared property. But why is it using protected property at all is not clear to me too.
Now, more complex sample:
class Test
{
protected $a;
protected $b;
function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
}
$x = new Test(1, 2);
$y = new Test(1, 3);
// true, false, false
var_dump($x < $y, $x == $y, $x > $y);
$x = new Test(3, 1);
$y = new Test(2, 1);
// false, false, true
var_dump($x < $y, $x == $y, $x > $y);
-so it will use first not-equal property for comparison. But code snippets above are only some cases. I want to know exactly how it's happening and why. Thus,
Question
Is: how it works? I mean, more detailed:
Can I rely on fact, that PHP will use first not-equal property for comparison?
What will be done if count of properties isn't equal? (i.e. some property was dynamically added to instance during code execution)
Can I treat protected/private properties as to be counted for such comparison always?
e.t.c. - so if there are some additional conditions/restrictions/rules that will affect result - please, post. Documentation states only for ==/=== comparison. Also, comparison of instances of different classes is out of the issue since it'll return false (obviously).
PHP compares sequentially (in the order of declaration) the object properties and stops at the first inequal property found. This behavior is not documented, so there's not much to be said about it, sadly, other than looking at the source of PHP.
Not documented is usually a synonym of "don't rely on it".
Each class in php has an associated structure (in the c code) of handler functions, it looks like
struct _zend_object_handlers {
/* general object functions */
zend_object_add_ref_t add_ref;
zend_object_del_ref_t del_ref;
[...]
zend_object_compare_t compare_objects;
[...]
};
compare_objects points to a function that "takes two objects" and returns -1,0,1 according to whatever this comparator defines as the order (just like strcmp() does for strings).
This function is used only when both operands (objects) point to the same comparision function - but let's just stick with this case.
That's where e.g. DateTime "adds" its feature to compare two DateTime instances, it just defines another, DateTime-specific compare_objects function and puts it in the structure describing its class.
static void date_register_classes(TSRMLS_D)
{
[...]
INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date);
ce_date.create_object = date_object_new_date;
[...]
date_object_handlers_date.compare_objects = date_object_compare_date;
So if you want to know (exactly) how two DateTime instances are compared, take a look at date_object_compare_date.
The comparision described in the manual (at least for the case cmp(o1,o2)==0) seems to be implemented in zend_std_compare_objects. And it's used by both StdClass and a simple user defined class like e.g.
<?php
class Foo { }
$a = new StdClass;
$b = new Foo;
$a > $b;
But other classes (in php extensions) do set other functions. DateTime, ArrayObject, PDOStatement, even Closures use different functions.
But I haven't found a way to define a comparision function/method in script code (but haven't looked too hard/long)
The exact behavior is defined in the PHP language specification, thus, you can rely on it.
[…] if the objects are of different types, the comparison result is FALSE. If the objects are of the same type, the properties of the objects are compares [sic] using the array comparison described above.
And the array comparison is defined as follows:
[…] For arrays having the same numbers of elements, the keys from the left operand are considered one by one, if the next key in the left-hand operand exists in the right-hand operand, the corresponding values are compared. If they are unequal, the array containing the lesser value is considered less-than the other one, and the comparison ends; otherwise, the process is repeated with the next element. […] If all the values are equal, then the arrays are considered equal.
Simply exchange every mention of array with object and key with property in your mind and you have the exact description of how this works. I omitted useless array specifics in above quote.

PHP clone keyword [duplicate]

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.

Peculiar Behaviour with PHP (5.3), static inheritance and references

I'm writing a library in PHP 5.3, the bulk of which is a class with several static properties that is extended from by subclasses to allow zero-conf for child classes.
Anyway, here's a sample to illustrate the peculiarity I have found:
<?php
class A {
protected static $a;
public static function out() { var_dump(static::$a); }
public static function setup($v) { static::$a =& $v; }
}
class B extends A {}
class C extends A {}
A::setup('A');
A::out(); // 'A'
B::out(); // null
C::out(); // null
B::setup('B');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // null
C::setup('C');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // 'C'
?>
Now, this is pretty much desired behaviour for static inheritance as far as I'm concerned, however, changing static::$a =& $v; to static::$a = $v; (no reference) you get the behaviour I expected, that is:
'A'
'A'
'A'
'B'
'B'
'B'
'C'
'C'
'C'
Can anyone explain why this is? I can't understand how references effect static inheritance in any way :/
Update:
Based on Artefacto's answer, having the following method in the base class (in this instance, A) and calling it after the class declarations produces the behaviour labelled as 'desired' above without the need to assign by reference in setters, whilst leaving the results when using self:: as the 'expected' behaviour above.
/*...*/
public static function break_static_references() {
$self = new ReflectionClass(get_called_class());
foreach($self->getStaticProperties() as $var => $val)
static::$$var =& $val;
}
/*...*/
A::break_static_references();
B::break_static_references();
C::break_static_references();
/*...*/
TL;DR version
The static property $a is a different symbol in each one of the classes, but it's actually the same variable in the sense that in $a = 1; $b = &$a;, $a and $b are the same variable (i.e., they're on the same reference set). When making a simple assignment ($b = $v;), the value of both symbols will change; when making an assignment by reference ($b = &$v;), only $b will be affected.
Original version
First thing, let's understand how static properties are 'inherited'. zend_do_inheritance iterates the superclass static properties calling inherit_static_prop:
zend_hash_apply_with_arguments(&parent_ce->default_static_members TSRMLS_CC,
(apply_func_args_t)inherit_static_prop, 1, &ce->default_static_members);
The definition of which is:
static int inherit_static_prop(zval **p TSRMLS_DC, int num_args,
va_list args, const zend_hash_key *key)
{
HashTable *target = va_arg(args, HashTable*);
if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) {
SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p,
sizeof(zval*), NULL) == SUCCESS) {
Z_ADDREF_PP(p);
}
}
return ZEND_HASH_APPLY_KEEP;
}
Let's translate this. PHP uses copy on write, which means it will try to share the same actual memory representation (zval) of the values if they have the same content. inherit_static_prop is called for each one of the superclass static properties so that can be copied to the subclass. The implementation of inherit_static_prop ensures that the static properties of the subclass will be PHP references, whether or not the zval of the parent is shared (in particular, if the superclass has a reference, the child will share the zval, if it doesn't, the zval will be copied and new zval will be made a reference; the second case doesn't really interest us here).
So basically, when A, B and C are formed, $a will be a different symbol for each of those classes (i.e., each class has its properties hash table and each hash table has its own entry for $a), BUT the underlying zval will be the same AND it will be a reference.
You have something like:
A::$a -> zval_1 (ref, reference count 3);
B::$a -> zval_1 (ref, reference count 3);
C::$a -> zval_1 (ref, reference count 3);
Therefore, when you do a normal assignment
static::$a = $v;
since all three variables share the same zval and its a reference, all three variables will assume the value $v. It would be the same if you did:
$a = 1;
$b = &$a;
$a = 2; //both $a and $b are now 1
On the other hand, when you do
static::$a =& $v;
you will be breaking the reference set. Let's say you do it in class A. You now have:
//reference count is 2 and ref flag is set, but as soon as
//$v goes out of scope, reference count will be 1 and
//the reference flag will be cleared
A::$a -> zval_2 (ref, reference count 2);
B::$a -> zval_1 (ref, reference count 2);
C::$a -> zval_1 (ref, reference count 2);
The analogous would be
$a = 1;
$b = &$a;
$v = 3;
$b = &$v; //$a is 1, $b is 3
Work-around
As featured in Gordon's now deleted answer, the reference set between the properties of the three classes can also be broken by redeclaring the property in each one of the classes:
class B extends A { protected static $a; }
class C extends A { protected static $a; }
This is because the property will not be copied to the subclass from the superclass if it's redeclared (see the condition if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) in inherit_static_prop).

Workaround for basic syntax not being parsed

I want to have a class property that allow for an expression to take place on the right side of the equals sign. All versions of PHP choke on the following code, but it is written in this way to allow for easier extendibility in the future.
/* Example SDK Class */
class SDK
{
/* Runtime Option Flags */
// Strings
# 0: Makes no change to the strings.
var $STRING_NONE = (1 << 0);
# 1: Removes color codes from the string.
var $STRING_STRIP_COLOR = (1 << 1);
# 2: Removes language codes from the string.
var $STRING_STRIP_LANG = (1 << 2);
# 3: Removes all formatting from the string.
var $STRING_STRIP = SELF::STRING_STRIP_COLOR & SELF::STRING_STRIP_LANG;
# 4: Converts color codes to HTML & UTF-8.
var $STRING_HTML = (1 << 3);
# 8: Converts color codes to ECMA-48 escape color codes & UTF-8.
var $STRING_CONSOLE = (1 << 4);
# 16: Changes player names only.
var $STRING_NAMES = (1 << 5);
# 32: Changes host names only.
var $STRING_HOSTS = (1 << 6);
function SDK($fString = SELF::STRING_HTML & SELF::STRING_NAMES & SELF_HOST)
{
// constructor code.
}
}
$SDK &= new SDK(SDK::STRING_NONE);
(1 << 0) seems like very basic syntax to me, and is not fathomable why PHP would not allow for such a thing. Can anyone think of a work around that would maintain readability and future expandability of the following code?
When declaring a class constant or property in PHP you can only specify a primitive values for default values. So for instance, this class declaration won't work:
class TEST {
const ABC = 2 * 4;
const DEF = some_function();
static $GHI = array(
'key'=> 5 * 3,
);
}
But this class declaration will:
class TEST {
const ABC = 8;
static $GHI = 15;
}
These rules apply to default values for class constants/properties - you can always initialize other variables with the results of an expression:
$a= array(
'a'=> 1 * 2,
'b'=> 2 * 2,
'c'=> 3 * 2,
);
The reason for this class declaration behavior is as follows: expressions are like verbs. They do something. Classes are like nouns: they declare something. A declarative statement should never produce the side-effects of an action statement. Requiring primitive default values enforces this rule.
With this in mind we can refactor the original class as follows:
class SDK
{
static protected $_types= null;
static public function getType($type_name) {
self::_init_types();
if (array_key_exists($type_name, self::$_types)) {
return self::$_types[$type_name];
} else {
throw new Exception("unknown type $type_name");
}
}
static protected function _init_types() {
if (!is_array(self::$_types)) {
self::$_types= array(
'STRING_NONE'=> 1 << 0,
// ... rest of the "constants" here
'STRING_HOSTS'=> 1 << 6
);
}
}
function __construct($fString = null) {
if (is_null($fString)) {
$fString= self::getType('STRING_NONE') & self::getType('STRING_HOSTS');
}
var_dump($fString);
}
}
$SDK &= new SDK(SDK::getType('STRING_HOSTS'));
PHP class property can not have an expression in it's declaration.
[...] This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated. [...]
Source: PHP Manual: Properties
The following code would work fine, as it's information can be ascertained at compile time and does not require the compiler to look anywhere else, do any mathematical or string functions get the information.
class goodExample
{
// These are OK.
var $Var = 1;
const consts = 'I\'m a Constant Property of the class goodExample.';
static $static = array(FALSE, TRUE);
}
The following, on the other hand, is not valid as it's value must be parsed to get it's true value. This is not valid in PHP at all. The var $Var requires a mathematical operation. The const consts requires concations and a varable look up to get it's value so that's two reasons why that one would not work. Lastly, the static property $static requires two more mathematical operations to get it's true value.
class badExample
{
// These are NOT OK.
var $Var = 1 + 1;
const consts = "I'm a constant property of the class " . __CLASS__ . '.';
static $static = array((1 << 0), (1 << 2));
}
Consts & Static Keywords
Once you declare these properties, their value can not change.
const
[...] Constants differ from normal variables in that you don't use the $ symbol to declare or use them.
The value must be a constant expression, not (for example) a variable, a property, a result of a mathematical operation, or a function call.
Its also possible for interfaces to have constants.
Source: PHP Manual: Class Constants
static
[...] Declaring class properties or methods as static makes them accessible without needing an instantiation of the class. A property declared as static can not be accessed with an instantiated class object (though a static method can).
Source: PHP Manual: Static Keyword
Static can be used outside of the class, so long as you reference the class name.
I think your problem is just with all the vars that you have in there. They should probably all be replaced by const. var was necessary in PHP 4 for declaring member variables, but causes an error in PHP 5. You should be using const, public, etc as of PHP 5 instead.
PHP is able to do (1 << 0) the problem is just because you are using var when you should be using const. Also, doing self::VARIABLE is meant for static variables, and I believe it should be self::$variable (unless you define them as const

Closures in PHP... what, precisely, are they and when would you need to use them?

So I'm programming along in a nice, up to date, object oriented fashion. I regularly make use of the various aspects of OOP that PHP implements but I am wondering when might I need to use closures. Any experts out there that can shed some light on when it would be useful to implement closures?
PHP will support closures natively in 5.3. A closure is good when you want a local function that's only used for some small, specific purpose. The RFC for closures gives a good example:
function replace_spaces ($text) {
$replacement = function ($matches) {
return str_replace ($matches[1], ' ', ' ').' ';
};
return preg_replace_callback ('/( +) /', $replacement, $text);
}
This lets you define the replacement function locally inside replace_spaces(), so that it's not:
1) cluttering up the global namespace
2) making people three years down the line wonder why there's a function defined globally that's only used inside one other function
It keeps things organized. Notice how the function itself has no name, it's simply defined and assigned as a reference to $replacement.
But remember, you have to wait for PHP 5.3 :)
When you will need a function in the future which performs a task that you have decided upon now.
For example, if you read a config file and one of the parameters tells you that the hash_method for your algorithm is multiply rather than square, you can create a closure that will be used wherever you need to hash something.
The closure can be created in (for example) config_parser(); it creates a function called do_hash_method() using variables local to config_parser() (from the config file). Whenever do_hash_method() is called, it has access to variables in the local scope ofconfig_parser() even though it's not being called in that scope.
A hopefully good hypothetical example:
function config_parser()
{
// Do some code here
// $hash_method is in config_parser() local scope
$hash_method = 'multiply';
if ($hashing_enabled)
{
function do_hash_method($var)
{
// $hash_method is from the parent's local scope
if ($hash_method == 'multiply')
return $var * $var;
else
return $var ^ $var;
}
}
}
function hashme($val)
{
// do_hash_method still knows about $hash_method
// even though it's not in the local scope anymore
$val = do_hash_method($val)
}
Apart from the technical details, closures are a fundamental pre-requisite for a programming style known as function oriented programming. A closure is roughly used for the same thing as you use an object for in object oriented programming; It binds data (variables) together with some code (a function), that you can then pass around to somewhere else. As such, they impact on the way that you write programs or - if you don't change the way you write your programs - they don't have any impact at all.
In the context of PHP, they are a little odd, since PHP already is heavy on the class based, object oriented paradigm, as well as the older procedural one. Usually, languages that have closures, has full lexical scope. To maintain backwards compatibility, PHP is not going to get this, so that means that closures are going to be a little different here, than in other languages. I think we have yet to see exactly how they will be used.
I like the context provided by troelskn's post. When I want to do something like Dan Udey's example in PHP, i use the OO Strategy Pattern. In my opinion, this is much better than introducing a new global function whose behavior is determined at runtime.
http://en.wikipedia.org/wiki/Strategy_pattern
You can also call functions and methods using a variable holding the method name in PHP, which is great. so another take on Dan's example would be something like this:
class ConfigurableEncoder{
private $algorithm = 'multiply'; //default is multiply
public function encode($x){
return call_user_func(array($this,$this->algorithm),$x);
}
public function multiply($x){
return $x * 5;
}
public function add($x){
return $x + 5;
}
public function setAlgorithm($algName){
switch(strtolower($algName)){
case 'add':
$this->algorithm = 'add';
break;
case 'multiply': //fall through
default: //default is multiply
$this->algorithm = 'multiply';
break;
}
}
}
$raw = 5;
$encoder = new ConfigurableEncoder(); // set to multiply
echo "raw: $raw\n"; // 5
echo "multiply: " . $encoder->encode($raw) . "\n"; // 25
$encoder->setAlgorithm('add');
echo "add: " . $encoder->encode($raw) . "\n"; // 10
of course, if you want it to be available everywhere, you could just make everything static...
A closure is basically a function for which you write the definition in one context but run in another context. Javascript helped me a lot with understanding these, because they are used in JavaScript all over the place.
In PHP, they are less effective than in JavaScript, due to differences in the scope and accessibility of "global" (or "external") variables from within functions. Yet, starting with PHP 5.4, closures can access the $this object when run inside an object, this makes them a lot more effective.
This is what closures are about, and it should be enough to understand what is written above.
This means that it should be possible to write a function definition somewhere, and use the $this variable inside the function definition, then assign the function definition to a variable (others have given examples of the syntax), then pass this variable to an object and call it in the object context, the function can then access and manipulate the object through $this as if it was just another one of it's methods, when in fact it's not defined in the class definition of that object, but somewhere else.
If it's not very clear, then don't worry, it will become clear once you start using them.
Here are examples for closures in php
// Author: HishamDalal#gamil.com
// Publish on: 2017-08-28
class users
{
private $users = null;
private $i = 5;
function __construct(){
// Get users from database
$this->users = array('a', 'b', 'c', 'd', 'e', 'f');
}
function displayUsers($callback){
for($n=0; $n<=$this->i; $n++){
echo $callback($this->users[$n], $n);
}
}
function showUsers($callback){
return $callback($this->users);
}
function getUserByID($id, $callback){
$user = isset($this->users[$id]) ? $this->users[$id] : null;
return $callback($user);
}
}
$u = new users();
$u->displayUsers(function($username, $userID){
echo "$userID -> $username<br>";
});
$u->showUsers(function($users){
foreach($users as $user){
echo strtoupper($user).' ';
}
});
$x = $u->getUserByID(2, function($user){
return "<h1>$user</h1>";
});
echo ($x);
Output:
0 -> a
1 -> b
2 -> c
3 -> d
4 -> e
5 -> f
A B C D E F
c
Bascially,Closure are the inner functions tat have access to the outer variables and are used as a callback function to anonmyous function (functions that do not have any name).
<?php
$param='ironman';
function sayhello(){
$param='captain';
$func=function () use ($param){
$param='spiderman';
};
$func();
echo $param;
}
sayhello();
?>
//output captain
//and if we pass variable as a reference as(&$param) then output would be spider man;
Closures:
MDN has the best explanation IMO:
A closure is the combination of a function bundled together (enclosed)
with references to its surrounding state (the lexical environment). In
other words, a closure gives you access to an outer function’s scope
from an inner function.
i.e. A closure is a function with access to the variables which are in the parent scope. A closure allows us to conveniently create functions on the fly since in some situations a functions is only needed in one place (callbacks, callable arguments).
Example:
$arr = [1,2,3,3];
$outersScopeNr = 2;
// The second arg in array_filter is a closure
// It would be inconvenient to have this function in global namespace
// The use keyword lets us access a variable in an outer scope
$newArr = array_filter($arr, function ($el) use ($outersScopeNr) {
return $el === 3 || $el === $outersScopeNr;
});
var_dump($newArr);
// array (size=3)
// 1 => int 2
// 2 => int 3
// 3 => int 3

Categories