web-dev-qa-db-ja.com

PHPでオブジェクトのインスタンスIDを取得します

私は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)
_

私は似たようなものが必要ですが、クラスに適用されます:

_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
_

上記は機能しますが、私はより速い解決策、または少なくとも出力バッファを含まない解決策を望んでいました。これは必ずしもコンストラクタ内で使用されるわけではなく、クラス自体の内部で使用されることもないことに注意してください!

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() もオブジェクトインスタンスを出力することを発見しました。残念ながら、返されないため、出力バッファリングも必要です結果。

29
Alix Axel

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

しかし、実際には、これは元の質問とは関係ありません。

33
Stefan Gehrig

まあ、はい、拡張機能付き。

その間に破壊されたオブジェクトに使用されるハンドルは再利用できることに注意してください。

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)
19
Artefacto

spl_object_hash() をご覧ください。使用例:

$id = spl_object_hash($object);

これを機能させるにはPHP 5> = 5.2.0が必要です。

3
karim79

質問のアリックス、あなたの解決策はまさに私が必要としたものでしたが、オブジェクトにオブジェクトがあると実際には壊れ、var_dumpの最後の#を返します。私はこれを修正し、正規表現を高速化し、Nice little関数に入れました。

/**
 * 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
Nate Ferrero

あなたがやろうとしているのは、実際には アスペクト指向プログラミング (AOP)です。

この時点で、PHP)にAOPで使用可能なフレームワークが少なくともいくつかあります。

  • seasar (以前のPHPaspect)はEclipseと統合されたより大きなフレームワークです。スクリーンショットは、質問に答える小さなコードスニペットを示し、プロジェクト全体の特定の新しいステートメントの周りにコードを織り込んでいます。
  • php-aop はAOPの軽量フレームワークです。
  • typo にはAOPフレームワークが組み込まれています。

これはあなたのニーズにとってはやり過ぎかもしれませんが、これらのようなアイデアの背後にある種類の考え方を探ることで、うさぎの穴を掘り下げ、ソフトウェア開発全般について考える新しい方法を教えることに気付くかもしれません-AOPは強力なコンセプトであり、戦略と懸念、または「側面」の観点からプログラムする。

PHPのような言語はprogrammingタスクを解決するように設計されました-APOの概念は解決するように設計されましたプログラマーのタスク。通常、特定の懸念事項がコードベースで毎回満たされるようにする方法を考える必要がある場合、これは単に「アスペクト」と考えることができます。 「プログラミングの方法を説明し、それらの用語を直接実装して、毎回実装する懸念を当てにしてください。

必要な規律が少なく、高レベルの構造コード要件に沿って設計するのではなく、実際のプログラミングタスクの解決に集中できます。

とにかく、あなたの時間の5分の価値があるかもしれません;-)

幸運を!

1
mindplay.dk

これを必要とするすべてのクラスを基本クラスに実装している限り、次のようなことができます。

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にする必要があります)
1
Kris

これはパーティーに少し遅れましたが、私はこの答えを見なかったので、最近、循環クラスを処理するためにデバッグクラスに同様の何かを実装しました。 _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_で追跡することで、クラスの外部に自分のインスタンス番号を簡単に割り当てることができるということです。これは、参照番号を取得するために(クラスのコードに影響を与える)醜いハックが必要ないことを意味します。また、「醜い」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'
}
_

ご覧のとおり、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*_の代わりに、循環参照が何であるかについてもう少し情報が必要でした。

それが将来誰かを助けることを願っています。

これが私のデバッグクラスの完全なコードです、これは300行目で使用されていることがわかります

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

1
ArtisticPhoenix

これをテストするためにPECLランキットを有効にしていませんが、クラスのインスタンスが最初に作成された後で、クラス定義からコンストラクターコードを削除できる場合があります。

コンストラクター内からコンストラクターを削除できるかどうかは、興味深い実験になります。

0
Mark Baker

出力バッファリングを使用したくない場合は、おそらくvar_export?の代わりに var_dump を使用してください。

0