Building object identity in PHP I am trying to build object identity in PHP so that when I have a collection of objects, each one can have a string as identifier and all of these identifiers are afterwards combined to form an unique md5 to represent the "identity of a collection".

Why? So that I can choose to skip re-execution of code when it's not needed:

interface SomeTestInterface
{
    public function testFunction();
}

abstract class Identifiable
{
    public function __toString()
    {
        $identity_shards = array_merge( get_object_vars( $this ), class_implements( $this ) );

        $identity_string = '';

        foreach( $identity_shards as $identity_shard_key =&gt; $identity_shard_value ) {
            $identity_string .= (string) $identity_shard_key . (string) json_encode( $identity_shard_value );
        }

        return md5( get_class( $this ) . $identity_string );
    }
}

class SomeBaseClass extends Identifiable implements SomeTestInterface
{
    public function __construct( $number )
    {
        $this-&gt;number = $number;
        $this-&gt;thing = 'a';
        $this-&gt;other_thing = ['a','b','c',1,2,3,];
    }

    public function testFunction()
    {
        return 'a';
    }
}


This is testable with:

for( $i = 1; $i &lt; 10000; $i++ ) {
    $class = new SomeBaseClass( $i );
    (string) $class;
}


For me, PHP 7.3 and WordPress, this takes ~100ms to execute.

My micro-decisions:


I need json_encode on $identity_shard_value because you can't cast an array to string, for example. json_encode is both fast in my experience and knows how to deal with it all.
I chose to make this an abstract class because json_encode doesn't have access to scoped classes, as such, it cannot encode what it can't find, so I must be able to access $this though it's weird because even in the abstract class, I still can't encode it, but I should be able to.


My main concerns with this is if I really need all these items to build my object identity or if there's another, faster way. 10000 objects in 0.1ms, although very good on its own, doesn't necessarily scale.

In essence, every single object that implements Identifiable in a collection that a module of my framework has will have an identity that I will then combine into a final "collection identity" to later do a check such as:

$collection_identity = getCollectionIdentity( $collection ); //MD5 computed from the identity of all these objects

if( $collection_identity != getCollectionIdentityByName( 'some_collection' ) {
    setCollectionIdentity( 'some_collection', $collection_identity );
    //re-execute code
} else {
 retrieveDataFromStorage();
}


As you can see, it checks if there was a change to the objects / collection and if so, it re-executes all the other code, but if not, it just retrieves what that "other code" generated in the past and as such, this is a way to use persistent storage to skip execution of heavy code.
I think this code is fine, and can't be sped up very much. But ....

The MD5 hash is most likely unique, it's got 16^32 (3.4e38) values after all, but once in a blue moon two different objects will have the same identity, especially if you use this a lot. This might cause very rare, random, bugs in your software. Bugs that are virtually impossible to track down.

I don't think the __toString() magic Method was intended for the purpose you're now using it for. I have learned that; "You should always use something for the purpose it was intended for.". The purpose of __toString() is to give you a readable representation of the object. By appropriating it now for identifying objects, you're loosing the capability to use it for its intended purpose later.

You're also relying on an undocumented property of get_object_vars(), namely that it will always return the variables in the same order. Will it? I don't know. It probably will, but doesn't have to. This could also change with changing versions of PHP, leaving you with a very big headache if it happens. You could use ksort() to make sure the order is always the same, but that will slow things down a lot.

I've also read in various places, and in the comments in the manual, that get_object_vars() doesn't return static variables. That makes sense since all objects of a class share the same values for these variables, but it is something to keep in mind.

The storing and checking of the identity hashes, in some collection of hashes, will probably be the slowest part of this whole idea.

Then my final problem wilt this code: 

Properly written code would know the identity of its objects, or at least have a 100% reliable method to check this. Your code should be written in such a way that it already minimizes object duplication. This code seems the result of not being able to write good and efficient code (sorry, I'm trying to make a point here). 

For instance, many objects could already have a simply ID integer that identifies them. For instance a model class, based on a database row, would most likely have such an ID. Most other classes could, if needed, have a similar way to identify themselves. Once you combine such an ID with the class name you should have a 100% reliable identifier.

If you really need a way to identify various objects you could simply add an identity() method to them. Something like this:

&lt;?php

class MyClass 
{
    public function __construct($id)
    {
        $this-&gt;id = $id;
    }   

    public function identity() {
        return get_class() . ":" . $this-&gt;id;
    }
}

$myObject = new MyClass(999);

echo $myObject-&gt;identity();

?&gt;


This would return:


  MyClass:999


I agree that this is a very basic example, but it should be possible to do something similar for any class.

By writing such a specific identifier method for each class you can optimize it, which means it will be faster, and you can make it a 100% reliable under any circumstances. It is also a lot easier to debug, because you can see and read what is going on. No hiding behind mysterious hashes here.

Conclusion: Despite my objections I think your code looks fine. I do however wonder whether this approach will, in the end, cause more trouble than it is worth.

Note: There is more discussion in the comments. In the end coolpasta wrote a response to this question.