I have a very strange problem, when I try to var_dump (or print_r) a Doctrine Object, my Apache responses with an empty blank page (200 OK header). I can var_dump a normal php var like:
$dummy = array("a" => 1, "b" =>2);
And it works fine. But I can't with any object from any Doctrine class, (like a result from $connection->query(), or an instance of a class from my object model with Doctrine).
Anybody knows why this happens?
Lazy load proxies always contain an instance of Doctrine’s EntityManager and all its dependencies.
Therefore a var_dump will possibly dump a very large recursive structure which is impossible to render and read. You have to use \Doctrine\Common\Util\Debug::dump() to restrict the dumping to a human readable level. Note that the default depth for this function is set to 2 (it's the second parameter)
Use the toArray method of the Doctrine_Record class
var_dump($doctrine_record->toArray());
will only display the DB fields and avoid dumping the complete Doctrine internals (which contains self reference/recursion btw)
I've had that sometimes when trying to print_r() a self-referencing object - it gets into a loop and runs out of memory. Possibly that's what's happening to you.
Try increasing the memory limit (ini_set('memory_limit', '256M');) and see if that fixes it.
Edit: I don't think there's an actual fix for this - it's PHP's internal var_dump / print_r that don't limit depth on recursion (or don't do it properly, at least). If you install the XDebug extension, this can replace the built-in var_dump with a version that handles recursion much better.
You can use toArray if you are sure the object is an instance of Doctrine_Collection.
Xdebug does not help with doctrine records.
The way I suggest is implementing a custom recursive function to print object, that use Doctrine_Record::toArray() when neeeded
function var_dump_improved()
{
foreach (func_get_args() as $arg) {
if ($args instanceof Doctrine_Collection) {
print_r($arg);
} else if ( $arg instanceof Traversable || is_array($arg) ) {
// do a foreach and recall var_dump_improved on subelements
} else if (...) {
// other types
}
}
}
Some recursive function to debug with max nesting levels are here
http://php.net/manual/en/function.var-dump.php
Look at the comments, look for "recursion"
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
Why UnexpectedValueException is thrown in session_start()?
I have object which has property of SPLObjectstorage. That object is assigned to session like
$_SESSION['foo'] = $barObject;
I suspect that internal session serializing facing problem to decode it. I store the session in database and it looks like it is serializing the objectStorage but can not decode it.
Sample session data
self|O:4:"User":8:{s:5:"�*�id";N;s:7:"�*�nick";N;s:13:"�*�reputation";i:1;s:11:"�*�password";N;s:8:"�*�email";N;s:7:"�*�crud";O:10:"CRUDobject":2:{s:13:"�*�fieldCache";a:0:{}s:13:"�*�dependency";r:1;}s:7:"�*�auth";N;s:11:"�*�roleList";C:11:"RoleStorage":23:{x:i:1;N;,r:13;;m:a:0:{}}}
Rolestorage is extends of SPLObjectstorage
session_decode() on above string also returns false any ideas?
removing the roleList attribute makes it serialize properly.
If I separately do
$sr = serialize($roles); // $roles is RoleStorage object
var_dump($sr);
var_dump(unserialize($sr));
It prints string 'C:11:"RoleStorage":22:{x:i:1;N;,r:3;;m:a:0:{}}' (length=46) and then fails with same message while unserializing. I have no clue why this is happening.
Note: while attaching object to RoleStorage I used the object itself as data. Means it is stored as reference. I don't know how (if) does serialize() handles internally this.
Objects with the name RoleStorage raises a couple of flags for me. Often, this object does contain a resource of sorts, or a reference to a built-in PHP object. Resources can't be serialized, nor can some PHP built-in types be serialized. Consider implementing the magic __sleep and __wakeup methods in those cases.
Say you have a PDO reference somewhere in the RoleStorage object, then these magic properties might look something like this:
public function __sleep()
{
$this->pdo->commit();//commit && close
$this->pdo = array($dsn, $user, $pwd, array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
)
);
return serialize($this);
}
public function __wakeup()
{
$this->pdo = new PDO($this->pdo[0], $this->pdo[1], $this->pdo[2], $this->pdo[3]);
}
But since you say the RoleStorage objects is a child of SPLObjectStorage, you'd be better off overriding the SPLObjectStorage's implementation of the Serializable interface:
It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.
I'd suggest declaring iterating over all properties in the child class's serialize method, and store that data into an array. Return that array serialized, and unserialize that string back in the unserialize method, reassigning every property in a loop.
If SPLObjectStorage has private properties, you can get to them like so:
class RoleStorage extends SPLObjectStorage
implements Serializable
{
public function serialize()
{
return serialize((array) $this);
}
public function unserialize($string)
{
$array = unserialize($string);
foreach($array as $property => $value)
{
$property = explode("\0", $property);//private & protected properties
$this->{end($property)} = $value;
}
}
}
For details on the explode("\0",$property);, refer to the manual, or check this question
I have no clue why this is happening
In your PHP version and with your concrete script it is not possible to serialize an object based on SPLObjectStorage unless you take care of the serialization your own.
If you see this part of your serialized string:
C:11:"RoleStorage":23:{x:i:1;N;,r:13;;m:a:0:{}}
This represents the RoleStorage object. The big C at the beginning stands for:
C - Object implementing serializeable Interface
So the object itself is responsible here about the serialization and unserialization. You can normally expect this works, however not all software is without bugs.
In your case it looks like PHP made a mistake. The internal format here is:
x:i:1;N;,r:13;;m:a:0:{}
^^^^^^^
And the problem is at the highlighted position, this requires a serialized object, not NULL. And it's not comma-terminated with a reference (r:13 here), but with null (N) to be working.
So looks like a hick-up triggered by referencing some earlier object (take care that this referencing is not the same as references / variable aliases in userland PHP).
So how to go on?
It's time you start to isolate your problem and create a self-contained, reproduceable example out of it. This is necessary to further look into the issue as you see it. This is important for two reasons:
If this is a bug in PHP, it should be reported, a regression test written and added to PHP Q&A and then the bug fixed (if not yet fixed).
If you're looking for a workaround, reproducing the original problem is necessary to create a workaround quickly and easily.
I did run some tests for a work-around, however so far I'm not able to reproduce your issue so I can not really suggest how to work around the issue as I don't have it here.
There has been a bug closed recently regarding an issue similar to this. Depending on the php version you are running, you may still be affected. The affected version is 5.3.15.
Excerpt:
[2012-07-27 16:08 UTC] j dot henge-ernst at interexa dot de
The problem is that the unserialize of ArrayIterator (and also maybe
ArrayObject or other SPL classes) can not dereference object
references.
If you are affected by this bug, then you may be right that it is related to dereferencing. Perhaps try a newer version of PHP to see if it still happens.
I was reading around about the Observer pattern, and found a dated article. Having read through, I noticed an interesting mention in this paragraph:
The key methods to look at here are attach(), detach(), and notify(). attach() and detach() handle adding and removing observers. We use a little trick here. Objects quoted in string context resolve to a unique identifier (even if __toString() is defined). You can use this fact to build keys for an associative array. The notify() method cycles through all attached observers, calling update() on each. The UploadManager class calls notify() whenever it has something important to report on upload and on error, in this case.
Which references this example:
function attach(UploadObserver $obs) {
$this->observers["$obs"] = $obs;
}
Now as mentioned, this article is dated. Casting objects to strings of course no longer works in this manner (I run 5.3.6 on my dev box, and push it for all client projects) but I'd like to achieve similar functionality. I can only think of (something like) this:
function attach(Observer $observer){
$this->_observers[md5(serialize($observer))] = $observer;
}
function detach(Observer $observer){
unset($this->_observers[md5(serialize($observer))]);
}
I'm curious, are there any other efficient ways to achieve this; creating a unique key from the object itself.
Caveat: I don't want to get into defined keys, I use those often enough with other repositories and such, implementing __set($key, $value), etc.
Note: I understand MD5 isn't ideal.
Update: Just found spl_object_hash, and I assume this is likely my best choice, however feel free to share your thoughts.
You're right that does not work that way any longer. You might want to use some other function instead: spl_object_hash()
function attach(Observer $observer){
$this->_observers[spl_object_hash($observer)] = $observer;
}
function detach(Observer $observer){
unset($this->_observers[spl_object_hash($observer)]);
}
The serialization based approach has a design problem btw: I stops working when objects are identical by value or in other words if objects return the same serialized value, e.g. NULL. This is fully controllable by the objects themselves when they implement the Serializable interface.
Have you tried the SPL object hash function?
Alternatively you could use SplObjectStorage directly.
Like:
function __construct(...){
$this->_observers = new SplObjectStorage;
}
function attach(Observer $observer) {
$this->_observers[$observer] = $observer;
}
function detach(Observer $observer){
unset($this->_observers[$observer]);
}
I'm trying to create a __set for an object in PHP that works with multidimensional arrays. Is this even possible?
I would like to be able to something like the following: $post->comments[0]['uid']=3;. However, comments is actually going to be a key in a private cache variable $_cache['comments']=array(). It'd be nice if the __set function could somehow get both the base key (comments) and the index (0) as well as the key/value it is setting (uid/3). However, that's not possible.
I've thought about making $_cache['comments'] and array of ArrayObjects but that wouldn't let me define a custom _get/_set overload. Instead, I think that I might end up having to create a new Comments object and then fill the array with those. However, I really wouldn't like to do this and it'd be sweet if somehow PHP could handle nested arrays in __set overloads.
I'm using Mongo and would like if I could just have one single object for each document. However, arrays objects in Mongo are creating a bit of a problem for me. I would like to just handle them as an array in PHP but that doesn't seem possible. The setter needs to take $post->comments[0]['uid']=3 and update both the cache as well as setting $this->data['comments'][0]['uid']=3.
I know that if comments was an array of objects I could do this:
$post->comments[0]->uid=3;
///Sets $_cache['comments'][0]->uid=3;
And it would work because the getter for comments would return the array of objects and allow it to access the uid property. I could then have a getter/setter within the comments object that would somehow edit the $post->data through a pseudo "friend" function/hack. However, I don't see an easy way of accomplishing this with arrays....
Any advice?
That's more complex than you actually imagine. You can accomplish what you want with a heap of workarounds, but it's seldomly worth the effort.
If ->comments itself is resolved by a getter method, than assigning something to the [0] subarray won't actually end up in the private property. And ->comments[0]= will not even invoke your setter method. Instead this is a read access.
To make this work at all you would have to make your __get method return an reference of & $this->_cache['comments'].
If you want to intercept set accesses in that comments array you would indeed need ArrayObject. The difference is that this requires to override offsetGet and offsetSet instead of __get and __set. But again, since you are accessing a further subarray, the __get method will actually be used and you need to return another reference, or yet again a level of ArrayObject workaround goo.
I jumped through some of these hoops when building my own PHP wrapper class.
https://github.com/gatesvp/MongoModel
It's still in the the works, but it does handle some basic "map this object to DB".
There's virtually nothing worthwhile written in PHP chat rooms or the php documentation that's going to be useful to you, Adam. Most of the suggestions tend along the lines of implementing interface ArrayAccess or extending class ArrayObject, both in the SPL. In fact, there is a surprisingly straightforward solution to your problem: $post->comments[0]['uid']=3 using overloaded setter __set().
Define private $comments = array(); in class post. For convenience, use a text key for the first subscript of $comments: here, integer 0 becomes, say, "zero". You then invoke the setter as follows:
$post->zero = ['uid', 3];
This invokes the magic setter because there is no publicly declared property $zero in class post: "The overloading methods are invoked when interacting with properties or methods that have not been declared or are not visible in the current scope." (PHP 5 man page on Overloading.)
The setter can also be setComments(), a convenience because you won't have to discriminate among incoming properties to identify those intended for array comments, but the calling syntax becomes less natural.
Your overloaded, auto-magical function __set receives two arguments: a property and a value:
public function __set($property, $value) {
very reminiscent of Crockford's JSON protocol. It is helpful to think of it in those terms.
Since property "zero" that you sent in does not exist in classpost, it needs to be trapped, and my preferred method, since the first subscript in property comments will likely have several values, is to define a private array of supported subscript values in post:
private $indices = [
"zero" => 0,
"one" => 1,
"two" => 2,
"three" => 3
];
When the index for comments arrives in __set() as $property, it is verified to exist in $indices. Now you simply iterate through the array supplied in $value, extract
uid and its corresponding value, then assign to $comments as follows:
public function __set($property, $value) {
if (array_key_exists($property, $this->indices) && is_array($value))
foreach ($value as $uid => $uid_value)
$this->comments[$this->indices[property]][$uid] = $uid_value;
else
...
}
with $this->indices[property] being used to extract the integer value 0 to be used to
index the first dimension of comments, and $uid_value extracted with value int 3 to be assigned.
The approach outlined here is not a gimmick, workaround or clever trick. It's a straightforward design technique intended to work with one of SPL's facilities and can, in principle, be extended to arrays of arbitrary dimension. I have the design implemented in a production system so, if you're still having difficulty, post here and I'll help you to debug your application. Best of luck!
I believe the closest you can do for overloading some properties is to use the magic method __set() defined here: http://us.php.net/__set
I am not sure you can handle the [0] before it gets taken by the PHP compiler...
So your other solution would be to transform comments into a method
public function comments($id) {
return $this->obj[$id]; // Obj
}
And the object you return has the __set property
class Obj {
private $id;
public function __set($key, $value) {
if($key === 'uid') {
$_cache = $GLOBALS['_cache'];
$_cache['comments'][$this->id]->uid = $value;
}
}
}
There is a lot of code missing here, but you can figure out how to do it with this __set method()
Create a function instead of trying to hack it on top of something that isn't even meant for that.
public function setCommentUid($commentId, $uid) {
$this->_cache['comments'][$commentId]->uid = $uid;
}
//then...
$post->setCommentUid(0, 3);
This makes it much simpler to use the class and it's much easier to see what it does.
In my task would be very nice to write a kind of objects serialization (for XML output). I've already done it, but have no idea, how to avoid recursive links.
The trouble is that some objects must have public(!) properties with links to their parents (it's really nessecary). And when I try to serialize a parent object which agregates some children - children with links to parent do recursion forever.
Is there a solution to handle such recursions as print_r() does without hacks?
I can't use somthing like "if ($prop === 'parent')", because sometimes there's more than 1 link to parents from different contexts.
Write your own serialization function and always pass it a list of already-processed items. Since PHP5 (I assume, you are using php5) always copies references to an object, you can do the following:
public function __sleep() {
return $this->serialize();
}
protected function serialize($processed = array()) {
if (($position = array_search($this, $processed, true)) !== false) {
# This object has already been processed, you can use the
# $position of this object in the $processed array to reference it.
return;
}
$processed[] = $this;
# do your actual serialization here
# ...
}