从 PHP 中的类外部调用私有方法和私有属性
Posted
技术标签:
【中文标题】从 PHP 中的类外部调用私有方法和私有属性【英文标题】:Call private methods and private properties from outside a class in PHP 【发布时间】:2011-02-13 21:13:09 【问题描述】:在极少数特定情况下,我想从类外部访问私有方法和变量。
我发现尽管使用了自省,但这是不可能的。
具体案例是下一个:
我想要这样的东西:
class Console
final public static function run()
while (TRUE != FALSE)
echo "\n> ";
$command = trim(fgets(STDIN));
switch ($command)
case 'exit':
case 'q':
case 'quit':
echo "OK+\n";
return;
default:
ob_start();
eval($command);
$out = ob_get_contents();
ob_end_clean();
print("Command: $command");
print("Output:\n$out");
break;
这个方法应该可以像这样注入到代码中:
Class Demo
private $a;
final public function myMethod()
// some code
Console::run();
// some other code
final public function myPublicMethod()
return "I can run through eval()";
private function myPrivateMethod()
return "I cannot run through eval()";
(这只是一个简化。真正的通过一个套接字,并实现更多的东西......)
所以……
如果您实例化类 Demo 并调用 $demo->myMethod(),您将获得一个控制台:该控制台可以访问第一个方法,编写如下命令:
> $this->myPublicMethod();
但是你不能成功运行第二个:
> $this->myPrivateMethod();
你们有任何想法吗,或者是否有任何 php 库允许您这样做?
非常感谢!
【问题讨论】:
Erm... 谁愿意让标记为私有的方法公开访问?我的意思是...如果您需要从外部访问它,只需使用 public。另外:您的控制台类与您在此处添加的方式毫无意义。它没有单独使用 OOP,基本上只是一个强制进入类的函数。 仅供参考while(true)
或 for(;;)
是更简洁和常见的循环方法,直到遇到明确的 break
或 return
。
@lamas:正如我之前所说,我将其作为 POC 而不是真实示例。真正的 Console 类或多或少大约有 1k 行,并通过组合扩展了其他一些行。代码的可维护性不是问题,因为它将被用作我们正在工作的主项目之外的独立组件,因此这不仅是“强制进入类的函数”,而且是类的摘录请勿在此处发布,以免引起人们的反感。 :) @meagar:呵呵,我把 while(TRUE != FALSE) 作为一个玩笑,因为 PHP 将 FALSE != 0 验证为 FALSE。无论如何谢谢;)
@meagar:而且,我忘了评论它,除非是 PoC,否则我从未在函数内写过 return 或 continue。我认为这不是一个好的做法,这样的事情可能会影响代码的易读性。
@lamas:我完全同意你的看法 回复:范围的选择。但我不同意你的观点,即如果一个类中没有单个 OOP 调用,并且其中没有纯静态函数,那么它就没有意义:它是一种构成库的方式,并且仍然受益来自自动加载器功能,甚至只是为了使代码更清晰,并为您的函数添加一个编目级别。
【参考方案1】:
<?php
$request="email";
$data=[1,2,3,4,5];
$name=new Update($request,$data);
class Update
private $request;
private $data;
function __construct($request,$data)
$this->request=$request;
$this->data=$data;
if($this->request=='email')
$this->update_email();
else
echo "Can't do anything";
private function update_email()
echo $this->request;
echo '\n';
foreach($this->data as $x)
echo $x."\n";
?>
【讨论】:
虽然这段代码 sn-p 可以解决问题,但它没有解释为什么或如何回答这个问题。请include an explanation for your code,因为这确实有助于提高您的帖子质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。您可以使用edit 按钮改进此答案以获得更多选票和声誉!【参考方案2】:编辑: 更新为包含带参数的私有函数调用示例。
从 PHP 5.4 开始,您可以使用预定义的 Closure
类将类的方法/属性绑定到甚至可以访问私有成员的 delta 函数。
The Closure class
例如,我们有一个带有私有变量的类,我们想在该类之外访问它:
class Foo
private $bar = "Foo::Bar";
private function add_ab($a, $b)
return $a + $b;
PHP 5.4+
$foo = new Foo;
// Single variable example
$getFooBarCallback = function()
return $this->bar;
;
$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo');
echo $getFooBar(); // Prints Foo::Bar
// Function call with parameters example
$getFooAddABCallback = function()
// As of PHP 5.6 we can use $this->fn(...func_get_args()) instead of call_user_func_array
return call_user_func_array(array($this, 'add_ab'), func_get_args());
;
$getFooAddAB = $getFooAddABCallback->bindTo($foo, 'Foo');
echo $getFooAddAB(33, 6); // Prints 39
从 PHP 7 开始,您可以使用新的 Closure::call
方法将对象的任何方法/属性绑定到回调函数,即使是私有成员:
PHP 7+
$foo = new Foo;
// Single variable example
$getFooBar = function()
return $this->bar;
;
echo $getFooBar->call($foo); // Prints Foo::Bar
// Function call with parameters example
$getFooAddAB = function()
return $this->add_ab(...func_get_args());
;
echo $getFooAddAB->call($foo, 33, 6); // Prints 39
【讨论】:
PHP 7+ 部分很棒!太好了! @NurbolAlpysbayev 谢谢。我刚刚意识到答案不包括带参数的函数调用,而只包括私有变量!更新并添加了 PHP 5.4+ 和 PHP 7+ 的私有函数调用示例。 这很好用!但是您认为这是最佳做法吗,因为他们可能出于某种原因将其设为私有? @DavidJarrin 当然,这不是最好的做法。如果可能,应该避免这种情况;当我们想要访问该私有成员并且 1) 我们不想更改类源文件(可能是在更新后会中断的现成库)和 2) 如果它是您项目的类之一,则将属性/函数更改为公共,或创建其他函数来检索该私有成员。 @ChristosLytras 是的,这就是我的情况,我真的不想更改库,因为它会在更新时中断,感谢您的回复。【参考方案3】:只需将方法公开即可。但是如果你想变得棘手,你可以试试这个(PHP 5.3):
class LockedGate
private function open()
return 'how did you get in here?!!';
$object = new LockedGate();
$reflector = new ReflectionObject($object);
$method = $reflector->getMethod('open');
$method->setAccessible(true);
echo $method->invoke($object);
【讨论】:
这正是我想要的。我目前在开发环境中使用 PHP 5.2.3,但我们正处于迁移过程中,所以这对我有很大帮助!! +1 @webbiedave:嗯,我也有 final class LockedGate private function __construct() ... ,它似乎不起作用。我想知道“最终”是否会阻止可访问性被更改。 @webbiedave:啊,我想我的问题可能是它是一个静态类。这对我有用$method = $reflector->getMethod('myStaticPrivate');
$method->setAccessible(true);
$method->invoke(NULL);
在 shell $ php -a
上尝试使用 PHP 5.3.22 失败:PHP Fatal error: Call to private method LockedGate::open() from context '' in php shell code on line 1
PHP Stack trace:
PHP 1. main() php shell code:0
Fatal error: Call to private method LockedGate::open() from context '' in php shell code on line 1
Call Stack:
5.3208 641904 1. main() php shell code:0
我已经更新了反射代码。但是,我的主要答案仍然是将方法公开。【参考方案4】:
答案公开给方法。无论您要做什么技巧,其他开发人员都无法理解。例如,他们不知道在其他一些代码中,通过查看 Demo 类可以公开访问此函数。
还有一件事。 该控制台可以访问第一个方法,编写如下命令:。这怎么可能?控制台无法使用 $this 访问演示类函数。
【讨论】:
我认为这里所有想要这样做的人都是“开发者伙伴”:)【参考方案5】:如果您能够在定义方法的类中添加方法,则可以添加在内部使用 call_user_method() 的方法。这也适用于 PHP 5.2.x
<?php
class SomeClass
public function callprivate($methodName)
call_user_method(array($this, $methodName));
private function somePrivateMethod()
echo 'test';
$object = new SomeClass();
$object->callprivate('somePrivateMethod');
【讨论】:
【参考方案6】:以下是其他答案的变体,可用于在一行中进行此类调用:
public function callPrivateMethod($object, $methodName)
$reflectionClass = new \ReflectionClass($object);
$reflectionMethod = $reflectionClass->getMethod($methodName);
$reflectionMethod->setAccessible(true);
$params = array_slice(func_get_args(), 2); //get all the parameters after $methodName
return $reflectionMethod->invokeArgs($object, $params);
【讨论】:
【参考方案7】:如果你真的想执行一些私有方法,我猜反射类是唯一的选择。无论如何,如果您只需要对私有或受保护属性的读取权限,您可以使用以下代码:
<?php
class Demo
private $foo = "bar";
$demo = new Demo();
// Will return an object with public, private and protected properties in public scope.
$properties = json_decode(preg_replace('/\\\\u([0-9a-f]4)|'.get_class($demo).'/i', '', json_encode((array) $demo)));
?>
【讨论】:
【参考方案8】:您应该问的第一个问题是,如果您需要从类外部访问它,为什么将其声明为私有?如果它不是您的代码,那么发起者可能有充分的理由将其声明为私有,直接访问它是一种非常糟糕(并且基本上无法维护)的做法。
编辑:正如 Adam V. 在 cmets 中指出的那样,您需要在调用私有方法之前使其可访问。代码示例已更新以包含此内容。不过,我还没有对其进行测试 - 只是在此处添加以保持答案更新。
话虽如此,您可以使用Reflection 来完成此操作。实例化ReflectionClass
,为要调用的方法调用getMethod
,然后在返回的ReflectionMethod
上调用invoke
。
代码示例(虽然我没有测试过,所以可能有错误)可能看起来像
$demo = new Demo();
$reflection_class = new ReflectionClass("Demo");
$reflection_method = $reflection_class->getMethod("myPrivateMethod");
$reflection_method->setAccessible(true);
$result = $reflection_method->invoke($demo, NULL);
【讨论】:
这应该会导致 ReflectionException('尝试从范围 ReflectionMethod 调用私有方法')。 PHP 反射 API是否 支持调用私有方法。您只需要在$reflection_method = $reflection_class->getMethod("myPrivateMethod")
之后添加一个 $reflection_method->setAccessible(true)
【参考方案9】:
我有时也会遇到这些问题,但是我通过我的编码标准解决了这些问题。私有或受保护的函数用前缀下划线表示,即
private function _myPrivateMethod()
然后我只是将函数公开。
public function _myPrivateMethod()
因此,尽管该函数是公共的,但命名约定会发出通知,即公共是私有的,不应真正使用。
【讨论】:
以上是关于从 PHP 中的类外部调用私有方法和私有属性的主要内容,如果未能解决你的问题,请参考以下文章