views:

396

answers:

7

I've learn a while ago on StackOverflow that we can get the "instance ID" of any resource, for instance:

var_dump(intval(curl_init()));  // int(2)
var_dump(intval(finfo_open())); // int(3)
var_dump(intval(curl_init()));  // int(4)
var_dump(intval(finfo_open())); // int(5)
var_dump(intval(curl_init()));  // int(6)

I need something similar but applied to classes:

class foo {
    public function __construct() {
        ob_start();
        var_dump($this); // object(foo)#INSTANCE_ID (0) { }
        echo preg_replace('~.+#(\d+).+~s', '$1', ob_get_clean());
    }
}

$foo = new foo();  // 1
$foo2 = new foo(); // 2

The above works but I was hoping for a faster solution or, at least, one that didn't involve output buffers. Please note that this won't necessarily be used within the constructor or even inside the class itself!

spl_object_hash() is not what I'm looking for because the two objects produce identical hashes:

var_dump(spl_object_hash($foo));  // 000000005111e639000000003a87b42e
var_dump(spl_object_hash($foo2)); // 000000005111e639000000003a87b42e

Casting to int like resources doesn't seem to work for objects:

Notice: Object of class foo could not be converted to int.

Is there a quick way to grab the same output without using object properties?


Besides var_dump(), I've discovered by trial and error that debug_zval_dump() also outputs the object instance, unfortunately it also needs output buffering since it doesn't return the result.

To the down voters: explain your reasons or, if you think this is a basic question, suggest a solution.

+1  A: 

Have a look at spl_object_hash(). Usage example:

$id = spl_object_hash($object);

Note that you'll need PHP 5 >= 5.2.0 for that to work.

karim79
Sounds to me like you want to use the factory pattern
Mark Baker
@Mark: Unfortunately, I'm pretty sure that's not the case.
Alix Axel
@Alix Axel: This is definetly a flaw in your concept !Running some code in the constructor only once is against EVERY OOP-Rule, describe your problem very detailled and we can find a solution, but what you're trying to do is nothing more than an UGLY HACK.
Tobias P.
@Tobias: I know that - forget about the constructor, it was only an example. I would like to know the instance ID of an object independently of the scope I'm in (even outside the class).
Alix Axel
@Alix Axel: Tell us the real problem, and we can give you real help. I can doubt there's a real net design pattern or best practice to solve your problem but not as long as you tell us your real problem.
Tobias P.
@Tobias: The real problem is too long to put into a comment and would just add confusion to the question to make things worse it can't be considered as a best practice implementation (and that's okay) since I know what the "best practice" approach would be and I don't want that in this case. Believe me, I've studied the problem well and the only way that would work for me is something that can return a unique id for a given object variable, independently of the scope. An `intval()` like approach would be the best, since it's fast and also counts how many times the object was instantiated.
Alix Axel
+3  A: 

spl_object_hash() could help you out here. It

returns a unique identifier for the object

which is always the same for a given instance.

EDIT after OP comment:

You could implement such a behavior using a static class property, e.g:

class MyClass 
{
    private static $_initialized = false;

    public function __construct()
    {
        if (!self::$_initialized) {
            self::$_initialized = true;
            // your run-only-once code 
        }
    }
}

But actually this has nothing to with your original question.

Stefan Gehrig
A: 

I don't have the PECL runkit enabled to test this, but this may allow you to remove the constructor code from the class definition after the first time that an instance of the class has been created.

Whether you can remove the constructor from within the constructor would be an interesting experiment.

Mark Baker
I understand you're trying to answer what has been discussed in previous comments but I just want to get the instance ID number of an object, that's my only goal. Nice rep by the way (2^11)! :)
Alix Axel
+3  A: 

Well, yes, with an extension.

Note that the handles used for objects that were, in the meantime, destroyed, can be reused.

Build with phpize && ./configure && make && make install

testext.h

#ifndef PHP_EXTTEST_H
# define PHP_EXTTEST_H
# ifdef HAVE_CONFIG_H
#  include<config.h>
# endif
# include <php.h>
extern zend_module_entry testext_module_entry;
#define phpext_testext_ptr &testext_module_entry
#endif

