代码之家  ›  专栏  ›  技术社区  ›  Alix Axel

在PHP中获取对象的实例ID

  •  26
  • Alix Axel  · 技术社区  · 16 年前

    我在StackOverflow上了解到 we can get the "instance ID" of any resource 例如:

    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)
    

    我需要一些类似的东西,但适用于课堂:

    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
    

    上述方法是可行的,但我希望有一个更快的解决方案,或者至少是一个不涉及输出缓冲区的解决方案。请注意,这不一定在构造函数中使用,甚至不一定在类本身中使用!

    spl_object_hash() 不是我要找的,因为这两个对象产生相同的哈希值

    这个问题以前包含了一个不正确的例子 spl_object_hash 输出;确保两个对象同时存在会产生细微不同的哈希:

    var_dump(spl_object_hash($foo));  // 0000000079e5f3b60000000042b31773
    var_dump(spl_object_hash($foo2)); // 0000000079e5f3b50000000042b31773
    

    强制转换为int-like资源似乎不适用于对象:

    注意事项: 无法将foo类的对象转换为int。

    是否有一种不使用对象属性快速获取相同输出的方法? ?

    此外 var_dump() 我通过反复试验发现 debug_zval_dump() 同时输出对象实例,不幸的是,它也需要输出缓冲,因为它不返回结果。

    9 回复  |  直到 8 年前
        1
  •  32
  •   Stefan Gehrig    16 年前

    spl_object_hash() 能在这里帮你吗?它

    返回对象的唯一标识符

    在给定的实例中总是相同的。

    编辑 操作注释后:

    可以使用静态类属性实现这种行为,例如:

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

    但实际上这与你最初的问题无关。

        2
  •  19
  •   Artefacto    16 年前

    嗯,是的,有一个分机。

    请注意,用于同时被销毁的对象的句柄可以重用。

    建立与 phpize && ./configure && make && make install

    TestEX.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
    

    测试版本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)
    

    组态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
    

    测试脚本

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

    产量

    int(1)
    int(2)
    
        3
  •  3
  •   Nate Ferrero    14 年前

    alix,问题的解决方案正是我需要的,但实际上当对象中有一个对象时会中断,返回var_转储中的最后一个。我修复了这个,使regex更快,并把它放在一个很好的小函数中。

    /**
     * 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]; 
    }
    
        4
  •  2
  •   karim79    16 年前

    看一看 spl_object_hash() . 使用实例:

    $id = spl_object_hash($object);
    

    请注意,您需要php 5>=5.2.0才能使其正常工作。

        5
  •  1
  •   Kris    16 年前

    只要你实现了基类,所有你需要它的类,你就可以这样做:

    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());
    

    输出将是:

    1 (should be 1) 
    2 (should be 2) 
    3 (should be 3) 
    4 (should be 4) 
    
        6
  •  1
  •   mindplay.dk    16 年前

    实际上,你想做的是 Aspect-Oriented Programming (AOP)。

    目前,至少有几个框架可用于PHP中的AOP:

    • seasar (以前称为phpaspect)是一个与Eclipse集成的更大的框架-屏幕截图显示了一个小代码片段,它可以回答您的问题,在整个项目中围绕一个特定的新语句编织一些代码。
    • php-aop 是AOP的轻量级框架。
    • typo3 内置AOP框架。

    这可能对您的需求有点杀伤力,但您可能会发现,探索这些想法背后的思维方式将引导您深入研究,并教会您思考软件开发的新方法一般来说-AOP是一个强大的概念,允许您根据策略和关注点或“方面”进行编程。

    像php这样的语言被设计用来解决 程序设计 任务-载脂蛋白的概念旨在解决 程序员的 任务。通常情况下,您需要考虑如何确保每次代码库中的某个特定问题都得到满足,您可以将其简单地看作是如何编程的“方面”,直接用这些术语实现它,并指望每次都实现您的问题。

    它需要更少的规则,您可以专注于解决实际的编程任务,而不是尝试通过高层结构代码需求来构建自己的方法。

    可能值你5分钟的时间,不管怎样;-)

    祝你好运!

        7
  •  1
  •   ArtisticPhoenix    8 年前

    这对聚会来说有点晚了,但我没有看到这个答案,只是最近为调试类(处理循环引用)实现了类似的东西。因为你们可能知道也可能不知道正常的打印功能,比如 var_export ,具有有限的或没有循环引用支持。

    如前所述,spl_对象_哈希对于每个实例都是唯一的,我遇到的问题是它很难看。不太适合为我的调试器打印,因为它类似于 000000006ac56bae0000000044fda36f 这很难说 000000006ac56bae0000000044fda35f . 所以,就像OP所说的,我想要的只是一些实例(我只是在每个类的基础上真正需要它)。

    因此,对我来说,简单的解决办法是做以下的事情。

        $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_#');
        }
    

    显然,调试代码要复杂得多,但这里最重要的是通过跟踪类和SPL哈希 $objInstances 我可以很容易地在班外分配自己的实例号。这意味着我不需要一些丑陋的黑客(影响类的代码)来获取参考号。另外,我不需要显示“丑陋”的SPL哈希。总之,我的完整代码输出类似于这样的结果。

    $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'
    }
    

    正如你所看到的,很容易看到 object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#} 从现在开始。我想让这个调试代码尽可能接近本机 var_dump 输出这个。

    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"
    }
    

    这里的区别是我需要返回字符串,而不是输出到浏览器。我还希望能够显示类常量、静态属性和私有属性(使用标志来更改调试器输出的内容和深度限制)。而且,我想知道更多关于循环引用是什么的信息,而不仅仅是 *RECURSION* 什么都不告诉我。

    希望以后能帮助别人。

    这是我调试类的完整代码,您可以在第300行找到它

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

        8
  •  0
  •   Mark Baker    16 年前

    我没有启用pecl runkit来测试这一点,但这可能允许您在第一次创建类的实例之后从类定义中删除构造函数代码。

    是否可以从构造函数中移除构造函数将是一个有趣的实验。

        9
  •  0
  •   Richard JP Le Guen    16 年前

    如果不想使用输出缓冲…也许用 var_export 而不是 var_dump ?

    推荐文章