Obtenir L'ID D'Instance d'un objet en PHP

j'ai appris il y a un certain temps sur StackOverflow que nous pouvons obtenir le "ID d'instance" de n'importe quelle ressource , par exemple:

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)

j'ai besoin de quelque chose de similaire, mais appliquée à des 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

les travaux ci-dessus mais j'espérais une solution plus rapide ou, au moins, un qui n'a pas impliqué des tampons de sortie. S'il vous plaît noter que cela ne sera pas nécessairement utilisé dans le constructeur ou même à l'intérieur de la classe elle-même!

spl_object_hash() n'est pas ce que je cherche parce que les deux objets produisent des hachures identiques

la question contenait auparavant un exemple incorrect de sortie spl_object_hash ; s'assurer que les deux objets existent en même temps produit des hachures qui sont subtilement différentes:

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

la conversion en int comme les ressources ne semble pas fonctionner pour les objets:

Avis: Objet de la classe foo n'a pas pu être converti en int.

y a-t-il un moyen rapide de saisir la même sortie sans utiliser les propriétés de l'objet ?

en plus de var_dump() , j'ai découvert par tâtonnements que debug_zval_dump() produit aussi l'instance de l'objet, malheureusement il a aussi besoin de tampon de sortie car il ne renvoie pas le résultat.

25
demandé sur Community 2010-05-20 13:12:46

9 réponses

spl_object_hash() je peux vous aider. It

renvoie un identifiant unique pour l'objet

qui est toujours le même pour une instance donnée.

MODIFIER après l'OP commentaire:

vous pouvez implémenter un tel comportement en utilisant une propriété de classe statique, E. g:

class MyClass 
{
    private static $_initialized = false;

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

mais en fait cela a rien à votre question initiale.

28
répondu Stefan Gehrig 2010-05-20 10:09:42

eh Bien, oui, avec une extension.

noter que les poignées utilisées pour les objets qui ont été, dans l'intervalle, détruits, peuvent être réutilisées.

Construire avec 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 de Test

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

sortie

int(1)
int(2)
19
répondu Artefacto 2010-06-22 01:16:45

Alix, votre solution dans la question était exactement ce dont j'avais besoin, mais en fait casse quand il y a un objet dans un objet, renvoie le dernier # dans le var_dump. J'ai réparé ça, j'ai fait le regex plus vite, et je l'ai mis dans une jolie petite fonction.

/**
 * 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]; 
}
3
répondu Nate Ferrero 2011-10-22 10:41:10

regardez spl_object_hash() . Exemple d'utilisation:

$id = spl_object_hash($object);

notez que vous aurez besoin de PHP 5 >= 5.2.0 pour que cela fonctionne.

2
répondu karim79 2010-05-20 09:15:55

aussi longtemps que vous mettez en œuvre la classe de base toutes les classes que vous allez avoir besoin de cela, vous pouvez faire quelque chose comme ceci:

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

la sortie serait:

1 (should be 1) 
2 (should be 2) 
3 (should be 3) 
4 (should be 4) 
1
répondu Kris 2010-06-22 18:00:05

Ce que vous êtes en train de faire, est en fait Aspect-Oriented Programming (AOP).

il y a au moins quelques cadres disponibles pour AOP en PHP à ce point:

  • seasar (anciennement PHPaspect) est un cadre plus grand intégrant avec Eclipse - la capture d'écran vous montre un petit morceau de code qui répond à votre question, tissant du code autour d'un nouvel énoncé particulier tout au long d'un projet.
  • php-aop est un cadre léger pour AOP.
  • typo3 a un cadre AOP intégré.

ce peut être exagéré pour vos besoins, mais vous pouvez trouver que l'exploration du type de pensée derrière des idées comme celles - ci vous mènera dans le rabbithole, et vous enseigner de nouvelles façons de penser le développement de logiciels en général-AOP est un puissant concept, vous permettant de programmer en termes de stratégies et de préoccupations, ou "aspects".

langages comme PHP ont été conçus pour résoudre programmation tâches - le concept D'APO a été conçu pour résoudre d'un programmeur tâches. Quand normalement vous auriez besoin de penser à la façon de s'assurer qu'une préoccupation particulière est remplie à chaque fois dans votre codebase, vous pouvez penser à cela comme simplement un "aspect" de la façon dont vous programmez, mettez-le en œuvre en ces termes directement, et comptez sur vos préoccupations pour être mis en œuvre à chaque fois.

il exige moins de discipline, et vous pouvez vous concentrer sur la résolution des tâches pratiques de programmation plutôt que d'essayer d'organiser votre chemin à travers les exigences de haut niveau de code structurel.

pourrait valoir 5 minutes de votre temps, de toute façon; -)

bonne chance!

1
répondu mindplay.dk 2010-08-04 12:16:12

C'est un peu tard pour la fête mais je n'ai pas vu cette réponse et j'ai récemment implémenté quelque chose de similaire pour une classe de débogage ( pour gérer les références circulaires). Comme vous les gars mai ou ne peut pas connaître les fonctions d'impression normales telles que var_export , ont limité ou pas de support de référence circulaire.

comme noté le spl_object_hash est unique par instance, le problème que j'ai eu avec elle est qu'elle est laide. Pas vraiment adapté à l'impression pour ma débogueur comme il est quelque chose comme ce 000000006ac56bae0000000044fda36f qui peut être difficile à comparer pour dire ce 000000006ac56bae0000000044fda35f . Donc, comme L'OP a déclaré ce que je voulais était juste un certain nombre de l'instance ( Je n'avais vraiment besoin de cela sur une base par classe ).

donc la solution simple pour moi était de faire ce qui suit.

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

évidemment le code de débogage est beaucoup plus complexe, mais l'essentiel ici est qu'en traçant la classe et le hash spl dans $objInstances je peux facilement attribuer mes propres numéros d'instance en dehors de la classe. Cela signifie que je n'ai pas besoin d'un hack moche ( qui affecte le code de la classe ) pour obtenir un numéro de référence. De plus, je n'ai pas besoin d'afficher le "laid" hachage spl. De toute façon, mon code complet produit quelque chose comme ça.

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

comme vous pouvez le voir, il est très facile de voir d'où vient object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#} . Je voulais garder ce code de débogage aussi proche du natif var_dump les résultats de cette.

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

la différence ici est que j'avais besoin du retour comme une chaîne de caractères, Pas de sortie sur le navigateur. J'ai aussi voulu pouvoir montrer les constantes de classe, les propriétés statiques et les propriétés privées ( avec des options pour changer les sorties du Débogueur, et la limite de profondeur). Et, je voulais un peu plus d'informations sur ce qu'était la référence circulaire au lieu de simplement *RECURSION* qui ne me dit rien.

Espère que cela aide quelqu'un dans l'avenir.

voici le code complet de ma classe de débogage, vous pouvez trouver ceci utilisé à propos de la ligne # 300

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

1
répondu ArtisticPhoenix 2017-11-05 10:08:51

Je n'ai pas le kit D'exécution PECL activé pour tester ceci, mais cela peut vous permettre de supprimer le code du constructeur de la définition de classe après la première fois qu'une instance de la classe a été créée.

Si vous pouvez supprimer le constructeur de l'intérieur, le constructeur serait une expérience intéressante.

0
répondu Mark Baker 2010-06-16 22:19:45

si vous ne voulez pas utiliser de tampon de sortie... peut-être utiliser var_export au lieu de var_dump ?

0
répondu Richard JP Le Guen 2010-06-22 15:47:42