Подтвердить что ты не робот

Получить идентификатор экземпляра объекта в PHP

Недавно я узнал о StackOverflow, что мы можем получить "идентификатор экземпляра" любого ресурса, например:

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() также выводит экземпляр объекта, к сожалению, он также нуждается в буферизация вывода, поскольку она не возвращает результат.

4b9b3361

Ответ 1

spl_object_hash() может помочь вам здесь. Это

возвращает уникальный идентификатор объекта

который всегда является одним и тем же для данного экземпляра.

EDIT после комментария OP:

Вы можете реализовать такое поведение, используя статическое свойство класса, например:

class MyClass 
{
    private static $_initialized = false;

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

Но на самом деле это не имеет никакого отношения к вашему первоначальному вопросу.

Ответ 2

Хорошо, да, с расширением.

Обратите внимание, что дескрипторы, используемые для объектов, которые в то же время были уничтожены, могут быть повторно использованы.

Построить с помощью 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

Тест script

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

Выход

int(1)
int(2)

Ответ 3

Посмотрите spl_object_hash(). Пример использования:

$id = spl_object_hash($object);

Обратите внимание, что для этого вам понадобится PHP 5 >= 5.2.0.

Ответ 4

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

Ответ 5

Пока вы реализуете базовый класс всех классов, от которых вам понадобится, вы можете сделать что-то вроде этого:

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

То, что вы пытаетесь сделать, на самом деле Аспектно-ориентированное программирование (AOP).

В настоящее время существует пара фреймворков для AOP в PHP:

  • seasar (ранее PHPaspect) - это более крупная инфраструктура, интегрирующаяся с Eclipse - на скриншоте показан небольшой фрагмент кода, который отвечает на ваш вопрос, соткав некоторый код вокруг определенного нового оператора во всем проекте.
  • php-aop - облегченная структура для АОП.
  • typo3 имеет встроенную структуру AOP.

Это может быть излишним для ваших нужд, но вы можете обнаружить, что изучение такого мышления, лежащего в основе таких идей, приведет вас к раббитолу и научит вас новым способам думать о разработке программного обеспечения в целом - АОП - это мощная концепция, позволяя вам программировать с точки зрения стратегий и проблем или "аспектов".

Языки, подобные PHP, были разработаны для решения задач программирования - концепция APO была разработана для решения задач программиста. Когда обычно вам нужно подумать о том, как обеспечить выполнение конкретной задачи каждый раз в вашей кодовой базе, вы можете думать об этом как о простом "аспекте" того, как вы программируете, реализуете его в этих терминах напрямую и считаете о ваших проблемах, которые будут выполняться каждый раз.

Это требует меньше дисциплины, и вы можете сосредоточиться на решении практических задач программирования, а не на попытке архитектовать свой путь через требования к структурному коду высокого уровня.

Возможно, стоит 5 минут вашего времени, -)

Удачи!

Ответ 7

Это немного поздно для вечеринки, но я не видел этого ответа и совсем недавно реализовал нечто подобное для класса отладки (для обработки круговых ссылок). Поскольку вы, ребята, можете или не можете знать, что обычные функции печати, такие как 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_#');
    }

Очевидно, что код отладки более сложный, но здесь важно, что, отслеживая хэш класса и spl в $objInstances, я могу легко присвоить свои собственные номера экземпляров вне класса. Это означает, что мне не нужен какой-то уродливый хак (который влияет на код класса), чтобы получить ссылочный номер. Кроме того, мне не нужно отображать "уродливый" хэш-шип. В любом случае мой полный код для этого выводит что-то вроде этого.

$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

Ответ 8

У меня нет проверочного теста PECL, но это может позволить вам удалить код конструктора из определения класса после того, как первый экземпляр класса был создан.

Можно ли удалить конструктор из конструктора, будет интересным экспериментом.

Ответ 9

Если вы не хотите использовать буферизацию вывода... возможно, используйте var_export вместо var_dump?