I'd like to destroy an object and the intern objects in it. Why the example below is not working:
<?php
class I
{
public $elt = 'hello world!!';
public function __destruct()
{
var_dump('I: destroyed');
}
}
class A
{
public $val1=1;
public $val2=2;
public $val3=3;
public $val4=4;
public $i;
public function __construct($i)
{
$this->i = $i;
}
public function __destruct()
{
var_dump('A destroyed');
unset($this->i);
}
}
$i = new I();
$a = new A($i);
unset($a);
var_dump($i);
OUTPUT:
string(11) "A destroyed"
object(I)#1 (1) { ["elt"]=> string(13) "hello world!!" }
string(12) "I: destroyed"
How come I didn't get the notice Undefined variable: i?
And how come the message of the destructor of the class I is displayed after my var_dump of $i?
UPDATE
The thing is that I have a main object, this object has to purge/refresh its nested objects at the end of each iteration of a loop.
Let's talk about the four lines of code:
$i = new I();
$a = new A($i);
unset($a);
var_dump($i);
The first line creates an object of type I and stores a reference to it in variable $i (PHP objects are always assigned as references, you need to use the operator clone to create a copy).
The second line passes $i to the constructor of class A and that creates another reference to the same object, stored in $a->$i.
The third line destroys the object of type A; this removes the second reference to the object of type I but it does not affect the first one; the variable $i still holds it.
The fourth line dumps the content of variable $i that was never unset. It is the object of type I created on the first line.
If you want to destroy the object of type I when the object of type A is destroyed then make sure there is no other reference to it. This can be accomplished by either creating the object of type I in the constructor of class A or by removing all the references to $i after it was passed to the constructor of class A. Adding unset($i); after $a = new A($i); will do the job.
From the PHP document: http://php.net/manual/en/language.oop5.decon.php
Destructors called during the script shutdown have HTTP headers
already sent. The working directory in the script shutdown phase can
be different with some SAPIs (e.g. Apache).
The destruction methods will be called after executing of other calls finished. Meaning your var_dump($i) will be called first, then destructions are executed.
Related
In this code:
<?php
class Foo
{
var $value;
function foo($value)
{
$this->setValue($value);
}
function setValue($value)
{
$this->value=$value;
}
}
class Bar
{
var $foos=array();
function Bar()
{
for ($x=1; $x<=10; $x++)
{
$this->foos[$x]=new Foo("Foo # $x");
}
}
function getFoo($index)
{
return $this->foos[$index];
}
function test()
{
$testFoo=$this->getFoo(5);
$testFoo->setValue("My value has now changed");
}
}
?>
When the method Bar::test() is run and it changes the value of foo # 5 in the array of foo objects, will the actual foo # 5 in the array be affected, or will the $testFoo variable be only a local variable which would cease to exist at the end of the function?
Why not run the function and find out?
$b = new Bar;
echo $b->getFoo(5)->value;
$b->test();
echo $b->getFoo(5)->value;
For me the above code (along with your code) produced this output:
Foo #5
My value has now changed
This isn't due to "passing by reference", however, it is due to "assignment by reference". In PHP 5 assignment by reference is the default behaviour with objects. If you want to assign by value instead, use the clone keyword.
You can refer to http://ca2.php.net/manual/en/language.oop5.references.php for the actual answer to your question.
One of the key-points of PHP5 OOP that is often mentioned is that "objects are passed by references by default". This is not completely true.
A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
They are passed by value in PHP 4 and by reference in PHP 5. In order to pass objects by reference in PHP 4 you have to explicitly mark them as such:
$obj = &new MyObj;
How to combine two variables to obtain / create new variable?
public $show_diary = 'my';
private my_diary(){
return 1;
}
public view_diary(){
return ${"this->"}.$this->show_diary.{"_diary()"}; // 1
return $this->.{"$this->show_diary"}._diary() // 2
}
both return nothing.
Your class should be like following:
class Test
{
public $show_diary;
function __construct()
{
$this->show_diary = "my";
}
private function my_diary(){
return 707;
}
public function view_diary(){
echo $this->{$this->show_diary."_diary"}(); // 707
}
}
It almost looks from your question like you are asking about how to turn simple variables into objects and then how to have one object contain another one. I could be way off, but I hope not:
So, first off, what is the differnce between an object and a simple variable? An object is really a collection of (generally) at least one property, which is sort of like a variable within it, and very often functions which do things to the properties of the object. Basically an object is like a complex variable.
In PHP, we need to first declare the strucutre of the object, this is done via a class statement, where we basicaly put the skeleton of what the object will be into place. This is done by the class statement. However, at this point, it hasn't actually been created, it is just like a plan for it when it is created later.
The creation is done via a command like:
$someVariable= new diary();
This executes so create a new variable, and lays it out with the structure, properties and functions defined in the class statement.
From then on, you can access various properties or call functions within it.
class show_diary
{
public $owner;
public function __construct()
{
$this->owner='My';
}
}
class view_diary
{
public $owner;
public $foo;
public function __construct()
{
$this->foo='bar';
$this->owner=new show_diary();
}
}
$diary= new view_diary();
print_r($diary);
The code gives us two classes. One of the classes has an instance of the other class within it.
I have used constructors, which are a special type of function that is executed each time we create a new instance of a class - basically each time we declare a variable of that type, the __construct function is called.
When the $diary= new view_diary(); code is called, it creates an instance of the view_diary class, and in doing so, the first thing it does is assigns it's own foo property to have the value 'bar' in it. Then, it sets it's owner property to be an instance of show_diary which in turn then kicks off the __construct function within the new instance. That in turn assigns the owner property of the child item to have the value 'My'.
If you want to access single properties of the object, you can do so by the following syntax:
echo $diary->foo;
To access a property of an object inside the object, you simply add more arrows:
echo $diary->owner->owner;
Like this?
$diary = $this->show_diary . '_diary';
return $this->$diary();
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
Regarding PHP, what are the differences between:
get_class_vars()
get_object_vars()
get_class_vars() takes the class_name
get_object_vars() takes an $object variable
They both function similarly:
get_class_vars() will expose default public variables (or private/protected if called within the class)
get_object_vars() will expose the current public variables (or private/protected if called within the class object)
Neither will expose methods.
As you can see from the get_class_vars and get_object_vars manual pages, get_class_vars gets the default values of properties of a class, and get_object_vars gets the current values of properties of an object.
Furthermore, get_class_vars takes a string (ie. the name of a class), whereas get_object_vars takes an object.
class Example
{
public $var = 123;
}
$e = new Example();
$e->var = 456;
var_dump(get_class_vars("Example"));
/*
array(1) {
["var"]=>
int(123)
}
*/
var_dump(get_object_vars($e));
/*
array(1) {
["var"]=>
int(456)
}
*/
Theres one more thing:
get_object_vars() doesn't see static variables, but sees vars created during runtime!
get_class_vars() DOES SEE the static ones, but DOES NOT see new variables created during runtime, eg. in the constructor:
public function __construct() {$this->newval = "newval";}
As of PHP 5.5.9
I have noticed that there's actually a slight difference between get_class_vars and get_object_vars.
Regarding to PHP Bug Report, and I have tested it myself as well.
Both methods return the same result. BUT, you can manipulate object properties by using get_object_vars and not with get_class_vars.
Consider the following sample,
class Foo
{
public $bar = null;
public function __construct() {
$this->bar = new DateTime(); // Now
$this->far = &$this->bar;
}
}
$foo = new Foo();
var_dump($foo);
$vars = get_object_vars($foo);
$vars['bar'] = new DateTime('2014-03-25');
var_dump($foo);
In the sample above, you will notice that the second var_dump will
have $foo->bar modified.
Note: If you try to change the method from get_object_vars to
get_class_vars, you will also notice that $foo->bar will not be
modified.
get_class_vars() gives you even nonstatic variables, but their values are taken at the time of creating object, eg. init values.
I ran this very unscientific (and probably quite error prone) performance test:
I used this class:
class foo {
public $bar;
public $baz = "abcd";
public $qux = 123;
public $quux;
public $quuz = "abcd";
public $corge = 1234;
}
and did test 1 (get_object_vars):
$start = microtime(true);
for($i = 1; $i >= 100000; $i++) {
$foo = new foo;
$gov = get_object_vars($foo);
}
$end = microtime(ture);
echo $end - $start;
then test 2 (get_class_vars - replacing the inside of the for loop):
$gcv = get_class_vars("foo");
then test 3 (get_class_vars, and assuming you need to instantiate the object anyway - again replacing the for loop):
$foo = new foo();
$gcv = get_class_vars("foo");
I ran each one 5 times and these are the results I got:
test 1:
0.00014019012451172
0.00014519691467285
0.00014495849609375
0.00012087821960449
0.00012421607971191
AVG: 0.0001350879669
test 2:
0.00013995170593262
0.00012898445129395
0.00010204315185547
0.00013494491577148
0.00061392784118652
AVG: 0.0002239704132
(if we remove the outlier): 0.0001264810562
test 3:
0.00014710426330566
0.00012397766113281
0.00012612342834473
0.00011992454528809
0.00014090538024902
AVG: 0.0001316070557
In test two there was a weird outlier in round 5... not sure why... hiccup on my server. If we remove that outlier, it looks like get_class_vars() is slightly more performant, regardless of whether you need to instantiate the class.
This test does not take into account any variable caching or php memory optimization that may be going on. Of course in a real world application it's highly unlikely that you'll iterate over either of the functions 10k times. So the functions' single and first execution performance is really more interesting, but much harder to test (accurately).
I've come across some unexpected behavior with static variables defined inside object methods being shared across instances. This is probably known behavior, but as I browse the PHP documentation I can't find instances of statically-defined variables within object methods.
Here is a reduction of the behavior I've come across:
<?php
class Foo {
public function dofoo() {
static $i = 0;
echo $i++ . '<br>';
}
}
$f = new Foo;
$g = new Foo;
$f->dofoo(); // expected 0, got 0
$f->dofoo(); // expected 1, got 1
$f->dofoo(); // expected 2, got 2
$g->dofoo(); // expected 0, got 3
$g->dofoo(); // expected 1, got 4
$g->dofoo(); // expected 2, got 5
Now, I would have expected $i to be static per instance, but in reality $i is shared between the instances. For my own edification, could someone elaborate on why this is the case, and where it's documented on php.net?
This is the very definition of static.
If you want members to be specific to an instance of an object, then you use class properties
e.g.
<?php
class Foo
{
protected $_count = 0;
public function doFoo()
{
echo $this->_count++, '<br>';
}
}
Edit: Ok, I linked the documentation to the OOP static properties. The concept is the same though. If you read the variable scope docs you'll see:
Note: Static declarations are resolved in compile-time.
Thus when your script is compiled (before it executes) the static is "setup" (not sure what term to use). No matter how many objects you instantiate, when that function is "built" the static variable references the same copy as everyone else.
I agree that the current PHP documentation is not sufficiently clear on exactly what "scope" means for a static variable inside a non-static method.
It is of course true (as hobodave indicates) that "static" generally means "per class", but static class properties are not exactly the same thing as static variables within a (non static) method, in that the latter are "scoped" by method (every method in a class can have its own static $foo variable, but there can be at most one static class member named $foo).
And I would argue that although the PHP 5 behavior is consistent ("static" always means "one shared instance per class"), it is not the only way that PHP could behave.
For example, most people use static function variables to persist state across function calls, and for global functions the PHP behavior is exactly what most everyone would expect. So it is certainly possible to imagine a PHP interpreter that maintains the state of certain method variables across method invocation and does so "per instance", and that's actually what I also expected to happen the first time I declared a local method variable to be static.
That is what static is, it's the same variable across all instances of the class.
You want to write this so that the variable is a private member of the instance of the class.
class Foo {
private $i = 0;
public function dofoo() {
echo $this->i++ . '<br>';
}
}
The static keyword can be used with variables, or used with class methods and properties. Static variables were introduced in PHP 4 (I think, it might have been earlier). Static class members/methods were introduced in PHP 5.
So, per the manual, a static variable
Another important feature of variable scoping is the static
variable. A static variable exists only in a local function
scope, but it does not lose its value when program execution
leaves this scope.
This is consistant with the behavior you described. If you want a per instance variable, used a regular class member.
Ups 7 years it a long time but anyway here it goes.
All classes have a default constructor why am I saying this?!?
Because if you define a default behaviour in constructor each instance of the class will be affected.
Example:
namespace Statics;
class Foo
{
protected static $_count;
public function Bar()
{
return self::$_count++;
}
public function __construct()
{
self::$_count = 0;
}
}
Resulting in:
require 'Foo.php';
use Statics\Foo;
$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
0
1
2
0
1
2
Every new instance from the upper class will start from 0!
The static count behaviour will NOT be shared across the multiple instances as it will be starting from the value assigned in constructor.
If you need to share data across multiple instances all you need to do is to define a static variable and assign default data outside the constructor!
Example:
namespace Statics;
class Foo
{
//default value
protected static $_count = 0;
public function Bar()
{
return self::$_count++;
}
public function __construct()
{
//do something else
}
}
Resulting in:
require 'Foo.php';
use Statics\Foo;
$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
0
1
2
3
4
5
As you can see the results are completely different, the memory space allocation is the same in between class instances but it can produce different results based on how you define default value.
I hope it helped, not that the above answers are wrong but I felt that it was important to understand the all concept from this angle.
Regards, from Portugal!