I have spot the following pattern in code I'm working with: in some classes in destructor I have found private variable being nulled, in example:
public function __destruct()
{
foreach($this->observers as $observer)
{
$observer = null;
}
$this->db_build = null;
}
Is there any point in doing this when PHP has GC? Does it somehow improve performance of script?
It's sometimes just for the cleanliness meme. But in your exmaple both $observer and ->$db_build reference sub-objects. So here the intention is to have them destroyed before the destruction of the current object finishes. (Albeit I'm unsure if Zend core really likes being interrupted when it's on a destroying rampage. It probably has a spool list or something.)
Anyway, it's not necessary from the GC point of view. But it might be sensible if the composite subojects have some inderdependencies; e.g. counters or registry references themselves. So, in most cases not needed I'd say.
I've made a silly example to demonstrate the __destruct order:
class dest {
function __construct($name, $sub=NULL) {
$this->name = $name;
$this->sub = $sub;
}
function __destruct() {
print "<destroying $this->name>\n";
$this->sub = NULL;
print "</destroying $this->name>\n";
}
}
$d = new dest("first", new dest("second", new dest("third")));
exit;
Without the $this->sub = NULL the destruction of the objects would each happen individually, not necessarily in instantiation order. With unsetting composite objects manually however PHP destroys the three objects in a nested fashion:
<destroying first>
<destroying second>
<destroying third>
</destroying third>
</destroying second>
</destroying first>
It could be because PHP's garbage collection is based on reference countring, and older versions could not handle cyclical dependencies. Then, in some cases it would have been necessary to manually set references to null to enable the GC to do its work, and there may still be some special cases that the cycle detection algorithm does not catch.
More likely though, it's just an example of cargo cult programming (the Wikipedia entry even explicitly lists this as an example).
first - it's a good programming tone, second - it makes script memory free. if right after invoking destructor php script terminates, i see no advantages.
Related
Is there a more native way (e.x. a built-in function) with less userland code to check if an objects property values have changed instead of using one of those methods:
The serialize approach
$obj = new stdClass(); // May be an instance of any class
echo $hashOld = md5(serialize($obj)) . PHP_EOL;
$obj->change = true;
echo $hashNew = md5(serialize($obj)) . PHP_EOL;
echo 'Changed: '; var_dump($hashOld !== $hashNew);
Which results in:
f7827bf44040a444ac855cd67adfb502 (initial)
506d1a0d96af3b9920a31ecfaca7fd26 (changed)
Changed: bool(true)
The shadow copy approach
$obj = new stdClass();
$shadowObj = clone $obj;
$obj->change = true;
var_dump($shadowObj != $obj);
Which results in:
bool(true);
Both approaches work. But both have disadvantages compared to a non userland implementation. The first one needs CPU for serialization and hashing and the second one needs memory for storing clones. And some classes may not be cloned.
Doesn't PHP track changes at object properties? And does PHP not expose a method to make use of it?
What you are trying to do?
You are trying to compare object with itself, after some chain of "unknown" operations to check if the object has changed. If this is true, there are some logical points to observe. At first, if you want to compare object with itself, you've got only two options:
Remember the whole object state (for example hash, or just copy whole object)
Track changes over time
There is no other logical approach. Comparing memory allocations, real objects, copying objects, comparing hashes, is all in point one. Tracking changes, saving changes inside object, remembering meantime operations, inside point 2.
So in my opinion this question is sort of backing up data questions. In that case there are many, many solutions but none of them are hardcoded inside php as far as I'm concerned. Why?
The answer is simple. PHP guys have got the same problems you've got :). Because if this would be hardocded inside php, then php should run / use one of those mechanisms (1) or (2).
In that case every object that you create, and every operation you made should be written somewhere to remember every state / object / something and use them for comparison in the future.
While you need this solution, almost ~100% of websites don't. So hardcoding this inside php would made ~100% of websites work slower and your work faster ;).
PHP hypothetical solution?
The only solution (maybe built in php in the future) I can think of is making some kind of php config flag: track objects, and only if this flag is true, then run all the php mechanisms of tracking objects states. But this also mean a huge performance gap. As all the ifs (if tracking, if tracking, if tracking) are also procesor and memory time consuming.
There is also a problem, what to compare? You need to compare object with same object, but... Few minutes ago? Few operations ago? No... You must point exactly one place in code, and then point second place in code and compare object in those two places. So hypothetical auto tracking is... Kind of powerless, as there is no "key" in the object state ofer time array. I mean, even if you got magic_object_comparer function, what it should look like?
<?php
function magic_object_comparer() {} // Arguments??
function magic_object_comparer($object_before, $object_after) {} // you must save object_before somewhere...??
function magic_object_comparer($object, $miliseconds) {} // How many miliseconds?
function magic_object_comparer($object, $operations) {} // How many operations? Which operations?
magic_comparer_start($object);
// ... Few operations...
$boolean = magic_comparer_compare_from start($object);
// Same as own implementation...
?>
Sadly, you are left with own implementation...
After all, I would propose to implement some kind of own mechanism for that, and remember to use it only there, where you need it. As this mechanism will for sure be time and memory consuming. So think carefully:
Which objects you want to compare. Why?
When you want to compare them?
Does all changes need to be compared?
What is the easiest way of saving those states changes?
And after all of that, try to implement it. I see that you've got a huge php knowledge, so I'm pretty sure that you will figure out something. There are also many comments, and possible ideas in this question and discussion.
But after all maybe I explained a little why, there is no build in solution, and why there should not be one in the future... :).
UPDATE
Take a look here: http://www.fluffycat.com/PHP-Design-Patterns/. This is a great resource about php patterns. You should take a look at adapter, decorator and observer patterns, for possible elegant object oriented solutions.
While I too am looking for a very fast/faster approach, a variant of method 2 is effectively what I use. The advantage of this method is that it is (pretty) fast (in comparison to an isset()), depending on object size. And you don't have to remember to set a ->modified property each time you change the object.
global $shadowcopy; // just a single copy in this simple example.
$thiscopy = (array) $obj; // don't use clone.
if ($thiscopy !== $shadowcopy) {
// it has been modified
// if you want to know if a field has been added use array_diff_key($thiscopy,$shadowcopy);
}
$shadowcopy = $thiscopy; // if you don't modify thiscopy or shadowcopy, it will be a reference, so an array copy won't be triggered.
This is basically method 2, but without the clone. If your property value is another object (vobj), then clone may be necessary (otherwise both references will point to the same object), but then it is worth noting that it is that object vobj you want to see if has changed with the above code. The thing about clone is that it is constructing a second object (similar performance), but if you want to see what values changed, you don't care about the object itself, only the values. And array casting of an object is very fast (~2x the speed of a boolean cast of a bool) .. well, up until large objects. Also direct array comparison === is very fast, for arrays under say 100 vals.
I'm pretty sure an even faster method exists...
I can offer you another solution to the problem, In fact to detect "if an object has changed" we can use observer pattern design principles. May that way should be better for some people who want to get notify about changes in object.
Contracts/ISubject.php
<?php
namespace Contracts;
interface ISubject
{
public function attach($observer): void;
public function detach($observer): void;
public function notify(): void;
}
Contracts/IObserver.php
<?php
namespace Contracts;
interface IObserver
{
public function update($subject);
}
Subject.php
class Subject implements ISubject
{
public $state; // That is detector
private $observers;
public function __construct()
{
$this->observers = new \SplObjectStorage(); // That is php built in object for testing purpose I use SplObjectStorage() to store attach()'ed objects.
}
public function attach($observer): void
{
echo "Subject: Attached an observer.\n";
$this->observers->attach($observer);
}
public function detach($observer): void
{
$this->observers->detach($observer);
echo "Subject: Detached an observer.\n";
}
public function notify(): void
{
echo "Subject: Notifying observers...\n";
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function someYourLogic()
{
$this->state = rand(0, 10);
echo "Subject: My state has just changed to: {$this->state}\n";
$this->notify();
}
}
Observer1.php | Plus you are able to have as many ConcreteObserver as you want
class Observer1 implements IObserver
{
public function update($subject): void
{
if ($subject->state < 5) {
echo "Observer1: Reacted to the event.\n";
}
}
}
Clinet.php
$subject = new Subject();
$o1 = new Observer1();
$subject->attach($o1);
$subject->someYourLogic();
There is no built-in method, I'm afraid. The shadow copy approach is the best way.
A simpler way, if you have control over the class, is to add a modified variable:
$this->modified = false;
When I modify the object in any way, I simply use
$obj->modified = true;
This way I can later check
if($obj->modified){ // Do Something
to check if it was modified. Just remember to unset($obj->modified) before saving content in a database.
We can implement it without observer.
For pure php, we can use $attributes & $original to check what has been modified check this explanation if needed.
$modifiedValues = [];
foreach($obj->attributes as $column=>$value) {
if(!array_key_exists($column, $obj->original) || $obj->original[$column] != $value) {
$modifiedValues[$column] = $value;
}
}
// then check $modifiedValues if it contains values
For Laravel user, we can use the isDirty() method. Its usage:
$user = App\User::first();
$user->isDirty(); //false
$user->name = "Peter";
$user->isDirty(); //true
What is the exact order of object deconstruction?
From testing, I have an idea: FIFO for the current scope.
class test1
{
public function __destruct()
{
echo "test1\n";
}
}
class test2
{
public function __destruct()
{
echo "test2\n";
}
}
$a = new test1();
$b = new test2();
Which produces the same results time and time again:
test1
test2
The PHP manual is vague (emphasis mine to highlight uncertainty): "The destructor method will be called as soon as there are no other references to a particular object or in any order during the shutdown sequence."
What is the exact order of deconstruction? Can anyone describe in details the implementation of destruction order that PHP uses? And, if this order is not consistent between all the PHP versions, can anyone pinpoint which PHP versions change in this order?
First of all, a bit on general object destruction order is covered here: https://stackoverflow.com/a/8565887/385378
In this answer I will only concern myself with what happens when objects are still alive during the request shutdown, i.e. if they were not previously destroyed through the refcounting mechanism or the circular garbage collector.
The PHP request shutdown is handled in the php_request_shutdown function. The first step during the shutdown is calling the registered shutdown functions and subsequently freeing them. This can obviously also result in objects being destructed if one of the shutdown functions was holding the last reference to some object (or if the shutdown function itself was an object, e.g. a closure).
After the shutdown functions have run the next step is the one interesting to you: PHP will run zend_call_destructors, which then invokes shutdown_destructors. This function will (try to) call all destructors in three steps:
First PHP will try to destroy the objects in the global symbol table. The way in which this happens is rather interesting, so I reproduced the code below:
int symbols;
do {
symbols = zend_hash_num_elements(&EG(symbol_table));
zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC);
} while (symbols != zend_hash_num_elements(&EG(symbol_table)));
The zend_hash_reverse_apply function will walk the symbol table backwards, i.e. start with the variable that was created last and going towards the variable that was created first. While walking it will destroy all objects with refcount 1. This iteration is performed until no further objects are destroyed with it.
So what this basically does is a) remove all unused objects in the global symbol table b) if there are new unused objects, remove them too c) and so on. This way of destruction is used so objects can depend on other objects in the destructor. This usually works fine, unless the objects in the global scope have complicated (e.g. circular) interrelations.
The destruction of the global symbol table differs significantly from the destruction of all other symbol tables. Normally symbol tables are destructed by walking them forward and just dropping the refcount on all objects. For the global symbol table on the other hand PHP uses a smarter algorithm that tries to respect object dependencies.
The second step is calling all remaining destructors:
zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC);
This will walk all objects (in order of creation) and call their destructor. Note that this only calls the "dtor" handler, but not the "free" handler. This distinction is internally important and basically means that PHP will only call __destruct, but will not actually destroy the object (or even change its refcount). So if other objects reference the dtored object, it will still be available (even though the destructor was already called). They will be using some kind of "half-destroyed" object, in a sense (see example below).
In case the execution is stopped while calling the destructors (e.g. due to a die) the remaining destructors are not called. Instead PHP will mark the objects are already destructed:
zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC);
The important lesson here is that in PHP a destructor is not necessarily called. The cases when this happens are rather rare, but it can happen. Furthermore this means that after this point no more destructors will be called, so the remainder of the (rather complicated) shutdown procedure does not matter anymore. At some point during the shutdown all the objects will be freed, but as the destructors have already been called this is not noticeable for userland.
I should point out that this is the shutdown order as it currently is. This has changed in the past and may change in the future. It's not something you should rely on.
Example for using an already destructed object
Here is an example showing that it is sometimes possible to use an object that already had its destructor called:
<?php
class A {
public $state = 'not destructed';
public function __destruct() { $this->state = 'destructed'; }
}
class B {
protected $a;
public function __construct(A $a) { $this->a = $a; }
public function __destruct() { var_dump($this->a->state); }
}
$a = new A;
$b = new B($a);
// prevent early destruction by binding to an error handler (one of the last things that is freed)
set_error_handler(function() use($b) {});
The above script will output destructed.
What is the exact order of deconstruction? Can anyone describe in detail the implementation of destruction order that PHP uses? And, if this order is not consistent between any and all PHP versions, can anyone pinpoint which PHP versions this order changes in?
I can answer three of these for you, in a somewhat roundabout way.
The exact order of destruction is not always clear, but is always consistent given a single script and PHP version. That is, the same script running with the same parameters that creates objects in the same order will basically always get the same destruction order as long as it runs on the same PHP version.
The shutdown process -- the thing that triggers object destruction when script execution has stopped -- has changed in the recent past, at least twice in a way that impacted the destruction order indirectly. One of these two introduced bugs in some old code I had to maintain.
The big one was back in 5.1. Prior to 5.1, the user's session was written to disk at the very start of the shutdown sequence, before object destruction. This meant that session handlers could access anything that was left over object-wise, like, say, custom database access objects. In 5.1, sessions were written after one sweep of object destruction. In order to retain the previous behavior, you had to manually register a shutdown function (which are run in order of definition at the start of shutdown before destruction) in order to successfully write session data if the write routines needed a (global) object.
It is not clear if the 5.1 change was intended or was a bug. I've seen both claimed.
The next change was in 5.3, with the introduction of the new garbage collection system. While the order of operations at shutdown remained the same, the precise order of destruction could now change based on ref counting and other delightful horrors.
NikiC's answer has details on the current (at time of writing) internal implementation of the shutdown process.
Once again, this is not guaranteed anywhere, and the documentation very expressly tells you to never assume a destruction order.
For anyone interested - as at PHP 8.0:
class A {
function __destruct() {
print get_class();
}
}
class B {
private $child;
function __construct() {
$this->child = new A();
}
function __destruct() {
print get_class();
}
}
class C {
private $child;
function __construct() {
$this->child = new B();
}
function __destruct() {
print get_class();
}
}
new C;
results in output of
CBA
ie. the containing object destructor fires before the contained object destructor.
To reverse the order if desired ie. to ABC, change the destructor in all but A (innermost class) to be:
function __destruct() {
unset($this->child);
print get_class();
}
I created a class implementing ArrayAccess and I added it a function to prevents WRITE actions:
$Obj->Add("key","something");
$Obj->Add("key2","something2");
$Obj->SetReadOnly(); // sets read only property
unset($Obj["key2"]); // throws error, object is readonly
But, i want to prevent unsetting object too:
unset($Obj);
I can do it?I hear suggestions.
Thanks for help!.
I can't imagine any situation where you would really want to do this. I can also imagine doing this will cause you serious problems at script termination when all objects are destroyed. The PHP manual says the following on throwing exceptions in destructors:
Note:
Attempting to throw an exception from a destructor (called in the time
of script termination) causes a fatal error.
The above statement implies that you can throw an exception if you're not in the script termination phase, so maybe the following is possible.
public function __destruct ()
{
if ($this -> isReadOnly ())
{
throw new Exception ('Class is read-only');
}
}
However, as the manual points out, this will trigger a fatal error during script shutdown.
I honestly can't see any point to wanting to prevent object destruction. It should be up to the programmer to manage object lifetimes.
unset() does not actually destruct an object, if that's what you're trying to prevent.
An object will only be destructed when all references to it have been unset or are no longer in scope. Even then it won't happen until the garbage collector runs.
So if you have some code that you are worried will molest your object, you've already done a good job of making it immutable with your read-only logic.
Let's say you have
$Obj = gotMyObjectSomehow();
and you need to pass it to a some other code you don't want to unset $Obj. As long as that code is called inside a function, there's nothing to be concerned about. If you call
someFunction($Obj);
and let's say that function unsets the parameter it's passed in
function someFunction($anObj) {
unset($anObj);
}
then your original $Obj variable will still be set.
The function creates a second variable referencing the original object and uses that in its own scope.
You can't control unsetting variable names, because those names are not technically a part of the object referenced. Consider the following:
$a = new MyObject();
$b = $a;
Now you have two references to the same object. There is no difference between using $a and $b, because in PHP objects are always used by reference (i.e. you don't have to do $b =& $a in the second line). So both $a and $b are essentially the same object; unsetting $a will not destroy the object, as well as unsetting $b won't destroy it; all references need to be unset before the object is destroyed.
I don't think you can do what you're asking for - it's not possible to prevent a variable being unset like that.
However, a comment of yours above set me thinking. You said:
.... idea if you want to prevent unsets system variables in a thirdparty extensions
So if I understand you right, your aim here is to ensure that while the thirdparty code (ie your software) is in use, all the variables associated with it remain in place?
Now you haven't specified much about what variables there are in this system. We see one object in the question, but presumably there must be more than that? I'm guessing you've got a bunch of things that tie together, right? It would help in these sorts of questions to provide a bit more context; the actual thing that you're asking for isn't possible, but with a bit of understanding about what you want to achieve, we could come up with alternatives.
Okay. So my suggestion: create your objects as Singletons. This is often frowned on by purists, but might work well for this situation, depending on exactly what you're doing. The beauty here is that you can encapsulate all access to the object inside class methods, meaning that the developer using your code doesn't have access to the master copy of the object in order to unset it.
A singleton works like this:
<?php
class mySingletonClass {
private static $masterObject=null;
public static function getInstance() {
if(!isset(self::$masterObject)) {
self::$masterObject = new self;
}
return self::$masterObject;
}
private function __construct() {
//your existing constructor, as it already exists, but marked as private.
}
//...and all the other methods as you already have them.
}
The class constructor method is private, so can only be accessed from methods within the class. Therefore, you can no longer do new classname(). The only way you can get an object of this class is to get it from the static getInstance() method. And the key thing here is that this method always returns the same copy of the object.
$obj = mySingletonClass::getInstance();
unset($obj);
$obj = mySingletonClass::getInstance(); //will give the exact same object as the first time.
You can unset it if you want, but the original object is still there and can still be accessed. Any of your other classes can use that getInstance() method to get the same copy of the same object from anywhere in the program. It's an indestructible global variable.
Singletons are often used for a program's main database connection object, but it might be a useful pattern for you here.
I hope that helps. It's about the only way I can think of to get close to what you want.
I'm using __get() to make some of my properties "dynamic" (initialize them only when requested). These "fake" properties are stored inside a private array property, which I'm checking inside __get.
Anyway, do you think it's better idea to create methods for each of these proprties instead of doing it in a switch statement?
Edit: Speed tests
I'm only concerned about performance, other stuff that #Gordon mentioned are not that important to me:
unneeded added complexity - it doesn't really increase my app complexity
fragile non-obvious API - I specifically want my API to be "isolated"; The documentation should tell others how to use it :P
So here are the tests that I made, which make me think that the performance hit agument is unjustified:
Results for 50.000 calls (on PHP 5.3.9):
(t1 = magic with switch, t2 = getter, t3 = magic with further getter call)
Not sure what the "Cum" thing mean on t3. It cant be cumulative time because t2 should have 2K then...
The code:
class B{}
class A{
protected
$props = array(
'test_obj' => false,
);
// magic
function __get($name){
if(isset($this->props[$name])){
switch($name){
case 'test_obj':
if(!($this->props[$name] instanceof B))
$this->props[$name] = new B;
break;
}
return $this->props[$name];
}
trigger_error('property doesnt exist');
}
// standard getter
public function getTestObj(){
if(!($this->props['test_obj'] instanceof B))
$this->props['test_obj'] = new B;
return $this->props['test_obj'];
}
}
class AA extends A{
// magic
function __get($name){
$getter = "get".str_replace('_', '', $name); // give me a break, its just a test :P
if(method_exists($this, $getter))
return $this->$getter();
trigger_error('property doesnt exist');
}
}
function t1(){
$obj = new A;
for($i=1;$i<50000;$i++){
$a = $obj->test_obj;
}
echo 'done.';
}
function t2(){
$obj = new A;
for($i=1;$i<50000;$i++){
$a = $obj->getTestObj();
}
echo 'done.';
}
function t3(){
$obj = new AA;
for($i=1;$i<50000;$i++){
$a = $obj->test_obj;
}
echo 'done.';
}
t1();
t2();
t3();
ps: why do I want to use __get() over standard getter methods? the only reason is the api beauty; because i don't see any real disadvantages, I guess it's worth it :P
Edit: More Speed tests
This time I used microtime to measure some averages:
PHP 5.2.4 and 5.3.0 (similar results):
t1 - 0.12s
t2 - 0.08s
t3 - 0.24s
PHP 5.3.9, with xdebug active this is why it's so slow:
t1 - 1.34s
t2 - 1.26s
t3- 5.06s
PHP 5.3.9 with xdebug disabled:
t1 - 0.30
t2 - 0.25
t3 - 0.86
Another method:
// magic
function __get($name){
$getter = "get".str_replace('_', '', $name);
if(method_exists($this, $getter)){
$this->$name = $this->$getter(); // <-- create it
return $this->$name;
}
trigger_error('property doesnt exist');
}
A public property with the requested name will be created dynamically after the first __get call. This solves speed issues - getting 0.1s in PHP 5.3 (it's 12 times faster then standard getter), and the extensibility issue raised by Gordon. You can simply override the getter in the child class.
The disadvantage is that the property becomes writable :(
Here is the results of your code as reported by Zend Debugger with PHP 5.3.6 on my Win7 machine:
As you can see, the calls to your __get methods are a good deal (3-4 times) slower than the regular calls. We are still dealing with less than 1s for 50k calls in total, so it is negligible when used on a small scale. However, if your intention is to build your entire code around magic methods, you will want to profile the final application to see if it's still negligible.
So much for the rather uninteresting performance aspect. Now let's take a look at what you consider "not that important". I'm going to stress that because it actually is much more important than the performance aspect.
Regarding Uneeded Added Complexity you write
it doesn't really increase my app complexity
Of course it does. You can easily spot it by looking at the nesting depth of your code. Good code stays to the left. Your if/switch/case/if is four levels deep. This means there is more possible execution pathes and that will lead to a higher Cyclomatic Complexity, which means harder to maintain and understand.
Here is numbers for your class A (w\out the regular Getter. Output is shortened from PHPLoc):
Lines of Code (LOC): 19
Cyclomatic Complexity / Lines of Code: 0.16
Average Method Length (NCLOC): 18
Cyclomatic Complexity / Number of Methods: 4.00
A value of 4.00 means this is already at the edge to moderate complexity. This number increases by 2 for every additional case you put into your switch. In addition, it will turn your code into a procedural mess because all the logic is inside the switch/case instead of dividing it into discrete units, e.g. single Getters.
A Getter, even a lazy loading one, does not need to be moderately complex. Consider the same class with a plain old PHP Getter:
class Foo
{
protected $bar;
public function getBar()
{
// Lazy Initialization
if ($this->bar === null) {
$this->bar = new Bar;
}
return $this->bar;
}
}
Running PHPLoc on this will give you a much better Cyclomatic Complexity
Lines of Code (LOC): 11
Cyclomatic Complexity / Lines of Code: 0.09
Cyclomatic Complexity / Number of Methods: 2.00
And this will stay at 2 for every additional plain old Getter you add.
Also, take into account that when you want to use subtypes of your variant, you will have to overload __get and copy and paste the entire switch/case block to make changes, while with a plain old Getter you simply overload the Getters you need to change.
Yes, it's more typing work to add all the Getters, but it is also much simpler and will eventually lead to more maintainable code and also has the benefit of providing you with an explicit API, which leads us to your other statement
I specifically want my API to be "isolated"; The documentation should tell others how to use it :P
I don't know what you mean by "isolated" but if your API cannot express what it does, it is poor code. If I have to read your documentation because your API does not tell me how I can interface with it by looking at it, you are doing it wrong. You are obfuscating the code. Declaring properties in an array instead of declaring them at the class level (where they belong) forces you to write documentation for it, which is additional and superfluous work. Good code is easy to read and self documenting. Consider buying Robert Martin's book "Clean Code".
With that said, when you say
the only reason is the api beauty;
then I say: then don't use __get because it will have the opposite effect. It will make the API ugly. Magic is complicated and non-obvious and that's exactly what leads to those WTF moments:
To come to an end now:
i don't see any real disadvantages, I guess it's worth it
You hopefully see them now. It's not worth it.
For additional approaches to Lazy Loading, see the various Lazy Loading patterns from Martin Fowler's PoEAA:
There are four main varieties of lazy load. Lazy Initialization uses a special marker value (usually null) to indicate a field isn't loaded. Every access to the field checks the field for the marker value and if unloaded, loads it. Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real the object and then delegates. Value Holder is an object with a getValue method. Clients call getValue to get the real object, the first call triggers the load. A ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.
These approaches vary somewhat subtly and have various trade-offs. You can also use combination approaches. The book contains the full discussion and examples.
If your capitalization of the class names and the key names in $prop matched, you could do this:
class Dummy {
private $props = array(
'Someobject' => false,
//etc...
);
function __get($name){
if(isset($this->props[$name])){
if(!($this->props[$name] instanceof $name)) {
$this->props[$name] = new $name();
}
return $this->props[$name];
}
//trigger_error('property doesnt exist');
//Make exceptions, not war
throw new Exception('Property doesn\'t exist');
}
}
And even if the capitalization didn't match, as long as it followed the same pattern it could work. If the first letter was always capitalized you could use ucfirst() to get the class name.
EDIT
It's probably just better to use plain methods. Having a switch inside a getter, especially when the code executed for each thing you try to get is different, practically defeats the purpose of the getter, to save you from having to repeat code. Take the simple approach:
class Something {
private $props = array('Someobject' => false);
public getSomeobject() {
if(!($this->props['Someobject'] instanceof Someobject)) {
//Instantiate and do extra stuff
}
return $this->props['Someobject'];
}
public getSomeOtherObject() {
//etc..
}
}
I'm using __get() to make some of my properties "dynamic" (initialize them only when requested). These "fake" properties are stored inside a private array property, which I'm checking inside __get.
Anyway, do you think it's better idea to create methods for each of these proprties instead of doing it in a switch statement?
The way you ask your question I don't think it is actually about what anybody thinks. To talk about thoughts, first of all it must be clear which problem you want to solve here.
Both the magic _get as well as common getter methods help to provide the value. However, what you can not do in PHP is to create a read-only property.
If you need to have a read-only property, you can only do that with the magic _get function in PHP so far (the alternative is in a RFC).
If you are okay with accessor methods, and you are concerned about typing methods' code, use a better IDE that does that for you if you are really concerned about that writing aspect.
If those properties just do not need to be concrete, you can keep them dynamic because a more concrete interface would be a useless detail and only make your code more complex than it needs to be and therefore violates common OO design principles.
However, dynamic or magic can also be a sign that you do something wrong. And also hard to debug. So you really should know what you are doing. That needs that you make the problem you would like to solve more concrete because this heavily depends on the type of objects.
And speed is something you should not test isolated, it does not give you good suggestions. Speed in your question sounds more like a drug ;) but taking that drug won't give you the power to decide wisely.
Using __get() is said to be a performance hit. Therefore, if your list of parameters is static/fixed and not terribly long, it would be better performance-wise to make methods for each and skip __get(). For example:
public function someobject() {
if(!($this->props[$name] instanceof Someobject))
$this->props[$name] = new Someobject;
// do stuff to initialize someobject
}
if (count($argv = func_get_args())) {
// do stuff to SET someobject from $a[0]
}
return $this->props['someobject'];
}
To avoid the magic methods, you'd have to alter the way you use it like this
$bar = $foo->someobject; // this won't work without __get()
$bar = $foo->someobject(); // use this instead
$foo->someobject($bar); // this is how you would set without __set()
EDIT
Edit, as Alex pointed out, the performance hit is millisecond small. You can try both ways and do some benchmarks, or just go with __get since it's not likely to have a significant impact on your application.
In a PHP app, would it be a bad idea to run my saving code during an object's destructor? I ask because if it's ok then I could add a save() call in the destructor of the parent Model class and save myself the trouble of remembering anywhere else.
I know that doing this does work, as I have an entire app (albeit a poorly written one) running on it. But are there good reasons not to do it?
The destructor is not guaranteed to be called in any order. What happens if your page is unloading and all the objects instructors start to get called. you never know if the database object you need to use is still valid, or if it has been unloaded.
IMO, adding such functionality into the destructor is not the best choice. The reason, one very important to me, is increased code complexity and reduced readability. A third person new to the project will end-up spending quite a bit of time figuring out whats been happening.
Having said that, whether it is theoretically good or bad, its down to the programming logic being employed. If the class in question would be extended at a latter stage, then the save() in your destructor might give you some grief; again depends on what you are trying to achieve.
Actually, PHP will try to destroy objects in the right order, so it is quite safe (considering if you're trying to save something, it means you are still hodling a reference to it). What you need to be aware of is that throwing an exception during a destructor will cause a fatal error even if there's a catch. You can play with examples, and it's not easy to make PHP fail with normal referencing, here's a simple way to make PHP crazy with destructors so it doesn't know how to end them, but as I said, it's not something you normally find in your code:
<?php
class A
{
public $b;
function eco()
{
echo 'AAA';
}
function __destruct()
{
$b->eco();
}
}
class B
{
public $a;
function eco()
{
echo 'BBB';
}
function __destruct()
{
$a->eco();
}
}
$a = new A;
$b = new B;
$a->b = $b;
$b->a = $a;