I'm trying to get unique IDs for object instances in PHP 5+.
The function, spl_object_hash()
is available from PHP 5.2 but I'm wondering if there is a workaround for older PHP versions.
There are a couple of functions in the comments on php.net but they're not working for me. The first (simplified):
function spl_object_hash($object){
if (is_object($object)){
return md5((string)$object);
}
return null;
}
does not work with native objects (such as DOMDocument), and the second:
function spl_object_hash($object){
if (is_object($object)){
ob_start();
var_dump($object);
$dump = ob_get_contents();
ob_end_clean();
if (preg_match('/^object\(([a-z0-9_]+)\)\#(\d)+/i', $dump, $match)) {
return md5($match[1] . $match[2]);
}
}
return null;
}
looks like it could be a major performance buster!
Does anybody have anything up their sleeve?
I ran a couple of quick tests. I really think you'd be better off storing real callbacks in your bind() function using bind('evt_name', array($obj, 'callback_function'))
. If you absolutely want to go the spl_object_hash route, rather than storing references with the event bindings, you're looking at something like this:
A var_dump / extract and hash id implementation:
function spl_object_hash_var_dump($object){
if (is_object($object)){
ob_start();
var_dump($object);
$dump = ob_get_contents();
ob_end_clean();
if (preg_match('/^object\(([a-z0-9_]+)\)\#(\d)+/i', $dump, $match)) {
return md5($match[1] . $match[2]);
}
}
return null;
}
A naive references implementation:
function spl_object_dumb_references(&$object) {
static $hashes;
if (!isset($hashes)) $hashes = array();
// find existing instance
foreach ($hashes as $hash => $o) {
if ($object === $o) return $hash;
}
$hash = md5(uniqid());
while (array_key_exists($hash, $hashes)) {
$hash = md5(uniqid());
}
$hashes[$hash] = $object;
return $hash;
}
This one was basically 5-50x worse than the class-based reference function across the board, so it's not worth worrying about.
A store references by class implementation:
function spl_object_hash_references(&$object) {
static $hashes;
if (!isset($hashes)) $hashes = array();
$class_name = get_class($object);
if (!array_key_exists($class_name, $hashes)) {
$hashes[$class_name] = array();
}
// find existing instance
foreach ($hashes[$class_name] as $hash => $o) {
if ($object === $o) return $hash;
}
$hash = md5(uniqid($class_name));
while (array_key_exists($hash, $hashes[$class_name])) {
$hash = md5(uniqid($class_name));
}
$hashes[$class_name][$hash] = $object;
return $hash;
}
And you end up with results that look like this. Summary: the class based references implementation performs best around n/50 classes--at its best, it manages to pull off 1/3 the performance of the var_dump
based implementation, and it's usually much worse.
The var_dump
implementation seems to be tolerable, though not ideal. But if you're not doing too many of these lookups, it won't be a bottleneck for you. Especially as a fallback for PHP < 5.2 boxen.
I once wrote a helper function for wordpress that offers one unique hash per object, it works with a counter and stores the hash per as a public class property if it has been assigned to an object. The following example demonstrates this:
/**
* get object hash
*
* Returns a unique hash per object.
*
* Proxy function for wordpress installments on servers
* with a PHP version < 5.2.0.
*
* @since 3.0.2
* @note Become deprecated with version 3.2.0 (PHP 5.2 requirements)
* @param object $object
* @return string unique object hash
*/
function wp_object_hash( &$object ) {
static $prefix, $count = 0, $property = '__wphookobjhash__', $spl_function_exists;
isset( $spl_function_exists ) || $spl_function_exists = function_exists( 'spl_object_hash' );
// prefer spl_object_hash if available
if ( $spl_function_exists )
return spl_object_hash( $object );
// validate input
if ( !is_object( $object ) ) {
trigger_error( __FUNCTION__ . '() expects parameter 1 to be object', E_USER_WARNING );
return null;
}
// setup prefix and counter to generate object hash, set it to object if not set
isset( $prefix ) || ( ( $prefix = uniqid( '' ) ) && $property .= $prefix . '__' );
isset( $object->$property ) || ( $object->$property = sprintf( '%s-%08d', $prefix , ++$count ) );
return $object->$property;
}
If you're using a PHP 5 version, you don't need to pass the parameter by reference.
This is what you want.
I've fixed a very probable bug and streamlined the function from bobthecow answer (which is also borrowed from php.net) to this:
if ( !function_exists( 'spl_object_hash' ) ) {
function spl_object_hash( $object )
{
ob_start();
var_dump( $object );
preg_match( '[#(\d+)]', ob_get_clean(), $match );
return $match[1];
}
}
It returns an integer (usually in the sub-100 range), which is unique for any object (see this answer for details on what you're seeing).
P.S. I use this implementation in a real world scenario here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With