在PHP中获取对象的实例ID

时间:2010-05-20 09:12:47

标签: php class object resources instance

我刚才在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似乎不适用于对象:

  

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

是否有快速方法可以在不使用对象属性的情况下获取相同的输出

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

10 个答案:

答案 0 :(得分:32)

spl_object_hash()可以帮到你。它

  

返回对象的唯一标识符

对于给定的实例始终是相同的。

OP评论后

编辑

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

class MyClass 
{
    private static $_initialized = false;

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

但实际上这与你原来的问题无关。

答案 1 :(得分:19)

嗯,是的,有扩展名。

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

使用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

测试脚本

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

输出

int(1)
int(2)

答案 2 :(得分:3)

看看spl_object_hash()。用法示例:

$id = spl_object_hash($object);

请注意,您需要PHP 5&gt; = 5.2.0才能正常工作。

答案 3 :(得分:3)

Alix,您在问题中的解决方案正是我所需要的,但实际上在对象中有对象时会中断,返回var_dump中的最后一个#。我解决了这个问题,使正则表达式更快,并把它放在一个很好的小函数中。

/**
 * 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 :(得分:1)

只要您实现基类所需的所有类,您就可以执行以下操作:

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) 

答案 5 :(得分:1)

您要做的事实上是Aspect-Oriented Programming(AOP)。

此时PHP中至少有几个可用于AOP的框架:

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

这对你的需求来说可能有些过分,但是你可能会发现,探索这些想法背后的那种想法会让你失望,并教你一般思考软件开发的新方法 - AOP是一个强大的概念,允许您根据策略和关注点或“方面”进行编程。

PHP等语言旨在解决编程任务--APO的概念旨在解决程序员的任务。通常情况下,您需要考虑如何确保每次在代码库中满足特定关注点时,您可以将其视为编程方式的“方面”,直接在这些方面实现它,并计算关于你每次都要实施的问题。

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

无论如何,可能值5分钟的时间; - )

祝你好运!

答案 6 :(得分:1)

派对有点晚了,但我没有看到这个答案,最近刚刚为调试类实现了类似的东西(处理循环引用)。由于你们可能会或可能不知道正常的打印功能,例如var_export,因此有限或没有循环参考支持。

如前所述,spl_object_hash对每个实例都是唯一的,我遇到的问题是它很难看。不太适合我的调试器打印,因为它类似于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_#');
    }

显然调试代码更复杂,但这里必不可少的是通过跟踪$objInstances中的类和spl哈希,我可以轻松地在类之外分配我自己的实例编号。这意味着我不需要一些丑陋的黑客(影响类的代码)来获取参考号。此外,我不需要显示“丑陋”的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*,它没有告诉我什么。

希望将来帮助某人。

以下是我的Debug类的完整代码,您可以在第300行

中找到它

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

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

如果您不想使用输出缓冲...也许使用var_export代替var_dump

答案 9 :(得分:0)

从PHP 7.2开始,您可以使用spl_object_id

$id = spl_object_id($object);
$storage[$id] = $object;