2010-04-29 5 views
10

Я хочу получить доступ к приватным методам и переменным извне классов в очень редких случаях.Вызов частных методов и частных объектов извне класса в PHP

Я видел, что это невозможно, хотя интроспекция используется.

Конкретный случай является следующий:

Я хотел бы иметь что-то вроде этого:

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, и вы называете $ демо> MyMethod(), вы получите консоль: что консоль может получить доступ к первому способу написания команды, как:

> $this->myPublicMethod(); 

Но вы не можете успешно работать второй:

> $this->myPrivateMethod(); 

у кого из вас есть какие-либо идеи, или, если есть библиотека для PHP, что позволяет сделать это?

Большое спасибо!

+1

Erm ... Кто бы хотел, чтобы методы, помеченные частным общедоступным? Я имею в виду ... если вам нужно получить доступ к нему извне, просто используйте публикацию. Также: ваш класс консоли не имеет смысла, как вы его добавили. Он не использует ООП в одиночку и в основном является просто функцией, запущенной в класс. – lamas

+0

FYI 'while (true)' или 'for (;;)' - несколько более сжатые и распространенные методы цикла, пока не будут обнаружены явные 'break' или' return'. – meagar

+0

@lamas: Как я уже говорил ранее, я сделал это больше как POC, чем настоящий пример. Настоящий класс консоли имеет более или менее около 1 тыс. Строк и расширяет некоторые другие по составу.Ремонтопригодность кода не является проблемой, поскольку она будет использоваться как изолированный компонент вне основного проекта, над которым мы работаем, так что это не только «функция, принудительная в класс», но и выдержка из класса, который будет не следует размещать здесь, чтобы люди не раздражались. :) @meagar: Хе-хе, я сделал это (TRUE! = FALSE) как шутка, так как PHP проверяет FALSE! = 0 как FALSE. спасибо в любом случае;) –

ответ

43

Просто сделайте метод общедоступным. Но если вы хотите получить сложно, вы можете попробовать это (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); 
+0

, именно этого я и искал. В настоящее время я использую PHP 5.2.3 в среде dev, но мы в процессе миграции, так что это помогает мне много! +1 –

+0

@webbiedave: Хм, у меня также есть заключительный класс LockedGate {private function __construct() {} ...}, и он, похоже, не работает. Интересно, «окончательный» не позволяет изменить доступность. –

+2

@webbiedave: Ах, я думаю, что моя проблема, возможно, заключалась в том, что это был статический класс. Это работало для меня '$ method = $ reflector-> getMethod ('myStaticPrivate');' $ method-> setAccessible (true); '$ method-> invoke (NULL);' –

4

Первый вопрос, который вы должны задать, если вам нужно получить к нему доступ из-за пределов класса, почему вы объявили его закрытым? Если это не ваш код, у создателя, вероятно, была веская причина объявить его конфиденциальной, и доступ к нему напрямую - это очень плохой (и в значительной степени неподъемный) практикой.

РЕДАКТИРОВАТЬ: Как указывает Адам В., в комментариях вам нужно сделать доступный метод доступным до его вызова. Обновлен образец кода, чтобы включить это. Я еще не протестировал его, просто добавив сюда, чтобы сохранить ответ.

Чтобы сказать это, вы можете использовать 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); 
+0

Это должно привести к исключению ReflectionException («Пытаться вызвать частный метод из области ReflectionMethod»). – webbiedave

+0

Doh! Вы правы - мои извинения. – Dathan

+0

PHP Reflection API * позволяет * поддерживать вызовы частных методов. Вам просто нужен '$ reflection_method-> setAccessible (true)' после '$ reflection_method = $ reflection_class-> getMethod (« myPrivateMethod »)' –

2

У меня есть эти проблемы тоже иногда, но я получаю вокруг него через мои стандарты кодирования. Частные или защищенные функции обозначаются символом подчеркивания префикса, т.е.

private function _myPrivateMethod() 

Затем я просто делаю эту функцию общедоступной.

public function _myPrivateMethod() 

Таким образом, хотя функция общественного соглашение об именах дает уведомление, что в то время как публика является приватным и не должны действительно быть использованы.

0

Я предполагаю, что reflectionClass является единственной альтернативой, если вы действительно хотите, чтобы выполнить некоторые частные методы. Во всяком случае, если вы просто должны иметь доступ к приват или защищенным свойствам чтения, вы можете использовать этот код:

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

?> 
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); 
} 
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'); 
0

Ответ передается на рассмотрение методу. Какой бы трюк вы ни делали, это было бы непонятно другим разработчикам. Например, они не знают, что в каком-то другом коде эта функция была доступна как общедоступная, посмотрев класс Demo.

Еще одна вещь. Эта консоль может получить доступ к первому методу, написав команду вроде:. Как это возможно? Консоль не может получить доступ к функциям демонстрационного класса, используя $ this.

4

По PHP 5.4, вы можете использовать предопределенный Closure класс связать метод/свойство класса дельта-функций, имеет доступ даже к закрытым членам.

The Closure class

К примеру у нас есть класс с частной переменной, и мы хотим, чтобы получить доступ к нему вне класса:

class Foo { 
    private $bar = "Foo::Bar"; 
} 

PHP 5.4+

$foo = new Foo; 
$getFooBarCallback = function() { 
    return $this->bar; 
}; 

$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo'); 
echo $getFooBar(); // Prints Foo::Bar 

Начиная с PHP 7, вы можете использовать новый Closure::call met корыто, чтобы связать любой метод/свойство в obect к функции обратного вызова, даже для частных участников:

PHP 7+

$foo = new Foo; 
$getFooBar = function() { 
    return $this->bar; 
} 

echo $getFooBar->call($foo); // Prints Foo::Bar 
0

Почему бы вам не использовать защищенный? И расширить его

Смежные вопросы