In PHP, you can normally place an object in an array, like so:
class Car{}
$car = new Car();
// This runs without error
$array['vehicle'] = $car;
I have a custom MVC framework I've built, and I need the controller to get an ORM object from the model, so it can pass that to the view. So, I initialize my user object:
$user = new User(2);
Now, I want to put that user object into a $data array so it can be passed to the view:
($user->data returns an ORM object)
$array['user'] = $user->data;
The problem is, after doing this, I receive the following error:
Object of class ORM could not be converted to string
What am I doing wrong? Is there something I'm missing?
Thanks for any help in advance.
Edit: Here's what $user->data refers to, this is from the constructor of class User:
$this->data = ORM::for_table("users")->find_one($this->user_id);
(I'm using Idiorm as an ORM)
If you get an error message like:
Object of class ORM could not be converted to string
The first question you should ask is, "why does it have to be converted to a string"? An array can take a string just fine, so you can guess that $data is actually a string and PHP thinks you want to modify $data[0].
As you've seen, dynamically typed languages can leave befuddled if you aren't careful.
When your variables show suspect behavior, try to see what's actually in them using var_dump().
It's also a good idea to explicitly initialize arrays (eg: $my_array = array();) before using them.
Related
Please pardon me for not knowing what the terminology of what I'm asking for is. I don't quite know what you'd call it so bear with me.
In Laravel 4 or 5, is there some way to set a default template for an Eloquent Model? For a long time, I've been writing lines like this: (just using book system as example)
$book = Sentry::getUser()->books()->find(14);
return View::make( "books.show" )->withBook($book);
Is there any way that I can make return $book; expand into return View::make( "books.show" )->withBook($book); ?
What you're asking makes sense. However, it doesn't really fit with Laravel's view of the world -- so keep in mind you're sort of striking out on your own.
When you
return View::make( "books.show" )->withBook($book);
from a route closure or controller action, Laravel treats anything returned as a view. This include regular strings -- i.e., you can do something like
return '<p>Foo</p>';
and Laravel will render the HTML fragment.
If you try this with a regular PHP object,
$o = SomeObject;
return $o;
you'll see an error something like this
The Response content must be a string or object implementing __toString(), "object" given.
This error happens because Laravel has tried treating the returned object as a string, and PHP has no default way of rendering an object as a string.
What PHP does have is a way for you, (the programmer), to tell it (PHP) how PHP should render an object if some code (Laravel) treats that object as a string. If you add a __toString method to any object, then PHP will use this method to render a string for the object. Try the following small program
<?php
class SomeObject
{
public function __toString()
{
return 'I am a rendered SomeObject';
}
}
$object = SomeObject;
// cast the object as a string ("treat" above)
echo (string) $object;
So -- this means you can, in any object, embed the logic for rendering a view. Assuming your book class is named Book, try the following
class Book
{
//...
public function __toString()
{
return View::make( "books.show" )->withBook($this);
}
}
and then in your route closure/controller action
$book = Sentry::getUser()->books()->find(14);
return $book;
One thing to keep in mind -- since there can only be one __toString definition, if you're extending classes that implement __toString, you may break someone else's functionality. To avoid that try using some sort of conditional rendering.
Hope that helps!
Update. Per the comments below. I can't speak to the Accepts header, although my instincts say Laravel doesn't do this as there's not a culture in PHP of looking at the Accepts header. I could be wrong. As for using it yourself in __toString, I'd nix that as well -- if a user is using your object during an Accepts JSON request, but needs to render it as a non JSON string for some reason, your code would interfere with that. Better to give you object rendering contexts, and then the people who render your object choose how it renders in the route closure, controller action, or a "IoCrewrite" of the rendering methods themselves.
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
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.
Have to do some php coding but I am totally new to it. The question is I was trying to get a property value from an object called $mgm_member, this object is from wordpress plugin which records website's member info. First I use:
var_dump(get_object_vars($mgm_member));
The results shows that this obeject has 37 properties and all of them have values. Than I tried use
var_dump($mgm_member->phone);
The result is null. I tried many other properties but only the first property of this object have value. Can anyone help me? Thank you very much.
well I suppose if the scope of "phone" is private var_dump will not be able to access and view it, is it? In my case, I can view all properties and their values using var_dump() function but when I tried to get one property it just doesn't work. However,I can get the first property "id" using the same code
echo $mgm_member->id;
That is really weird.
As the property is 'private' you will need to make a function to access and return it.
A dump may display them but you WILL NOT be able to directly access a 'private' property.
class .... {
public function getPhone()
{
return ($this->phone);
}
}
then:
echo $mgm_member->getPhone();
var_dump(get_object_vars($mgm_member)); shows the object variables. If you are getting data with this line of code, you have data in your object.
You can access properties of a variable in your code with $mgm_member->phone - why not do so?
If you want to place the data into a variable you can use something like this:
$myVar=$mgm_member->phone;
but that defeats the purpose of OOP. Why not refer to it as exactly $mgm_members->phone all the way through your code?
Edit: As you point out, the property is private which means that only the object itself can use it. You could get around this by modifying the object, but this may be a nasty approach - it is private for a reason. Can you not use functions within the object to display the values? Alternately, is there a function you can use in the object to return you a clone of the object with different property attributes?
Please stop me if i am doing something wrong. It works but somehow it doesn't appear the right way to me... Look at the member function call in talks.php. Does this look right to you? Is there a better way to solve that? Thanks.
show.php
I am passing my user class by reference:
$talks = new talks($comments, $user);
talks.php:
[...]
function __construct($comments, &$user)
{
//Passing user class
$this->user = $user;
[...]
if ($this->user->is_loaded()){}
This looks a-ok to me. What problem do you see with it?
In php 5, objects are always passed by reference.
From http://www.php.net/manual/en/language.oop5.references.php:
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.
So, you should not need the "&" operator in the parameter list of your constructor.