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

时间:2010-04-29 15:39:50

标签: 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;
            }
        }
    }
}

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

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库?

非常感谢!

10 个答案:

答案 0 :(得分:52)

只需将方法设为公开。但是如果你想变得棘手,你可以试试这个(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);

答案 1 :(得分:11)

修改 更新为包含带参数的私有函数调用的示例。

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

答案 2 :(得分:4)

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

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

据说,您可以使用Reflection来完成此任务。实例化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);

答案 3 :(得分:3)

以下是其他答案的变体,可用于将此类调用换成一行:

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

答案 4 :(得分:2)

我有时会遇到这些问题,但是我通过编码标准解决了这个问题。私有或受保护的函数用前缀下划线表示,即

private function _myPrivateMethod()

然后我简单地将该功能公之于众。

public function _myPrivateMethod()

因此,尽管该函数是公共的,但命名约定会给出通知,即public是私有的,不应该真正使用。

答案 5 :(得分:1)

将答案公之于众。无论你打算做什么伎俩都不会让其他开发人员理解。例如,他们不知道在其他一些代码中,通过查看Demo类,该函数已被公开访问。

还有一件事。 控制台可以访问第一个编写命令的方法,如: 。这怎么可能呢?控制台无法使用$ this访问演示类函数。

答案 6 :(得分:0)

我想如果你真的想要执行一些私有方法,那么reflectionClass是唯一的选择。无论如何,如果您只需要对privat或受保护属性的读访问权限,则可以使用以下代码:

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

?>

答案 7 :(得分:0)

如果能够在定义方法的类中添加方法,则可以添加在内部使用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');

答案 8 :(得分:0)

为什么不使用受保护的?并扩展它

答案 9 :(得分:-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";
          }
      }
    }
?>