Soru PHP'de bir nesnenin örnek kimliğini alın


StackOverflow üzerinde bir süre önce öğrendim ki herhangi bir kaynağın "örnek kimliği" ni alabiliriz, Örneğin:

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)

Benzer bir şeye ihtiyacım var ama derslere başvurdum:

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

Yukarıdaki çalışmalar, ancak daha hızlı bir çözüm ya da en azından, çıktı arabelleklerini içermeyen bir umuyordum. Lütfen bunun mutlaka yapıcıda veya hatta sınıfın içinde kullanılmayacağını unutmayın!

spl_object_hash() aradığım şey değil çünkü iki nesne aynı hashları üretiyor

Soru daha önce yanlış bir örnek içeriyordu. spl_object_hash çıktı; Her iki nesnenin aynı anda var olmasını sağlamak, oldukça farklı olan karmalar üretir:

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

Kaynaklara int gibi döküm yapmak nesneler için işe yaramaz:

Uyarı: Class foo nesnesi int'ye dönüştürülemedi.

Nesne özellikleri kullanmadan aynı çıkışı almanın hızlı bir yolu var mı?

dışında var_dump()Deneme yanılma yoluyla keşfettim debug_zval_dump() Ayrıca, nesne örneğini çıkarır, ne yazık ki sonuç döndürmediği için çıktı arabelleklemeye de ihtiyaç duyar.


25
2018-05-20 09:12


Menşei


Hayır, bu, herhangi bir kaynağın '' örnek kimliği '' DEĞİLDİR, kaynak kimliği bir tamsayıya dönüştürülür - bu da aynı şey değildir. - symcbean
@symcbean: Kapsayan alıntıları fark etmiş görünüyorsunuz ... Soru hala geçerli, reddetmek için bir sebep yok. - Alix Axel
PHP kaynak koduna girmeyi ve böyle bir özelliği yaratmayı ne kadar istekli olacaksınız? Yapmayacağınızı (ne istediğinizi anladığımı varsayarak - herhangi bir sınıfın kaç kere oluşturulduğunu / her kurucuya kod eklemeden kaç kez başlatılacağını belirlemek) PHP'de istediğiniz şeyi yapabileceksiniz. - salathe
@Alix: Tamam, sadece bunun üzerine tökezlediğim benzersiz bir nesne kimliğini kullanmakla ilgili bir uyarı: mail-archive.com/internals@lists.php.net/msg28779.html - Martin Wickman
@Martin: Teşekkürler! Bu yorum mail-archive.com/internals@lists.php.net/msg28820.html beni iki kere kontrol ettim spl_object_hash aslında bir fark yaratıyor! Ancak çok alçakgönüllü olan: 0000000079e5f3b60000000042b31773 ve 0000000079e5f3b50000000042b31773, Örneğin. Bu çoğunlukla benim ihtiyacımı çözer! :) - Alix Axel


Cevaplar:


spl_object_hash() burada size yardımcı olabilir. O

nesne için benzersiz bir tanımlayıcı döndürür

Belirli bir örnek için her zaman aynıdır.

DÜZENLE OP yorumundan sonra:

Statik sınıf özelliğini kullanarak böyle bir davranışı uygulayabilirsiniz.

class MyClass 
{
    private static $_initialized = false;

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

Ama aslında bu, asıl sorununla hiçbir ilgisi yok.


28
2018-05-20 09:16



Not, gelen spl_object_hash belgeler: "Bir nesne yok olduğunda, onun karma başka nesneler için yeniden kullanılabilir." Buna rastladım ve bu, bazı amaçlar için işe yaramıyor. - tremby


Evet, uzatma ile.

Bu arada, yıkılan nesneler için kullanılan tutamaçların yeniden kullanılabileceğini unutmayın.

İle inşa 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

Test komut dosyası

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

Çıktı

int (1)
int (2)

19
2018-06-22 01:16



Her ne kadar bu bir çözüm olmasa da, onu bir daha açıklayacağım ve daha iyi bir çözüm sunulmazsa, bu cevaba ödül vereceğim. Bütün bu belaya gitmek zorunda değildin, ama güzel iş! :) - Alix Axel
İyi çalışma için +1 get_object_id. - Martin Wickman
Burada döndürülen "nesne tanıtıcısı", mevcut tarafından kullanılan iki bilgi parçasından biridir. spl_object_hash Burada kaynak kodunda görüldüğü gibi işlev: lxr.php.net/xref/PHP_5_6/ext/spl/php_spl.c#785 - IMSoP


Bir bak bakalım spl_object_hash(). Kullanım örneği:

$id = spl_object_hash($object);

Çalışmak için PHP 5> = 5.2.0'a ihtiyacınız olacağını unutmayın.


2
2018-05-20 09:15



Bana fabrika modelini kullanmak istediğin gibi geliyor - Mark Baker
@Mark: Maalesef, durumun böyle olmadığından eminim. - Alix Axel
@Alix Axel: Bu kesinlikle konseptinizde bir kusur! Kurucudaki bazı kodları sadece bir kez EVERY OOP-Rule'a karşı çalıştırmak, sorununuzu çok açıklamak ve bir çözüm bulabiliriz, ama yapmaya çalıştığınız şey bir UGLY HACK'dan başka bir şey değildir. - Tobias P.
@Tobias: Bunu biliyorum - yapıcıyı unutun, bu sadece bir örnekti. Bir nesnenin örnek kimliğini, bulunduğum kapsamdan bağımsız olarak (sınıfın dışında bile olsa) bilmek istiyorum. - Alix Axel
@Alix Axel: Bize gerçek problemi söyle, size gerçek yardım verebiliriz. Sorununuzu çözmek için gerçek bir net tasarım deseni veya en iyi uygulama olduğundan şüpheliyim, fakat gerçek probleminizi bize anlattığınız sürece değil. - Tobias P.


