Get Instance ID of an Object in PHP

前端 未结 10 1610
说谎
说谎 2020-12-14 07:19

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_dum         


        
相关标签:
10条回答
  • 2020-12-14 07:45

    This is a bit late to the party but I didn't see this answer and just recently implemented something similar for a debugging class ( to handle circular references). As you guys may or may not know the normal printing functions such as var_export, have limited or no circular reference support.

    As noted the spl_object_hash is unique per instance, the problem I had with it is that it is ugly. Not really suited to printing for my debugger as it's something like this 000000006ac56bae0000000044fda36f which can be hard to compare to say this 000000006ac56bae0000000044fda35f. So like the OP stated what I wanted was just a number of the instance ( I only really needed this on a per class basis ).

    Therefor the simple solution for me was to do the following.

        $class = get_class( $input );
        $hash = spl_object_hash( $input );
        if( !isset( $objInstances[ $class ] )){
            $objInstances[ $class ] = array();
        }
    
        $output = 'object(%s) #%s (%s){%s}'; //class, instance, prop_count, props
        if( false === ( $index = array_search($hash, $objInstances[ $class ] ) ) ){
            $index = count($objInstances[ $class ]); //set init index for instance
            $objInstances[ $class ][] = $hash;
            // .... debugging code
            $output = 'debugging result.', //sprintf 
        }else{
            $output = sprintf( $output, $class, $index, 0, '#_CIRCULAR_REFRENCE_#');
        }
    

    Obviously the debugging code is way more complex, but the essential thing here is that by tracking the class and spl hash in $objInstances I can easily assign my own instance numbers outside of the class. This means I don't need some ugly hack ( that affects the class's code ) to get a reference number. Also, I don't need to display the "ugly" spl hash. Anyway my full code for this outputs something like this.

    $obj = new TestObj();
    $obj1 = new TestObj();
    
    $obj->setProProp($obj1);
    $obj1->setProProp($obj); //create a circular reference 
    
    object(TestObj) #0 (7){
        ["SOME_CONST":const] => string(10) 'some_const',
        ["SOMEOTHER_CONST":const] => string(16) 'some_other_const',
        ["SOME_STATIC":public static] => string(6) 'static',
        ["_PRO_STATIC":protected static] => string(10) 'pro_static',
        ["someProp":public] => string(8) 'someProp',
        ["_pro_prop":protected] => object(TestObj) #1 (7){
            ["SOME_CONST":const] => string(10) 'some_const',
            ["SOMEOTHER_CONST":const] => string(16) 'some_other_const',
            ["SOME_STATIC":public static] => string(6) 'static',
            ["_PRO_STATIC":protected static] => string(10) 'pro_static',
            ["someProp":public] => string(8) 'someProp',
            ["_pro_prop":protected] => object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#},
            ["_proProp":protected] => string(7) 'proProp'
        },
        ["_proProp":protected] => string(7) 'proProp'
    }
    

    As you can see it's very easy to see where object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#} came from now. I wanted to keep this debugging code as close to the native var_dump which outputs this.

    object(TestObj)#7 (3) {
      ["someProp"]=> string(8) "someProp"
      ["_pro_prop":protected]=> object(TestObj)#10 (3) {
        ["someProp"]=> string(8) "someProp"
        ["_pro_prop":protected]=> *RECURSION*
        ["_proProp":protected]=> string(7) "proProp"
      }
      ["_proProp":protected]=> string(7) "proProp"
    }
    

    The difference here is I needed the return as a string, not output to the browser. I also wanted to be able to show class constants, static properties, and private properties ( with flags to change what the debugger outputs, and the depth limit). And, I wanted a bit more information as to what the circular reference was instead of just *RECURSION* which doesn't tell me anything.

    Hope it helps someone in the future.

    Here is the full code for my Debug class, you can find this used about line #300

    https://github.com/ArtisticPhoenix/Evo/blob/master/Evo/Debug.php

    0 讨论(0)
  • 2020-12-14 07:46

    Alix, your solution in the question was exactly what I needed, but actually breaks when there's an object in an object, returns the last # in the var_dump. I fixed this, made the regex faster, and put it in a nice little function.

    /**
     * Get global object ID
     * From: http://stackoverflow.com/questions/2872366/get-instance-id-of-an-object-in-php
     * By: Alix Axel, non-greedy fix by Nate Ferrero
     */
    function get_object_id(&$obj) {
        if(!is_object($obj))
            return false;
        ob_start();
        var_dump($obj);// object(foo)#INSTANCE_ID (0) { }
        preg_match('~^.+?#(\d+)~s', ob_get_clean(), $oid);
        return $oid[1]; 
    }
    
    0 讨论(0)
  • 2020-12-14 07:48

    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.

    0 讨论(0)
  • 2020-12-14 07:51

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

    0 讨论(0)
  • 2020-12-14 07:52

    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)
    
    0 讨论(0)
  • 2020-12-14 07:53

    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.

    0 讨论(0)
提交回复
热议问题