我有一个类
Foo
具有公共和受保护的属性。 Foo
需要有一个非静态方法,getPublicVars()
,它返回Foo
的所有公共属性的列表(这只是一个例子,我从outside知道Foo
对象调用get_object_vars()
)
将完成此任务,不需要我的 getPublicVars()
方法)。
注意: 这还必须返回在运行时分配给类定义中未定义的类实例(对象)的动态声明的属性。
示例如下:
class Foo{
private $bar = '123';
protect $boo = '456';
public $beer = 'yum';
//will return an array or comma seperated list
public function getPublicVars(){
// thar' be magic here...
}
}
$foo = new Foo();
$foo->tricky = 'dynamically added var';
$result = $foo->getPublicVars();
var_dump($result); // array or comma list with 'tricky' and 'beer'
从类自己的方法(其中 public 和 protected 都可见)中获取对象的唯一公共属性的最简洁方法是什么?
我看过:
但这似乎并没有解决我的问题,因为它指向从对象外部使用
get_object_vars()
。
自 PHP 8.1(2021 年 11 月)第一类可调用语法可用于创建闭包,然后 __invoke() 它:
return get_object_vars(...)->__invoke($this);
自 PHP 7.1(2016 年 12 月)从可调用对象创建闭包可用于创建闭包,然后 __invoke() 它:
return \Closure::fromCallable("get_object_vars")->__invoke($this);
自 PHP 7.0(2015 年 12 月)起,可以创建闭包,但超出范围:
return (function($object){return get_object_vars($object);})->bindTo(null, null)($this);
(ikkez 在 2022 年的 answer 中有 PHP 7.4 变体。)
在 PHP 7.0 之前,可以使用 call_user_func() 的实现指定行为,这是 Brad Kents' example:
return call_user_func('get_object_vars', $this);
get_object_vars
中是范围敏感的。您只需要 public 对象属性。
因此,从该函数到公共变体并不是很大的一步:
function get_object_public_vars($object) {
return get_object_vars($object);
}
调用这个
get_object_public_vars
只会给你公共属性,因为它超出了当前 $object 的范围。
ReflectionObject
:
(new ReflectionObject($this))->getProperties(ReflectionProperty::IS_PUBLIC);
这样做的好处是你不需要在全局命名空间中引入另一个函数。
不应使用函数 create_user_func() (因为每次创建新函数时都会使用 eval() 和 IIRC)。幸运的是,自 PHP 5.3(2009 年 6 月)以来,出现了匿名函数。
然而,这是对 2013 年 4 月 jumc answered 时给出的问题的正确答案 Brad Kents 的 hack 尚未出现,并且不存在其他答案来显示更改需要 PHP 5.4 的闭包范围(2012 年 3 月) ; “实现了闭包重新绑定作为 参数来绑定到。” ref).
因此,为了完整起见,PHP 5.4 语法闭包示例:
$scope = array ( function ($object) { return get_object_vars($object); }, 'bindTo' );
return call_user_func($scope(null, null), $this);
作为以下形式的替代形式:
$scope = create_function('$object', 'return get_object_vars($object);');
return $scope($this);
不适用于 php 版本 >=7
因此,我真的不能再推荐解决方案了。
使用反射代替
从类中获取公共属性
$publicProperties = call_user_func('get_object_vars', $this);
“技巧”是
get_object_vars
是从 call_user_func
的范围而不是对象的范围中调用的
不需要反射、独立函数、闭包等
另一种(PHP 7.* 兼容)方法是将
$this
转换为 array
并过滤掉任何二进制键。这比使用反射要简单一些。
return array_filter(
(array) $this,
static fn(string $key): bool => strpos($key, "\0") !== 0, ARRAY_FILTER_USE_KEY,
);
根据这篇文章(由 Vance Lucas 撰写),您可以使用“anonymous”函数在“Foo”类定义中创建一个新的调用范围,然后您可以从内部调用
get_object_vars()
。这允许您从类内部获取only公共属性,即使这些属性是稍后从外部动态创建的。
非常适合您的示例:
<?php
class Foo {
private $bar = '123';
protected $boo = '456';
public $beer = 'yum';
// return an array of public properties
public function getPublicVars(){
$publicVars = create_function('$obj', 'return get_object_vars($obj);');
return $publicVars($this);
}
}
$foo = new Foo();
$foo->tricky = 'dynamically added var';
$result = $foo->getPublicVars();
print_r($result);
输出将是:
数组 ( [啤酒] => 嗯 [tricky] => 动态添加 var )
上面提到的文章中有第二个例子,它展示了使用所谓的“闭包”(来自 php 5.3)执行相同操作的另一种方法,但由于某种原因,它不适用于 php v5.4,所以私有和受保护的属性仍然包含在结果数组中。
使用ReflectionClass,你可以轻松做到这一点。例如,下面,我创建了所有公共属性的关联数组。
$rc = new \ReflectionClass($this);
//get all the public properties.
$props = $rc->getProperties(\ReflectionProperty::IS_PUBLIC);
$ret = [];
foreach($props as $p) {
//get the name and value of each of the public properties.
$ret[$p->getName()] = $p->getValue($this);
}
您可以将其包装到闭包中并将其绑定到任何地方,然后您将拥有本地公共属性的外部范围视图。
public function getPublicVars(){
return \Closure::bind(fn($obj) => get_object_vars($obj),null,null)($this);
}