testext.c

#include "testext.h"

PHP_FUNCTION(get_object_id)
{
    zval *obj;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj)
            == FAILURE) {
        return;
    }

    RETURN_LONG(Z_OBJ_HANDLE_P(obj));
}

static zend_function_entry ext_functions[] = {
    PHP_FE(get_object_id, NULL)
    {NULL, NULL, NULL, 0, 0}
};

zend_module_entry testext_module_entry = {
    STANDARD_MODULE_HEADER,
    "testext",
    ext_functions, /* Functions */
    NULL, /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
    NO_VERSION_YET,
    STANDARD_MODULE_PROPERTIES
};

ZEND_GET_MODULE(testext)

config.m4

PHP_ARG_ENABLE(testext,
  [Whether to enable the "testext" extension],
  [  enable-testext         Enable "testext" extension support])

if test $PHP_EXTTEST != "no"; then
  PHP_SUBST(EXTTEST_SHARED_LIBADD)
  PHP_NEW_EXTENSION(testext, testext.c, $ext_shared)
fi

Test script

<?php
$a = new stdclass();
$b = new stdclass();
var_dump(get_object_id($a));
var_dump(get_object_id($b));

Output

int(1)
int(2)
Artefacto
Although this was not the solution I was hoping for I'm gonna upvote it and if no better solution is presented I'm gonna award the bounty to this answer. You didn't have to go to all this trouble, but nice job! :)
Alix Axel
+1 for good work creating `get_object_id`.
Martin Wickman
A: 

If you don't want to use output buffering... perhaps use var_export instead of var_dump?

LeguRi
A: 

As long as you implement the base class all the classes you're going to need this from, you can do something like this:

class MyBase
{
    protected static $instances = 0;
    private $_instanceId  = null;
    public function getInstanceId()
    {
        return $this->_instanceId;
    }

    public function __construct()
    {
        $this->_instanceId = ++self::$instances;
    }
}

class MyTest extends MyBase
{
    public function Foo()
    {
        /* do something really nifty */
    }
}

$a = new MyBase();
$b = new MyBase();

$c = new MyTest();
$d = new MyTest();


printf("%d (should be 1) \n", $a->getInstanceId());
printf("%d (should be 2) \n", $b->getInstanceId());
printf("%d (should be 3) \n", $c->getInstanceId());
printf("%d (should be 4) \n", $d->getInstanceId());

The output would be:

1 (should be 1) 
2 (should be 2) 
3 (should be 3) 
4 (should be 4) 
Kris
+1  A: 

What you're trying to do, is actually Aspect-Oriented Programming (AOP).

There are at least a couple of frameworks available for AOP in PHP at this point:

  • seasar (formerly PHPaspect) is a larger framework integrating with Eclipse - the screenshot shows you a little code snippet that answers your question, weaving some code around a particular new statement throughout a project.
  • php-aop is a lightweight framework for AOP.
  • typo3 has an AOP framework built in.

This may be overkill for your needs, but you may find that exploring the kind of thinking behind ideas like these will lead you down the rabbithole, and teach you new ways to think about software development in general - AOP is a powerful concept, allowing you to program in terms of strategies and concerns, or "aspects".

Languages like PHP were designed to solve programming tasks - the concept of APO was designed to solve a programmer's tasks. When normally you would need to think about how to ensure that a particular concern gets fulfilled every time in your codebase, you can think of this as simply an "aspect" of how you're programming, implement it in those terms directly, and count on your concerns to be implemented every time.

It requires less discipline, and you can focus on solving the practical programming tasks rather than trying to architect your way through high-level structural code requirements.

Might be worth 5 minutes of your time, anyway ;-)

Good luck!

mindplay.dk
Thanks, I'll definitely take a look at it ASAP. Can't find any screenshots for seasar though.
Alix Axel
Yeah, I don't think there is one, sorry - I meant to point you to this tool:http://code.google.com/p/apdt/This one has integration with Eclipse too, and that must have been the screenshot I had in my mind.
mindplay.dk