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.
Related
I thought I was pretty comfortable with the fundamentals of OOP in a few languages like Python and PHP, but I got a bit confused trying to figure out what's going on in certain situations in CakePHP.
Say for example I'm in a controller called TestsController but I want to look up data from another controller, such as OthersController. I'd do something like this
$this->loadmodel('Other');
$this->Other->find('all');
I understand that $this is a reference to the object of the class you're in, and Other clearly refers to OthersController, but what exactly is Other? Is it some kind of initialization variable? An Object? Something else? Does $this->Other become an object itself? If so, how does PHP/CakePHP do this, or is it just something inherent of PHP that it just "knows" to do so.
I found this example of something called method chaining, but it looks like using one object to call many actions.
<?php
class MyClass{
public $prop1 = "I'm a class property!";
}
$obj = new MyClass;
echo $obj->prop1; // Output the property
?>
I understand with $obj you're accessing $prop1, but what's happening in CakePHP when there's something like $obj->SomeModel->action()?
If possible could you modify that little OOP example to mimic that of CakePHP?
In your example, $this->loadmodel('Other') populates $this->Other with the model object you asked for. So you can treat it as a typical object (call methods, access properties, etc.). You could even do this:
$this->loadmodel('Other');
$other = $this->Other;
$other->find('all');
$other is assigned a reference to $this->Other, so they will both act exactly the same.
As for chaining, let's say that $this->Other->find('all') returns another object of the type ResultSet, and the ResultSet class has the method getNumResults(). Chaining means you could do something like this:
echo $this->Other->find('all')->getNumResults();
As long as each method you're calling in the chain returns another object, you can keep calling the methods on that object.
$this->loadmodel('Other');
$this->Other->find('all');
I understand that $this is a reference to the object of the class
you're in, and Other clearly refers to OthersController, but what
exactly is Other? Is it some kind of initialization variable? An
Object? Something else? Does $this->Other become an object itself? If
so, how does PHP/CakePHP do this, or is it just something inherent of
PHP that it just "knows" to do so.
Other does not refer to OthersController, it refers to an Other model. Cake automatically loads the model named Other inside OthersController (it infers the relationship from their names), but inside any other controller $this->Other is not available unless you explicitly load the model by calling $this->loadmodel('Other') first. This is what the snippet above does.
Even when the model is loaded by Cake, this is still done explicitly (even though it's transparent to you) inside the constructClasses method.
From the point that the model is loaded onwards, it is accessible through $controller->ModelName as any other object.
Adding to Jon answer, PHP frameworks do a lot of MAGIC.
I find myself learning about magic getter / setters, and much things like that.
For example, the Yii ActiveRecord, each row in database is an object.
So you, for example do this to load a record:
$user1 = User::model()->findByPk('1');
$user1->name = 'Jorge';
$user1->save;
So, it will take time until you start to actually comprehend what the framework is doing.
I bet that when you do $this->load->('model'); it fills something, it loads the model in some structure, maybe an array, and replacing the magic getters/setters you can actually say that when going for $this->modelName you actually are going to $this->modelsArray['modelName'].
An example without data validation:
class MyClass
{
private $arrayExample = array( 'value1'=>123, 'value2'=>234);
public function __get( $property )
{
if( array_key_exists( $this->arrayExample, $property )
return $this->arrayExample[$property];
return 'undefined';
}
}
$class = new MyClass;
echo $class->value1; //outputs 123
echo $class->value0; //outputs undefined
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 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.
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"