I'm currently looking for a way to set a new instance to be the same as an exists instance if already exists.
Is there any easy way as doing something like:
Class Check {
public static $instance = null;
public $name = "";
function __construct($name) {
if (self::$instance !== null) {
$this = self::$instance;
} else {
$this->name = $name;
self::$instance = $this;
}
}
}
$a = new Check("A");
$b = new Check("B");
echo $a->name." = ".$b->name;
to achieve it?
what it returns:
A =
What I want it to return:
A = A
Is it possible to accomplish that?
I want that all variables of the older instance to maintain the same.
Thanks in advance.
This is the only two ways I found to achieve it
#1 Using get_object_vars
You can use get_object_vars that returns all public variables that a specific object contains, then you can iterate them using foreach
example:
if (self::$instance !== null) {
foreach (get_object_vars(self::$instance) as $prop=>$value) {
$this->{$prop} = $value;
}
}
#2 Is using PHP reflection, I find it less useful in my case because it doesn't reflect properties that was generated on the go
Note: Reflection will reflect also properties such as private|static and so. you can filter them inside the getProperties function or iterate through all properties and use boolean methods filter between them.
example:
if (self::$instance !== null) {
$reflection = new ReflectionClass(self::$instance);
$props = $reflection->getProperties();
foreach ($props as $prop) {
$this->{$prop->name} = $prop->getValue(self::$instance);
}
}
so if you do self::$instance->newVar = 1;
it won't be reflected. but in the #1 Option it will be.
Hope it will help someone out there.
I would like to get all the instances of an object of a certain class.
For example:
class Foo {
}
$a = new Foo();
$b = new Foo();
$instances = get_instances_of_class('Foo');
$instances should be either array($a, $b) or array($b, $a) (order does not matter).
A plus is if the function would return instances which have a superclass of the requested class, though this isn't necessary.
One method I can think of is using a static class member variable which holds an array of instances. In the class's constructor and destructor, I would add or remove $this from the array. This is rather troublesome and error-prone if I have to do it on many classes.
If you derive all your objects from a TrackableObject class, this class could be set up to handle such things (just be sure you call parent::__construct() and parent::__destruct() when overloading those in subclasses.
class TrackableObject
{
protected static $_instances = array();
public function __construct()
{
self::$_instances[] = $this;
}
public function __destruct()
{
unset(self::$_instances[array_search($this, self::$_instances, true)]);
}
/**
* #param $includeSubclasses Optionally include subclasses in returned set
* #returns array array of objects
*/
public static function getInstances($includeSubclasses = false)
{
$return = array();
foreach(self::$_instances as $instance) {
if ($instance instanceof get_class($this)) {
if ($includeSubclasses || (get_class($instance) === get_class($this)) {
$return[] = $instance;
}
}
}
return $return;
}
}
The major issue with this is that no object would be automatically picked up by garbage collection (as a reference to it still exists within TrackableObject::$_instances), so __destruct() would need to be called manually to destroy said object. (Circular Reference Garbage Collection was added in PHP 5.3 and may present additional garbage collection opportunities)
Here's a possible solution:
function get_instances_of_class($class) {
$instances = array();
foreach ($GLOBALS as $value) {
if (is_a($value, $class) || is_subclass_of($value, $class)) {
array_push($instances, $value);
}
}
return $instances;
}
Edit: Updated the code to check if the $class is a superclass.
Edit 2: Made a slightly messier recursive function that checks each object's variables instead of just the top-level objects:
function get_instances_of_class($class, $vars=null) {
if ($vars == null) {
$vars = $GLOBALS;
}
$instances = array();
foreach ($vars as $value) {
if (is_a($value, $class)) {
array_push($instances, $value);
}
$object_vars = get_object_vars($value);
if ($object_vars) {
$instances = array_merge($instances, get_instances_of_class($class, $object_vars));
}
}
return $instances;
}
I'm not sure if it can go into infinite recursion with certain objects, so beware...
I need this because I am making an event system and need to be able to sent events to all objects of a certain class (a global notification, if you will, which is dynamically bound).
I would suggest having a separate object where you register objects with (An observer pattern). PHP has built-in support for this, through spl; See: SplObserver and SplSubject.
As far as I know, the PHP runtime does not expose the underlying object space, so it would not be possible to query it for instances of an object.
Basically, I need to store past versions of this object in its current state in an array. Something like:
public class MyClass{
$n = 0;
$array = array()
storeOldVersion(){
$this->array[] = clone $this;
}
}
I know I do something like "clone $this->n", but how can I literally clone the MyClass object itself, and store it into an array the MyClass object holds?
Thanks
What you proposed actually works, except you had some incorrect code. The following version works:
class MyClass{
protected $n = 0;
public $array = array();
public function storeOldVersion(){
$this->array[] = clone $this;
$this->n = 2;
}
}
$a = new MyClass();
$a->storeOldVersion();
echo "<pre>";
var_dump( $a ); // retuns class with n = 2
var_dump( $a->array ); // return array with one class having n = 0
A word of warning though
If you call storeOldVersion() more than once, as is, it will recursively clone the "array" that contains other clones and your object could get pretty massive exponentially. You should probably unset the array variable from the clone before storing it in the array..
e.g.
$clone = clone $this;
$clone->array = array();
$this->array[] = $clone;
You can also try serialization for complicated objects. It will store the object as a string. After all you can unserialize string to object with the past state.
http://php.net/manual/en/language.oop5.serialization.php
With a little cleanup, your code works. However, you probably want to clean out the $array of old versions in the old versions you store, otherwise you're going to end up with a huge recursive mess on your hands.
<?php
class MyClass{
private $n = 0;
private $array = array();
public function storeOldVersion(){
$clone = clone $this;
$clone->array = null;
$this->array[] = $clone;
}
}
$c = new MyClass();
$c->storeOldVersion();
var_dump($c);
We have two PHP5 objects and would like to merge the content of one into the second. There are no notion of subclasses between them so the solutions described in the following topic cannot apply.
How do you copy a PHP object into a different object type
//We have this:
$objectA->a;
$objectA->b;
$objectB->c;
$objectB->d;
//We want the easiest way to get:
$objectC->a;
$objectC->b;
$objectC->c;
$objectC->d;
Remarks:
These are objects, not classes.
The objects contain quite a lot of fields so a foreach would be quite slow.
So far we consider transforming objects A and B into arrays then merging them using array_merge() before re-transforming into an object but we can't say we are proud if this.
If your objects only contain fields (no methods), this works:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
This actually also works when objects have methods. (tested with PHP 5.3 and 5.6)
foreach($objectA as $k => $v) $objectB->$k = $v;
You could create another object that dispatches calls to magic methods to the underlying objects. Here's how you'd handle __get, but to get it working fully you'd have to override all the relevant magic methods. You'll probably find syntax errors since I just entered it off the top of my head.
class Compositor {
private $obj_a;
private $obj_b;
public function __construct($obj_a, $obj_b) {
$this->obj_a = $obj_a;
$this->obj_b = $obj_b;
}
public function __get($attrib_name) {
if ($this->obj_a->$attrib_name) {
return $this->obj_a->$attrib_name;
} else {
return $this->obj_b->$attrib_name;
}
}
}
Good luck.
I understand that using the generic objects [stdClass()] and casting them as arrays answers the question, but I thought the Compositor was a great answer. Yet I felt it could use some feature enhancements and might be useful for someone else.
Features:
Specify reference or clone
Specify first or last entry to take precedence
Multiple (more than two) object merging with syntax similarity to array_merge
Method linking: $obj->f1()->f2()->f3()...
Dynamic composites: $obj->merge(...) /* work here */ $obj->merge(...)
Code:
class Compositor {
protected $composite = array();
protected $use_reference;
protected $first_precedence;
/**
* __construct, Constructor
*
* Used to set options.
*
* #param bool $use_reference whether to use a reference (TRUE) or to copy the object (FALSE) [default]
* #param bool $first_precedence whether the first entry takes precedence (TRUE) or last entry takes precedence (FALSE) [default]
*/
public function __construct($use_reference = FALSE, $first_precedence = FALSE) {
// Use a reference
$this->use_reference = $use_reference === TRUE ? TRUE : FALSE;
$this->first_precedence = $first_precedence === TRUE ? TRUE : FALSE;
}
/**
* Merge, used to merge multiple objects stored in an array
*
* This is used to *start* the merge or to merge an array of objects.
* It is not needed to start the merge, but visually is nice.
*
* #param object[]|object $objects array of objects to merge or a single object
* #return object the instance to enable linking
*/
public function & merge() {
$objects = func_get_args();
// Each object
foreach($objects as &$object) $this->with($object);
// Garbage collection
unset($object);
// Return $this instance
return $this;
}
/**
* With, used to merge a singluar object
*
* Used to add an object to the composition
*
* #param object $object an object to merge
* #return object the instance to enable linking
*/
public function & with(&$object) {
// An object
if(is_object($object)) {
// Reference
if($this->use_reference) {
if($this->first_precedence) array_push($this->composite, $object);
else array_unshift($this->composite, $object);
}
// Clone
else {
if($this->first_precedence) array_push($this->composite, clone $object);
else array_unshift($this->composite, clone $object);
}
}
// Return $this instance
return $this;
}
/**
* __get, retrieves the psudo merged object
*
* #param string $name name of the variable in the object
* #return mixed returns a reference to the requested variable
*
*/
public function & __get($name) {
$return = NULL;
foreach($this->composite as &$object) {
if(isset($object->$name)) {
$return =& $object->$name;
break;
}
}
// Garbage collection
unset($object);
return $return;
}
}
Usage:
$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);
Example:
$obj1 = new stdClass();
$obj1->a = 'obj1:a';
$obj1->b = 'obj1:b';
$obj1->c = 'obj1:c';
$obj2 = new stdClass();
$obj2->a = 'obj2:a';
$obj2->b = 'obj2:b';
$obj2->d = 'obj2:d';
$obj3 = new Compositor();
$obj3->merge($obj1, $obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj2:a, obj2:b, obj1:c, obj2:d
$obj1->c;
$obj3 = new Compositor(TRUE);
$obj3->merge($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, obj1:c, obj2:d
$obj1->c = 'obj1:c';
$obj3 = new Compositor(FALSE, TRUE);
$obj3->with($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, #obj1:c, obj2:d
$obj1->c = 'obj1:c';
A very simple solution considering you have object A and B:
foreach($objB AS $var=>$value){
$objA->$var = $value;
}
That's all. You now have objA with all values from objB.
The \ArrayObject class has the possibility to exchange the current array to disconnect the original reference. To do so, it comes with two handy methods: exchangeArray() and getArrayCopy(). The rest is plain simple array_merge() of the provided object with the ArrayObjects public properties:
class MergeBase extends ArrayObject
{
public final function merge( Array $toMerge )
{
$this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
}
}
The usage is as easy as this:
$base = new MergeBase();
$base[] = 1;
$base[] = 2;
$toMerge = [ 3,4,5, ];
$base->merge( $toMerge );
a solution To preserve,both methods and properties from merged onjects is to create a combinator class that can
take any number of objects on __construct
access any method using __call
accsess any property using __get
class combinator{
function __construct(){
$this->melt = array_reverse(func_get_args());
// array_reverse is to replicate natural overide
}
public function __call($method,$args){
forEach($this->melt as $o){
if(method_exists($o, $method)){
return call_user_func_array([$o,$method], $args);
//return $o->$method($args);
}
}
}
public function __get($prop){
foreach($this->melt as $o){
if(isset($o->$prop))return $o->$prop;
}
return 'undefined';
}
}
simple use
class c1{
public $pc1='pc1';
function mc1($a,$b){echo __METHOD__." ".($a+$b);}
}
class c2{
public $pc2='pc2';
function mc2(){echo __CLASS__." ".__METHOD__;}
}
$comb=new combinator(new c1, new c2);
$comb->mc1(1,2);
$comb->non_existing_method(); // silent
echo $comb->pc2;
I would go with linking the second object into a property of the first object. If the second object is the result of a function or method, use references. Ex:
//Not the result of a method
$obj1->extra = new Class2();
//The result of a method, for instance a factory class
$obj1->extra =& Factory::getInstance('Class2');
To merge any number of raw objects
function merge_obj(){
foreach(func_get_args() as $a){
$objects[]=(array)$a;
}
return (object)call_user_func_array('array_merge', $objects);
}
This snippet of code will recursively convert that data to a single type (array or object) without the nested foreach loops. Hope it helps someone!
Once an Object is in array format you can use array_merge and convert back to Object if you need to.
abstract class Util {
public static function object_to_array($d) {
if (is_object($d))
$d = get_object_vars($d);
return is_array($d) ? array_map(__METHOD__, $d) : $d;
}
public static function array_to_object($d) {
return is_array($d) ? (object) array_map(__METHOD__, $d) : $d;
}
}
Procedural way
function object_to_array($d) {
if (is_object($d))
$d = get_object_vars($d);
return is_array($d) ? array_map(__FUNCTION__, $d) : $d;
}
function array_to_object($d) {
return is_array($d) ? (object) array_map(__FUNCTION__, $d) : $d;
}
All credit goes to: Jason Oakley
Here is a function that will flatten an object or array. Use this only if you are sure your keys are unique. If you have keys with the same name they will be overwritten. You will need to place this in a class and replace "Functions" with the name of your class. Enjoy...
function flatten($array, $preserve_keys=1, &$out = array(), $isobject=0) {
# Flatten a multidimensional array to one dimension, optionally preserving keys.
#
# $array - the array to flatten
# $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
# $out - internal use argument for recursion
# $isobject - is internally set in order to remember if we're using an object or array
if(is_array($array) || $isobject==1)
foreach($array as $key => $child)
if(is_array($child))
$out = Functions::flatten($child, $preserve_keys, $out, 1); // replace "Functions" with the name of your class
elseif($preserve_keys + is_string($key) > 1)
$out[$key] = $child;
else
$out[] = $child;
if(is_object($array) || $isobject==2)
if(!is_object($out)){$out = new stdClass();}
foreach($array as $key => $child)
if(is_object($child))
$out = Functions::flatten($child, $preserve_keys, $out, 2); // replace "Functions" with the name of your class
elseif($preserve_keys + is_string($key) > 1)
$out->$key = $child;
else
$out = $child;
return $out;
}
Let's keep it simple!
function copy_properties($from, $to, $fields = null) {
// copies properties/elements (overwrites duplicates)
// can take arrays or objects
// if fields is set (an array), will only copy keys listed in that array
// returns $to with the added/replaced properties/keys
$from_array = is_array($from) ? $from : get_object_vars($from);
foreach($from_array as $key => $val) {
if(!is_array($fields) or in_array($key, $fields)) {
if(is_object($to)) {
$to->$key = $val;
} else {
$to[$key] = $val;
}
}
}
return($to);
}
If that doesn't answer your question, it will surely help towards the answer.
Credit for the code above goes to myself :)
I would like to get all the instances of an object of a certain class.
For example:
class Foo {
}
$a = new Foo();
$b = new Foo();
$instances = get_instances_of_class('Foo');
$instances should be either array($a, $b) or array($b, $a) (order does not matter).
A plus is if the function would return instances which have a superclass of the requested class, though this isn't necessary.
One method I can think of is using a static class member variable which holds an array of instances. In the class's constructor and destructor, I would add or remove $this from the array. This is rather troublesome and error-prone if I have to do it on many classes.
If you derive all your objects from a TrackableObject class, this class could be set up to handle such things (just be sure you call parent::__construct() and parent::__destruct() when overloading those in subclasses.
class TrackableObject
{
protected static $_instances = array();
public function __construct()
{
self::$_instances[] = $this;
}
public function __destruct()
{
unset(self::$_instances[array_search($this, self::$_instances, true)]);
}
/**
* #param $includeSubclasses Optionally include subclasses in returned set
* #returns array array of objects
*/
public static function getInstances($includeSubclasses = false)
{
$return = array();
foreach(self::$_instances as $instance) {
if ($instance instanceof get_class($this)) {
if ($includeSubclasses || (get_class($instance) === get_class($this)) {
$return[] = $instance;
}
}
}
return $return;
}
}
The major issue with this is that no object would be automatically picked up by garbage collection (as a reference to it still exists within TrackableObject::$_instances), so __destruct() would need to be called manually to destroy said object. (Circular Reference Garbage Collection was added in PHP 5.3 and may present additional garbage collection opportunities)
Here's a possible solution:
function get_instances_of_class($class) {
$instances = array();
foreach ($GLOBALS as $value) {
if (is_a($value, $class) || is_subclass_of($value, $class)) {
array_push($instances, $value);
}
}
return $instances;
}
Edit: Updated the code to check if the $class is a superclass.
Edit 2: Made a slightly messier recursive function that checks each object's variables instead of just the top-level objects:
function get_instances_of_class($class, $vars=null) {
if ($vars == null) {
$vars = $GLOBALS;
}
$instances = array();
foreach ($vars as $value) {
if (is_a($value, $class)) {
array_push($instances, $value);
}
$object_vars = get_object_vars($value);
if ($object_vars) {
$instances = array_merge($instances, get_instances_of_class($class, $object_vars));
}
}
return $instances;
}
I'm not sure if it can go into infinite recursion with certain objects, so beware...
I need this because I am making an event system and need to be able to sent events to all objects of a certain class (a global notification, if you will, which is dynamically bound).
I would suggest having a separate object where you register objects with (An observer pattern). PHP has built-in support for this, through spl; See: SplObserver and SplSubject.
As far as I know, the PHP runtime does not expose the underlying object space, so it would not be possible to query it for instances of an object.