在PHP中从类外部调用私有方法和私有属性

Pab*_*res 16 php visibility introspection

我想在非常罕见的特定情况下从类外部访问私有方法和变量.

我已经看到尽管使用了内省,但这是不可能的.

具体案例是下一个案例:

我想要这样的东西:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个方法应该能够像这样注入代码中:

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()";
    }
}
Run Code Online (Sandbox Code Playgroud)

(这只是一个简化.真正的一个通过套接字,并实现了一堆更多的东西...)

所以...

如果您实例化Demo类并调用$ demo-> myMethod(),您将获得一个控制台:该控制台可以访问第一个编写命令的方法,如:

> $this->myPublicMethod();
Run Code Online (Sandbox Code Playgroud)

但你无法成功运行第二个:

> $this->myPrivateMethod();
Run Code Online (Sandbox Code Playgroud)

你们有没有任何想法,或者是否有任何允许你这样做的PHP库?

非常感谢!

web*_*ave 57

只需将方法公之于众.但如果你想变得棘手,你可以尝试这个(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);
Run Code Online (Sandbox Code Playgroud)

  • 在shell $ $ -a`上尝试使用PHP 5.3.22会失败:`PHP致命错误:在第1行的PHP shell代码中从上下文''调用私有方法LockedGate :: open()``PHP Stack trace :``PHP 1. {main}()php shell代码:0``致命错误:从第1行的php shell代码中的上下文''调用私有方法LockedGate :: open()``Call Stack:``5.3208 641904 1. {main}()php shell代码:0` (3认同)
  • @webbiedave:啊,我想我的问题可能是它是一个静态类.这对我有用'$ method = $ reflector-> getMethod('myStaticPrivate');``$ method-> setAccessible(true);``$ method-> invoke(NULL);` (2认同)

Chr*_*ras 14

编辑: 更新以包含带参数的私有函数调用的示例.

PHP 5.4开始,您可以使用预定义的Closure类将类的方法/属性绑定到甚至可以访问私有成员的delta函数.

Closure类

例如,我们有一个带有私有变量的类,我们想要在类外部访问它:

class Foo {
    private $bar = "Foo::Bar";
    private function add_ab($a, $b) {
        return $a + $b;
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

从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
Run Code Online (Sandbox Code Playgroud)

  • PHP 7+ 部分很棒!伟大的! (2认同)
  • @DavidJarrin 当然,这不是 ** 最佳做法。如果可能,应该避免这种情况;当我们想要访问该私有成员时可以使用它 *1)* 我们不想更改类源文件(可能是一个现成的库,更新后会损坏)和 *2)* 如果它是一个您的项目的类,然后将属性/函数更改为公共,或创建其他函数来检索该私有成员。 (2认同)

Dat*_*han 5

您应该问的第一个问题是,如果您需要从类外部访问它,为什么将其声明为私有?如果这不是您的代码,那么发起者可能有充分的理由将其声明为私有,并且直接访问它是一种非常糟糕的(并且很大程度上无法维护的)做法。

编辑:正如 Adam V. 在评论中指出的那样,您需要在调用私有方法之前使其可访问。代码示例已更新以包含此内容。不过,我还没有测试过它 - 只是在此处添加以保持答案更新。

话虽如此,您可以使用反射来完成此任务。实例化ReflectionClass,调用getMethod您要调用的方法,然后调用invoke返回的ReflectionMethod.

代码示例(虽然我还没有测试过,所以可能有错误)可能看起来像

$demo = new Demo();
$reflection_class = new ReflectionClass("Demo");
$reflection_method = $reflection_class->getMethod("myPrivateMethod");
$reflection_method->setAccessible(true);
$result = $reflection_method->invoke($demo, NULL);
Run Code Online (Sandbox Code Playgroud)