PHP OOP find object by attribute - php

I'm trying to pull a random instance of a class (object) by generating a random number and checking object ids against that number.
I've found a lot of info on how to retrieve an object attribute (specifically here it's the id) but not without knowing which object first.
So within my class I've got
public function getID() {
return $this->id;
}
But
getID()
only works if I use it as
$object->getID()
Is there a way to do something similar but for every object in a class, without specifying the objects?
I'm trying to avoid having to write if/then for every object in the class.

You could set up an array of objects, then iterate over the array and call the getID() method on each object. If your array of objects is called $myObjects...
foreach($myObjects as $object) {
$object->getID(); //And do something with it
}
However, if you want to pick a random object out of a set of objects, testing a whole bunch of them to see if they are the object you picked isn't really ideal. You'd be better off putting them into an array and using array_rand() to select a random object out of the array.
What's your purpose for doing this? That may indicate a better way to approach this.

I think you'd have to have planned for this eventuality, then loop thru the candidate objects as #Surreal Dreams; suggests.
See Get all instances of a class in PHP

Related

Get property from an object in PHP

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?

Merging 2 custom objects

I need to merge 2 custom objects of the same type. Here's what the class looks like:
<?php
class a{
public $one;
public $two;
public function doSomething(){
//do stuff
}
}
I have got 2 instance of a that I need to merge together. I understand that I can use array_merge like this:
$result = (object)array_merge((array)$a1, (array)$a2);
But the problem is that I need the result to be of the a class and not stdObj.
If I do:
$result->doSomething()
an error results: PHP Fatal error: Call to undefined method stdClass::doSomething()
Since we cannot type cast to non-primitives, one cannot do: $result = (a)array_merge((array)$a1, (array)$a2);
Besides using a loop to iterate through one object and get and set values, are there more performant or neater ways to do this?
array_merge() is not what you want. As you rightly point out, they are not arrays
It sounds to me like you need what C++ calls a copy constructor.
Bascially, you are going to have to code it yourself, so add a function called merge() to your class which takes a single paramter which is an other obect of that class.
To code it you just need to assign each individual field of the parameter's data to your object's data.
read the PHP manual on object cloning for more info.

Objects to strings, unique keys in PHP

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]);
}

Using a Setter with Multidimensional Arrays

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.

PHP SPL - is there any interface or class to control what happens when casting to array?

So by implementing Iterator, ArrayAccess, and Countable built-in interfaces, we have control over what happens inside an object when it's used in foreach loops or if a property is accessed as if it were an array index ($object['id']).
For example, if you wanted, you could set it up so $object['version'] += 1 could automatically increment version field in the database.
What's missing is casting the object to array. Is there any interface or class that allows control over what happens when you do: (array) $object? Any built-in interface or class at all, no matter how obscure? For example: if I wanted (array) $object to return $this->propertyArray instead of the normal object to array conversion of dumping all public object properties?
Note: something like requiring calling $object->toArray() by method name doesn't count, as the idea is to minimize the outside differences between an array and object as much as possible.
no there is not , because toArray() is not an magic function like __toString(); where casting works e.g
$foo = (string) $myObect;
you have to specify toArray() and inside it return your array , may be in future __toArray() might come.
You could add a method like this
public function toArray() {
return get_object_vars( $this );
}
See here. Or check SplFixedArray::toArray.

Categories