如何以编程方式从类的方法之一中查找类的公共属性

问题描述 投票:0回答:6

我有一个类

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 oop reflection
6个回答
37
投票

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

正如您已经意识到的,PHP 的构建在

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

21
投票

不适用于 php 版本 >=7
因此,我真的不能再推荐解决方案了。
使用反射代替

从类中获取公共属性

$publicProperties = call_user_func('get_object_vars', $this);

“技巧”是

get_object_vars
是从
call_user_func
的范围而不是对象的范围中调用的

不需要反射、独立函数、闭包等


1
投票

另一种(PHP 7.* 兼容)方法是将

$this
转换为
array
并过滤掉任何二进制键。这比使用反射要简单一些。

return array_filter(
    (array) $this,
    static fn(string $key): bool => strpos($key, "\0") !== 0, ARRAY_FILTER_USE_KEY,
);

0
投票

根据这篇文章(由 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,所以私有和受保护的属性仍然包含在结果数组中。


0
投票

使用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);
}

0
投票

您可以将其包装到闭包中并将其绑定到任何地方,然后您将拥有本地公共属性的外部范围视图。

public function getPublicVars(){
  return \Closure::bind(fn($obj) => get_object_vars($obj),null,null)($this);
}
© www.soinside.com 2019 - 2024. All rights reserved.