2010-05-28 2 views
8

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

function foo($bar) 
{ 
    $foo = $bar ; 
    return $foo ; 
} 

И впрыскивать некоторый код в это нравится:

function foo($bar) 
{ 
    //some code here 
    $foo = $bar ; 
    //some code here 
    return $foo ; 
} 

возможно?

+0

Я не думаю, что то, что вы хотите, возможно. Я мог ошибаться, но в целом * Reflection APIs * используются для ** получения ** информации о классах и т. Д., А не для изменения кода во время выполнения. –

+0

дайте дополнительную информацию о том, что вы пытаетесь сделать, и, может быть, мы сможем помочь найти другое решение. – Galen

+0

Хотя я уверен, что есть решение для этого, и вы знаете, что вам это нужно - вы уверены, что это то, что вы хотите? Почему вы хотите изменить характер функции во время выполнения? Лучшим вариантом может быть передача переменной, управляющей потоком функций; или, возможно, рассмотрите использование операторов eval() для запуска кода. Подумайте об этом так: как вы называете функцию, которая не имеет предопределяемого эффекта? – Kurucu

ответ

2

Посмотрите на anonymous functions. Если вы можете запустить PHP 5.3, который может быть более похожим на то, что вы пытаетесь сделать.

2

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

Существует расширение PHP под названием Runkit, которое, как мне кажется, обеспечивает этот тип функциональности - http://www.php.net/manual/en/book.runkit.php, однако это не будет установлено по умолчанию на подавляющем большинстве хостов.

Возможно, существует другой способ сделать это. Возможно, если бы вы могли дать дополнительную информацию о том, что вы пытаетесь сделать, и почему вы не можете изменить эту функцию, мы могли бы предоставить некоторые указатели. Например. это функция, которую вы хотите изменить, ядро ​​PHP, или код в сторонней библиотеке, которую вы не хотите редактировать?

+0

runkit - это бета-версия в то время, когда я отправил этот комментарий и не компилируется (дает ошибку при попытке скомпилировать на linux с php 5.3.19) – NickSoft

0

Просто идея:

function foo($bar, $preprocess, $postprocess){ 

    //IF CONDITION($preprocess) 
    include('/mylogic/'.$preprocess.".php"); //do some preprocessing here? 
    $foo = $bar ; 
    //IF CONDITION($postprocess) 
    include('/mylogic/'.$postprocess.".php"); //process $foo here? 
    //FINALLY return 
    return $foo; 

} 
+0

Хорошая идея ... хотя я 'd, вероятно, сделать $ preprocess и $ postprocess в callbacks (' functionname 'или array (' classname ',' methodname ')) и использовать call_user_func для их вызова. это будет иметь определенные последствия, но это также сделает его более безопасным ИМХО. – grossvogel

+0

это нужно сделать для всех методов в моем приложении, и я хочу, чтобы он оставался чистым – mononym

4

Ну, один путь, должен был бы сделать все вызовы методов "виртуальный":

class Foo { 
    protected $overrides = array(); 

    public function __call($func, $args) { 
     $func = strtolower($func); 
     if (isset($this->overrides[$func])) { 
      // We have a override for this method, call it instead! 
      array_unshift($args, $this); //Add the object to the argument list as the first 
      return call_user_func_array($this->overrides[$func], $args); 
     } elseif (is_callable(array($this, '_' . $func))) { 
      // Call an "internal" version 
      return call_user_func_array(array($this, '_' . $func), $args); 
     } else { 
      throw new BadMethodCallException('Method '.$func.' Does Not Exist'); 
     } 
    } 

    public function addOverride($name, $callback) { 
     $this->overrides[strtolower($name)] = $callback; 
    } 

    public function _doSomething($foo) { 
     echo "Foo: ". $foo; 
    } 
} 

$foo = new Foo(); 

$foo->doSomething('test'); // Foo: test 

PHP 5.2:

$f = create_function('$obj, $str', 'echo "Bar: " . $obj->_doSomething($str) . " Baz";'); 

PHP 5.3:

$f = function($obj, $str) { 
    echo "Bar: " . $obj->_doSomething($str) . " Baz"; 
} 

Все РНР:

$foo->addOverride('doSomething', $f); 

$foo->doSomething('test'); // Bar: Foo: test Baz 

Она проходит экземпляр объекта в качестве первого метода к «переопределение». Примечание. Этот метод «overriden» не будет иметь доступа к защищенным членам класса. Поэтому используйте геттеры (__get, __set). Она будет иметь доступ к защищенным методам, так как фактический вызов приходит от __call() ...

Примечания: Вам нужно изменить все стандартные методы, которые будут начинаться с «_» для этой работы. .. (или вы можете выбрать другую опцию префикса, или вы можете просто охватить все их защищенные) ...

-1

Возможно, мне что-то не хватает, но действительно ли вы хотите «ввести» код, как вы говорите? Чего вы пытаетесь достичь? Если вы просто хотите выполнить один блок кода, когда A произойдет, а другой, когда B произойдет, тогда вам понадобится простая логика программирования, например, оператор if().

Действительно ли вы пытаетесь изменить функцию во время выполнения? Или это просто логическая проблема?

Подробнее о том, что вам нужно сделать.

0

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

0

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

<?php 

class GenericClassInstrumentor 
{ 
    private $instrumentedClass; 
    private $className; 

    public function __construct($instrumentedClass) 
    { 
     $this->instrumentedClass = $instrumentedClass; 
     $this->className = get_class($instrumentedClass); 
    } 

    public function __call($name, $arguments) 
    { 
     $start = microtime(true); 
     $result = call_user_func_array(array($this->instrumentedClass, $name), $arguments); 
     $end = microtime(true); 
     $duration = ($end - $start) * 1000; 

     // optionally log something here 
     return $result; 
    } 
} 

Допустим, у вас экземпляр класса как $myClass, который имел функцию foo на нем, то вы могли бы сделать что-то вроде этого:

$myClass = new ClassWithFoo(); 
if ($instrumentationOn) { 
    $myClass = new GenericClassInstrumentor($myClass); 
} 

Calling $myClass->foo($bar) будет работать так же (с тем же оговорки как @ircmaxell ответ), если $instrumentationOn is true или false. Если это true, это будет просто лишний отсчет времени.

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