Alix, sorunun içindeki çözümün tam olarak ihtiyacım olan şeydi, ama aslında bir nesnede bir nesne olduğunda kırılırsa, var_dump'daki son # değerini döndürür. Bunu düzeltdim, normal ifadeyi daha hızlı yaptım ve güzel bir işleve soktum.

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

2
2017-10-22 10:41





Temel sınıfa, ihtiyacınız olan tüm sınıfları uyguladığınız sürece, aşağıdaki gibi bir şey yapabilirsiniz:

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

Çıkış şöyle olurdu:

1 (1 olmalı)
2 (2 olmalı)
3 (3 olmalı)
4 (4 olmalı)

1
2018-06-22 18:00





Ne yapmaya çalışıyorsun, aslında Yön Odaklı Programlama (AOP).

Bu noktada PHP'de AOP için en az birkaç çerçeve vardır:

  • seasar (eskiden PHPaspect), Eclipse ile bütünleşen daha geniş bir çerçevedir. Ekran görüntüsü, sorunuzu cevaplayan küçük bir kod snippet'ini gösterir ve bir proje boyunca belirli bir yeni ifadenin etrafına bazı kodlar yapıştırır.
  • php-aop AOP için hafif bir çerçeve.
  • typo3 yerleşik bir AOP çerçevesi vardır.

Bu sizin ihtiyaçlarınız için çok fazla olabilir, ancak bu tür fikirlerin ardındaki fikirlerin keşfedilmesinin sizi tavşan deliğine sürükleyeceğini ve genel olarak yazılım geliştirme hakkında yeni düşünmenin yeni yollarını öğreteceğini keşfedebilirsiniz. - AOP size izin veren güçlü bir kavramdır. Strateji ve endişeler veya "yönler" açısından programlamak.

PHP gibi diller çözmek için tasarlandı programlama görevleri - APO kavramı çözmek için tasarlanmıştır bir programcının görevler. Normalde, kod tabanınızda her seferinde belirli bir endişenin nasıl yerine getirildiğini düşünmeniz gerektiğinde, bunu nasıl programladığınızın bir "yönü" olarak, doğrudan bu terimlerle uygulayabileceğinizi ve sayılabileceğini düşünebilirsiniz. endişeleriniz her zaman uygulanacak.

Daha az disipline ihtiyaç duyar ve yüksek seviyeli yapısal kod gereksinimlerinden yola çıkmaya çalışmak yerine pratik programlama görevlerini çözmeye odaklanabilirsiniz.

Yine de 5 dakikasına değersin, yine de ;-)

İyi şanslar!


1
2017-08-04 12:16



Teşekkürler, kesinlikle ASAP bir göz atacağım. Seasar için herhangi bir ekran görüntüsü bulamıyor. - Alix Axel
Evet, bir tane olduğunu sanmıyorum, üzgünüm - seni bu araca yönlendirmek istedim: code.google.com/p/apdt  Bu Eclipse ile entegrasyon da var ve aklımda vardı ekran görüntüsü olması gerekir. - mindplay.dk


Bu partiye biraz geç kaldı ama ben bu cevabı görmedim ve son zamanlarda bir hata ayıklama sınıfı için benzer bir şey uyguladı (döngüsel referansları ele almak için). Sizin gibi normal yazdırma işlevlerini biliyorsunuz veya bilmiyorsunuz var_export, sınırlı veya hiç bir referans desteği yoktur.

Belirtildiği gibi spl_object_hash örnekte benzersizdir, onunla yaşadığım problem çirkin olmasıdır. Böyle bir şey olduğu için hata ayıklayıcım için yazdırmaya gerçekten uygun değil 000000006ac56bae0000000044fda36f Bunu söylemek için karşılaştırmak zor olabilir 000000006ac56bae0000000044fda35f. Yani OP'nin istediğim şey sadece bir dizi örnek olduğunu belirttiği gibi (sadece bunu her sınıfa dayalı olarak gerçekten ihtiyacım vardı).

Bunun için benim için basit bir çözüm aşağıdaki oldu.

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

Açıkçası, hata ayıklama kodu çok daha karmaşıktır, ancak asıl önemli olan şudur; $objInstances Kendi örnek numaralarını sınıfın dışına kolayca atayabilirim. Bu, bir referans numarası almak için çirkin bir hack (sınıfın kodunu etkiler) ihtiyacım olmadığı anlamına gelir. Ayrıca, "çirkin" spl hash göstermem gerekmiyor. Her neyse, bunun için tam kodum böyle bir şey çıktı.

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

Gördüğünüz gibi nerede görmek çok kolay object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#} şimdi geldi. Bu hata ayıklama kodunu yerel olarak yakın tutmak istedim var_dump bunu çıkarıyor.

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

Buradaki fark, tarayıcıya çıkış değil, bir dizge olarak dönüşe ihtiyacım oldu. Ayrıca sınıf sabitlerini, statik özelliklerini ve özel özelliklerini gösterebilmeyi (hata ayıklayıcısının çıktılarını ve derinlik sınırını değiştirmek için bayraklarla) da yapmak istedim. Ve ben sadece dairesel referansın ne olduğu ile ilgili biraz daha fazla bilgi istedim. *RECURSION* Bu bana hiçbir şey söylemiyor.

Umarım gelecekte birine yardım eder.

İşte Debug sınıfımın tam kodu, bu satırı # 300 kullanarak bulabilirsiniz.

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


1
2017-09-21 19:25