在 PHP 中获取对象的实例 ID

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2872366/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 07:59:31  来源:igfitidea点击:

Get Instance ID of an Object in PHP

phpclassobjectresourcesinstance

提问by Alix Axel

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

我不久前在 StackOverflow 上了解到我们可以获取任何资源的“实例 ID”,例如:

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', '', 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

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

The question previously contained an incorrect example of spl_object_hashoutput; ensuring that both objects exist at the same time produces hashes which are subtly different:

之前的问题包含一个错误的spl_object_hash输出示例;确保两个对象同时存在会产生略有不同的散列:

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

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

像资源一样转换为 int 似乎不适用于对象:

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

注意:foo 类的对象无法转换为 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.

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

回答by Stefan Gehrig

spl_object_hash()could help you out here. It

spl_object_hash()可以帮到你。它

returns a unique identifier for the object

返回对象的唯一标识符

which is always the same for a given instance.

对于给定的实例,这总是相同的。

EDITafter OP comment:

OP评论后编辑

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.

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

回答by Artefacto

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

构建 phpize && ./configure && make && make install

testext.h

测试文本.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

配置文件.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)

回答by karim79

Have a look at spl_object_hash(). Usage example:

看看spl_object_hash()。用法示例:

$id = spl_object_hash($object);

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

请注意,您需要 PHP 5 >= 5.2.0 才能工作。

回答by Nate Ferrero

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.

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]; 
}

回答by ArtisticPhoenix

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.

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

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 000000006ac56bae0000000044fda36fwhich 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 ).

如前所述, spl_object_hash 每个实例都是唯一的,我遇到的问题是它很难看。不太适合为我的调试器打印,因为它是这样的000000006ac56bae0000000044fda36f,很难比较这样说000000006ac56bae0000000044fda35f。所以就像 OP 所说的,我想要的只是一些实例(我只在每个班级的基础上真正需要这个)。

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 $objInstancesI 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.

显然调试代码要复杂得多,但这里最重要的是通过跟踪类和 spl 哈希,$objInstances我可以轻松地在类之外分配我自己的实例编号。这意味着我不需要一些丑陋的 hack(会影响类的代码)来获取参考号。另外,我不需要显示“丑陋”的 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'
}

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_dumpwhich outputs this.

正如你所看到的,很容易看出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"
}

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.

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

Hope it helps someone in the future.

希望它可以帮助将来的某个人。

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

这是我的 Debug 类的完整代码,你可以在 #300 行找到它

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

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

回答by Rain

As of PHP 7.2 you can use spl_object_id

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

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

回答by Kris

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) 

回答by mindplay.dk

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

您想要做的实际上是面向方面的编程(AOP)。

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

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

  • 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-aopis a lightweight framework for AOP.
  • typo3has an AOP framework built in.
  • seasar(以前称为 PHPaspect)是一个与 Eclipse 集成的更大的框架 - 屏幕截图向您展示了一个回答您的问题的小代码片段,围绕整个项目中的特定新语句编织一些代码。
  • php-aop是一个轻量级的 AOP 框架。
  • Typ3有一个内置的 AOP 框架。

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

这对于您的需求来说可能有点矫枉过正,但您可能会发现探索这些想法背后的思考方式会让您陷入困境,并教您以新的方式思考软件开发——AOP 是一个强大的概念,让您根据战略和关注点或“方面”进行规划。

Languages like PHP were designed to solve programmingtasks - the concept of APO was designed to solve a programmer'stasks. 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.

PHP 之类的语言旨在解决编程任务——APO 的概念旨在解决程序员的任务。通常,当您需要考虑如何确保每次在代码库中都满足某个特定问题时,您可以将其视为编程方式的一个“方面”,直接用这些术语实现它,并计算对您的顾虑每次都付诸实施。

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

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

Good luck!

祝你好运!

回答by Richard JP Le Guen

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

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

回答by Mark Baker

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.

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

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

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