PHP Conference Japan 2024

get_object_vars

(PHP 4、PHP 5、PHP 7、PHP 8)

get_object_vars取得給定物件的屬性

描述

get_object_vars(object $object): array

根據作用域,取得給定 object 可存取的非靜態屬性。

參數

object

物件實例。

傳回值

傳回指定 object 在作用域中定義的可存取非靜態屬性的關聯陣列。

範例

範例 #1 get_object_vars() 的用法

<?php

class foo {
private
$a;
public
$b = 1;
public
$c;
private
$d;
static
$e;

public function
test() {
var_dump(get_object_vars($this));
}
}

$test = new foo;
var_dump(get_object_vars($test));

$test->test();

?>

以上範例將輸出

array(2) {
  ["b"]=>
  int(1)
  ["c"]=>
  NULL
}
array(4) {
  ["a"]=>
  NULL
  ["b"]=>
  int(1)
  ["c"]=>
  NULL
  ["d"]=>
  NULL
}

注意:

未初始化的屬性會被視為無法存取,因此不會包含在陣列中。

參見

新增註解

使用者貢獻的註解 5 個註解

52
fmmarzoa at librexpresion dot org
20 年前
您仍然可以將物件轉換為陣列,以取得其所有成員並查看其可見性。範例

<?php

class Potatoe {
public
$skin;
protected
$meat;
private
$roots;

function
__construct ( $s, $m, $r ) {
$this->skin = $s;
$this->meat = $m;
$this->roots = $r;
}
}

$Obj = new Potatoe ( 1, 2, 3 );

echo
"<pre>\n";
echo
"Using get_object_vars:\n";

$vars = get_object_vars ( $Obj );
print_r ( $vars );

echo
"\n\nUsing array cast:\n";

$Arr = (array)$Obj;
print_r ( $Arr );

?>

這會傳回

Using get_object_vars
Array
(
[skin] => 1
)

Using array cast
Array
(
[skin] => 1
[ * meat] => 2
[ Potatoe roots] => 3
)

如您所見,您可以從此轉換取得每個成員的可見性。陣列索引中看起來像是空格的是 '\0' 字元,因此剖析索引的一般規則似乎是

Public 成員:member_name
Protected 成員:\0*\0member_name
Private 成員:\0Class_name\0member_name

我編寫了一個 obj2array 函式,可為每個索引建立沒有可見性的項目,因此您可以在陣列中像在物件內一樣處理它們

<?php

function obj2array ( &$Instance ) {
$clone = (array) $Instance;
$rtn = array ();
$rtn['___SOURCE_KEYS_'] = $clone;

while ( list (
$key, $value) = each ($clone) ) {
$aux = explode ("\0", $key);
$newkey = $aux[count($aux)-1];
$rtn[$newkey] = &$rtn['___SOURCE_KEYS_'][$key];
}

return
$rtn;
}

?>

我也建立了一個 <i>bless</i> 函式,其作用類似於 Perl 的 bless,因此您可以進一步轉換陣列,將其轉換為特定類別的物件

<?php

function bless ( &$Instance, $Class ) {
if ( ! (
is_array ($Instance) ) ) {
return
NULL;
}

// 先取得來源索引鍵(如果有的話)
if ( isset ($Instance['___SOURCE_KEYS_'])) {
$Instance = $Instance['___SOURCE_KEYS_'];
}

// 從陣列取得序列化資料
$serdata = serialize ( $Instance );

list (
$array_params, $array_elems) = explode ('{', $serdata, 2);
list (
$array_tag, $array_count) = explode (':', $array_params, 3 );
$serdata = "O:".strlen ($Class).":\"$Class\":$array_count:{".$array_elems;

$Instance = unserialize ( $serdata );
return
$Instance;
}
?>

使用這些方法,你可以做到像這樣的事情:

<?php

define
("SFCMS_DIR", dirname(__FILE__)."/..");
require_once (
SFCMS_DIR."/Misc/bless.php");

class
Potatoe {
public
$skin;
protected
$meat;
private
$roots;

function
__construct ( $s, $m, $r ) {
$this->skin = $s;
$this->meat = $m;
$this->roots = $r;
}

function
PrintAll () {
echo
"skin = ".$this->skin."\n";
echo
"meat = ".$this->meat."\n";
echo
"roots = ".$this->roots."\n";
}
}

$Obj = new Potatoe ( 1, 2, 3 );

echo
"<pre>\n";
echo
"使用 get_object_vars:\n";

$vars = get_object_vars ( $Obj );
print_r ( $vars );

echo
"\n\n使用 obj2array 函式:\n";

$Arr = obj2array($Obj);
print_r ( $Arr );

echo
"\n\n將所有成員設定為 0。\n";
$Arr['skin']=0;
$Arr['meat']=0;
$Arr['roots']=0;

echo
"將陣列轉換為原始類別的實例。\n";
bless ( $Arr, Potatoe );

if (
is_object ($Arr) ) {
echo
"\$Arr 現在是一個物件。\n";
if (
$Arr instanceof Potatoe ) {
echo
"\$Arr 是 Potatoe 類別的一個實例。\n";
}
}

$Arr->PrintAll();

