|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2010-08-20 17:34 UTC] marco dot weber at uni-trier dot de
Description:
------------
the problem with spl_object_hash is, that it is only unqiue for the existing objects.
For a given Object:
class container {
protected $storage=array();
public function add(test1 $obj) {
if(!isset($this->storage[spl_object_hash($obj)])) {
$this->storage[spl_object_hash($obj)]=$obj;
}
}
}
This leads to a problem, that the add method receives the same hash in to following cases:
CASE1:
$o=new container();
$o->add(new test1("lalala"));
$o->add(new test1("lololo")); // same hash, that's NOT ok!
CASE2:
$t=new test("lalala");
$o=new container();
$o->add($t);
$o->add($t); // same hash, that's ok!
Since there is nothing wrong, with the spl_object_hash() method, i suggest to introduce a new spl_object_id() function. This could simply return an (internal) uint32, that is attached to every object on its creation. This counter gets incremented on every object that gets created. :)
Test script:
---------------
// just with spl_object_hash :/
class container {
protected $storage=array();
public function add(test1 $obj) {
if(!isset($this->storage[spl_object_hash($obj)])) {
$this->storage[spl_object_hash($obj)]=$obj;
}
}
}
$o=new container();
$o->add(new test1("lalala")); // will be added
$o->add(new test1("lololo")); // not added - NOT as expected
$t=new test("lalala");
$o=new container();
$o->add($t); // will be added
$o->add($t); // not added - as expected
Expected result:
----------------
// with the new spl_object_id function :)
class container {
protected $storage=array();
public function add(test1 $obj) {
if(!isset($this->storage[spl_object_id($obj)])) {
$this->storage[spl_object_id($obj)]=$obj;
}
}
}
$o=new container();
$o->add(new test1("lalala")); // will be added
$o->add(new test1("lololo")); // will be added
$t=new test("lalala");
$o=new container();
$o->add($t); // will be added
$o->add($t); // not added
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Wed Oct 29 20:00:01 2025 UTC |
i've forgotten to write down the test1 class: class test1 { public function __construct($s) { } }I don't think attaching a serial number to every object from the get-go is a good approach, since this will add overhead (memory and CPU) for every object constructed. Objects are relatively lightweight in PHP, and sacrificing that for a feature that is probably less commonly used, to me, is unacceptable. What I would propose, is to assign a serial number the first time you access an object - something along the lines of this: public function object_serial($object) { static $next_sn = 1; if (!isset($object->__sn__)) $object->__sn__ = $next_sn++; return $object->__sn__; } You don't need to keep a serial-number in-memory until it's actually needed, and at that point, we'll just check and see if it already has an assigned serial- number. This is much simpler and easier on system-resources - the serial number is much lighter than the 32-character hash, and will work just as well. And since you're most likely going to use this value as index in an array, hash indexes will take up less memory, and lookups will probably be cheaper too. Unfortunately the PHP version of this collides with the magic __set() method, which is why the function shown above won't always work. If there were a way to go around the __get() and __set() methods, and directly access the properties of an object without colliding with these magic methods, that would probably be an even better solution. I would consider such a feature as belonging to the reflection domain - something like ReflectionObject::getValue($object, $name) and ReflectionObject::setValue($object, $name, $value) would do the trick. (this would probably have other uses too, so perhaps that's an even better solution to this problem, seeing as how implementing your own object_serial() method is literally only a few lines of code...)@rasmus at mindplay dot dk: the thing is PHP already creates internal unique index for each instantiated object. The requested spl_object_id() function would only have to return it. You may have seen the value of this variable while debugging your scripts. Have you not noticed the overhead? ;) Just kidding. Anyway, your idea for workaround seemed reasonable to me, and now thanks to the traits in 5.4 I'm able to apply this unique object id to every class that I need. Just use the below trait. It is also immune to the __get/__set issue. The downside of this is that the id is a string containing class name. But if I'd need an id that is unique only within a specific class scope I can use integer value without the class name: <?php /** * Provides unique object's identifier. */ trait TObjectUniqueId { /** * Object's unique id. * * @var int */ protected $__oid__ = null; /** * @return string */ public function getObjectUniqueId() { static $__object_index = 1; if ( null === $this->__oid__ ) { $this->__oid__ = __CLASS__ . '\\' . $__object_index++; } return $this->__oid__; } }Here's a chopped down version of my current solution. function id ( $object ) { static $id = 0; if (!isset($object->__id) || $object->__id[0] !== spl_object_hash($object)) { $object->__id = [spl_object_hash($object),++$id]; } return $object->__id[1]; } It works for clones to a degree. Clones can be made through this function to automatically refresh the id. function dupe ( $object ) { $clone = clone $object; id($clone); return $clone; }