Переопределить класс методов или классов

Есть ли способ переопределить класс или некоторые его методы без использования типичного наследования? Например:

class third_party_library { function buggy_function() { return 'bad result'; } function other_functions(){ return 'blah'; } } 

Что можно сделать для замены buggy_function() ? Очевидно, это то, что я хотел бы сделать

 class third_party_library redefines third_party_library{ function buggy_function() { return 'good result'; } function other_functions(){ return 'blah'; } } 

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

Я нашел эту библиотеку, которая говорит, что она может это сделать, но я опасаюсь, поскольку ей 4 года.

РЕДАКТИРОВАТЬ:

Я должен был уточнить, что я не могу переименовать класс из third_party_library в magical_third_party_library или что-то еще из-за ограничений рамки.

Для моих целей можно было бы просто добавить функцию в класс? Я думаю, вы можете сделать это на C # с чем-то, называемым «частичным классом».

Solutions Collecting From Web of "Переопределить класс методов или классов"

Это называется патчем обезьян . Но у PHP нет собственной поддержки.

Хотя, как отмечали другие, библиотека runkit доступна для добавления поддержки к языку и является преемником classkit . И, хотя он, казалось, был оставлен его создателем (заявив, что он несовместим с PHP 5.2 и более поздними версиями), теперь у проекта появился новый дом и сопровождающий .

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

Тем не менее, runkit_method_redefine видимому, является тем, что вы ищете, и пример его использования можно найти в /tests/runkit_method_redefine.phpt в репозитории:

 runkit_method_redefine('third_party_library', 'buggy_function', '', 'return \'good result\'' ); 

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

 class Patch { private $_code; public function __construct($include_file = null) { if ( $include_file ) { $this->includeCode($include_file); } } public function setCode($code) { $this->_code = $code; } public function includeCode($path) { $fp = fopen($path,'r'); $contents = fread($fp, filesize($path)); $contents = str_replace('<?php','',$contents); $contents = str_replace('?>','',$contents); fclose($fp); $this->setCode($contents); } function redefineFunction($new_function) { preg_match('/function (.+)\(/', $new_function, $aryMatches); $func_name = trim($aryMatches[1]); if ( preg_match('/((private|protected|public) function '.$func_name.'[\w\W\n]+?)(private|protected|public)/s', $this->_code, $aryMatches) ) { $search_code = $aryMatches[1]; $new_code = str_replace($search_code, $new_function."\n\n", $this->_code); $this->setCode($new_code); return true; } else { return false; } } function getCode() { return $this->_code; } } 

Затем включите класс, который нужно изменить, и переопределите его методы:

 $objPatch = new Patch('path_to_class_file.php'); $objPatch->redefineFunction(" protected function foo(\$arg1, \$arg2) { return \$arg1+\$arg2; }"); 

Затем измените новый код:

 eval($objPatch->getCode()); 

Немного грубо, но это работает!

Да, это называется extend :

 <?php class sd_third_party_library extends third_party_library { function buggy_function() { return 'good result'; } function other_functions(){ return 'blah'; } } 

Я префикс «sd». 😉

Имейте в виду, что при расширении класса для переопределения методов подпись метода должна соответствовать оригиналу. Так, например, если оригинал сказал buggy_function($foo, $bar) , он должен соответствовать параметрам в классе, расширяющем его.

PHP довольно подробный.

Ради полноты – исправление обезьян доступно в PHP через runkit . Подробнее см. runkit_method_redefine() .

Как обернуть его в другой класс, как

 class Wrapper { private $third_party_library; function __construct() { $this->third_party_library = new Third_party_library(); } function __call($method, $args) { return call_user_func_array(array($this->third_party_library, $method), $args); } } 

Там всегда расширяется класс с помощью нового, правильного метода и вызывает этот класс вместо багги.

 class my_better_class Extends some_buggy_class { function non_buggy_function() { return 'good result'; } } 

(Извините за дерьмовое форматирование)

Для людей, которые все еще ищут этот ответ.

Вы должны использовать расширения в сочетании с пространствами имен .

как это:

 namespace MyCustomName; class third_party_library extends \third_party_library { function buggy_function() { return 'good result'; } function other_functions(){ return 'blah'; } } 

Затем, чтобы использовать его, выполните следующие действия:

 use MyCustomName\third_party_library; $test = new third_party_library(); $test->buggy_function(); //or static. third_party_library::other_functions(); 

Zend Studio и PDT (ide на основе eclipse) имеют некоторые встроенные инструменты рефракционирования. Но для этого нет встроенных методов.

Также вы не хотите иметь плохой код в своей системе вообще. Так как это может быть вызвано по ошибке.

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

Возможно, вы сможете сделать это с помощью runkit. http://php.net/runkit

Вы можете сделать копию класса библиотеки, со всем тем же, кроме имени класса. Затем переопределите этот переименованный класс.

Это не идеально, но это улучшает видимость изменений расширенного класса. Если вы выберете библиотеку с помощью чего-то вроде Composer, вам придется перенести копию в исходный элемент управления и обновить ее при обновлении библиотеки.

В моем случае это была старая версия https://github.com/bshaffer/oauth2-server-php . Я модифицировал автозагрузчик библиотеки, чтобы извлечь файл класса. Мой файл класса взял первоначальное имя и расширил скопированную версию одного из файлов.

Поскольку у вас всегда есть доступ к базовому коду в PHP, переопределите основные функции класса, которые вы хотите переопределить, как показано ниже, это должно привести к неповрежденным интерфейсам:

 class third_party_library { public static $buggy_function; public static $ranOnce=false; public function __construct(){ if(!self::$ranOnce){ self::$buggy_function = function(){ return 'bad result'; }; self::$ranOnce=true; } . . . } function buggy_function() { return self::$buggy_function(); } } 

По какой-то причине вы можете использовать закрытую переменную, но тогда вы сможете получить доступ к этой функции, расширив класс или логику внутри класса. Точно так же вы можете захотеть, чтобы разные объекты одного и того же класса имели разные функции. Если это так, не используйте static, но обычно вы хотите, чтобы он был статичным, поэтому вы не дублируете использование памяти для каждого сделанного объекта. Код «ranOnce» просто позволяет вам только инициализировать его один раз для класса, а не для каждого $myObject = new third_party_library()

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

 $backup['buggy_function'] = third_party_library::$buggy_function; third_party_library::$buggy_function = function(){ //do stuff return $great_calculation; } . . . //do other stuff that needs the override . //when finished, restore the original function . third_party_library::$buggy_function=$backup['buggy_function']; 

В качестве побочного примечания, если вы выполняете все свои функции класса таким образом и используете строковое хранилище ключей / значений, например public static $functions['function_name'] = function(...){...}; это может быть полезно для размышлений. Не так много в PHP, как и другие языки, хотя, поскольку вы уже можете захватить имена классов и функций, но вы можете сохранить некоторую обработку, и будущие пользователи вашего класса могут использовать переопределения в PHP. Это, однако, один дополнительный уровень косвенности, поэтому я бы избегал использовать его на примитивных классах, где это возможно.