?>
11
Trismegiste
2 年前
請留意未初始化屬性所帶來的隱藏行為。註解說明:「未初始化的屬性被視為無法存取,因此不會包含在陣列中。」但在 PHP 8.1 中並不完全如此。這取決於屬性是否有宣告類型。

<?php

class Example
{
public
$untyped;
public
string $typedButNotInitialized;
public ?
string $typedOrNullNotInitialized;
public ?
string $typedOrNullWithDefaultNull = null;
}

var_dump(get_object_vars(new Example()));
?>

將會輸出

array(2) {
["untyped"]=>
NULL
["typedOrNullWithDefaultNull"]=>
NULL
}

如你所見,只有 "untyped" 和 "typedOrNullWithDefaultNull" 屬性會透過 get_object_vars() 輸出。當你遷移舊程式碼,並在沒有正確初始化(或預設值)的情況下,隨意地為所有地方加上類型宣告,並假設它會像舊程式碼一樣預設為 NULL 時,你可能會遇到問題。

希望這對你有幫助
4
marcus at marcusball dot me
3 年前
當處理大量物件時,值得注意的是,使用 `get_object_vars()` 可能會大幅增加記憶體使用量。

如果實例化的物件只使用類別中預先定義的屬性,那麼 PHP 可以對類別屬性使用單一雜湊表,而對物件屬性使用小型的、記憶體效率高的陣列。

如果一個類別定義了三個屬性($foo、$bar 和 $baz),「PHP 不再需要在雜湊表中儲存資料,而是可以說 $foo 是屬性 0,$bar 是屬性 1,$baz 是屬性 2,然後只需將屬性儲存在一個三元素的 C 陣列中。這表示 PHP 只需要在類別中使用一個雜湊表來進行屬性名稱到偏移量的對應,並在個別物件中使用記憶體效率高的 C 陣列。」

然而,如果你像這樣在物件上呼叫 `get_object_vars()`,那麼 PHP 將會為個別物件建立一個雜湊表。如果你有大量的物件,並且你在所有物件上呼叫 `get_object_vars()`,那麼將會為每個物件建立一個雜湊表,導致更多的記憶體使用量。這可以在這個錯誤報告中看到:https://bugs.php.net/bug.php?id=79392

這種影響可以在這個範例中看到

<?php
class Example {
public
$foo;
public
$bar;
public
$baz;
}

function
printMem($label) {
$usage = memory_get_usage();
echo
sprintf('%s: %d (%.2f MB)', $label, $usage, $usage / 1000000) . PHP_EOL;
}

printMem('start');

$objects = [];
for (
$i = 0; $i < 20000; $i++) {
$obj = new Example;
$obj->foo = bin2hex(random_bytes(5));
$obj->bar = bin2hex(random_bytes(5));
$obj->baz = bin2hex(random_bytes(5));
$objects[] = $obj;
}

printMem('before get_object_vars');

// Clone each object, and get the vars on the clone
foreach ($objects as $obj) {
$c = clone $obj;
$vars = get_object_vars($c);

// Accessing and modifying the original object is fine.
foreach ($vars as $var => $val) {
$obj->{$var} = strrev($val);
}
}

printMem('get_object_vars using clone');

// Get the vars on each object directly
foreach ($objects as $obj) {
$vars = get_object_vars($obj);

// The memory is used even if you do not modify the object.
}

printMem('get_object_vars direct access');
?>

這段程式碼的輸出結果如下:

start: 405704 (0.41 MB)
before get_object_vars: 6512416 (6.51 MB)
get_object_vars using clone: 6033408 (6.03 MB)
get_object_vars direct access: 13553408 (13.55 MB)

簡而言之,如果您使用類別來避免與雜湊表(如同關聯陣列)相關聯的額外記憶體使用量,請注意 `get_object_vars()` 會為傳遞給它的任何物件建立一個雜湊表。

這似乎在所有 PHP 版本中都存在;我已在 PHP 5、7 和 8 上測試過。

引述內容來自 Nikic 關於陣列和雜湊表記憶體使用量的部落格文章,以及 Github gist「為什麼物件(通常)在 PHP 中比陣列使用更少的記憶體」。
7
niemans at pbsolo dot nl
3 年前
您可以使用匿名類別從類別內部傳回 public 變數

public function getPublicVars () {
$me = new class {
function getPublicVars($object) {
return get_object_vars($object);
}
};
return $me->getPublicVars($this);
}

測試腳本

class Test {
protected $protected;
public $public;
private $private;
public function getAllVars () {
return call_user_func('get_object_vars', $this);
}
public function getPublicVars () {
$me = new class {
function getPublicVars($object) {
return get_object_vars($object);
}
};
return $me->getPublicVars($this);
}
}

$test = new Test();
print_r(get_object_vars($test)); // array("public" => NULL)
print_r($test->getAllVars()); // array("protected" => NULL, "public" => NULL, "private" => NULL)
print_r($test->getPublicVars()); // array("public" => NULL)
7
Fabien Haddadi
12 年前
似乎沒有任何函數可以判斷類別的所有 *static* 變數。

我為了專案需要,提出了這個方法:

<?php
function get_class_static_vars($object) {
return
array_diff(get_class_vars(get_class($object)), get_object_vars($object));
}
?>

它依賴一個有趣的特性:`get_object_vars` 僅傳回物件的非靜態變數。
To Top