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

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

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

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

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

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

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

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

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

Ну да, с расширением.

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

Создайте с помощью 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)

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

 $id = spl_object_hash($object); 

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

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

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

 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 (должно быть 1) 
 2 (должно быть 2) 
 3 (должно быть 3) 
 4 (должно быть 4) 

То, что вы пытаетесь сделать, на самом деле является Aspect-Oriented Programming (AOP).

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

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

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

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

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

Во всяком случае, стоит 5 минут вашего времени 😉

Удачи!

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

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

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

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