two multiple method herencia example classes php class methods redefine

multiple - php override method



Redefina los métodos o la clase de la clase (12)

¿Hay alguna forma de redefinir una clase o algunos de sus métodos sin usar herencia típica? Por ejemplo:

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

¿Qué puedo hacer para reemplazar buggy_function() ? Obviamente esto es lo que me gustaría hacer

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

Este es mi dilema exacto: actualicé una biblioteca de terceros que rompe mi código. No quiero modificar la biblioteca directamente, ya que las futuras actualizaciones podrían volver a romper el código. Estoy buscando una manera perfecta de reemplazar el método de clase.

Encontré esta library que dice que puede hacerlo, pero soy cauteloso ya que tiene 4 años.

EDITAR:

Debería haber aclarado que no puedo cambiar el nombre de la clase de third_party_library a magical_third_party_library o cualquier otra cosa debido a las limitaciones del marco.

Para mis propósitos, ¿sería posible agregar una función a la clase? Creo que puedes hacer esto en C # con algo llamado "clase parcial".


¿Qué tal envolverlo en otra clase como

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


Como siempre tiene acceso al código base en PHP, redefina las funciones de la clase principal que desea sobrescribir de la siguiente manera, esto debería dejar intactas sus interfaces:

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

Por algún motivo, puede utilizar una variable privada, pero solo podrá acceder a la función extendiendo la clase o la lógica dentro de la clase. Del mismo modo, es posible que desee tener diferentes objetos de la misma clase con diferentes funciones. Si es así, no use estática, pero generalmente quiere que sea estática para que no duplique el uso de la memoria para cada objeto creado. El código ''ranOnce'' solo asegura que solo necesita inicializarlo una vez para la clase, no para cada $myObject = new third_party_library()

Ahora, más adelante en su código u otra clase, siempre que la lógica llegue a un punto en el que necesite anular la función, simplemente haga lo siguiente:

$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''];

Como nota al margen, si usted hace todas las funciones de su clase de esta manera y utiliza un almacén de claves / valores basado en cadenas como public static $functions[''function_name''] = function(...){...}; esto puede ser útil para la reflexión. No tanto en PHP como en otros lenguajes, porque ya puedes tomar los nombres de clases y funciones, pero puedes guardar algo de procesamiento y los futuros usuarios de tu clase pueden usar anulaciones en PHP. Sin embargo, es un nivel adicional de indirección, por lo que evitaría usarlo en clases primitivas siempre que sea posible.


En aras de la integridad, el parche de mono está disponible en PHP a través de runkit . Para más detalles, vea runkit_method_redefine() .



Para las personas que todavía están buscando esta respuesta.

Debe usar extends en combinación con namespaces .

Me gusta esto:

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

Entonces, para usarlo hazlo así:

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


Puede hacer una copia de la clase de la biblioteca, con todo igual excepto el nombre de la clase. Luego, anule esa clase renombrada.

No es perfecto, pero mejora la visibilidad de los cambios de la clase que se extiende. Si busca la biblioteca con algo así como Composer, deberá enviar la copia al control de fuente y actualizarla cuando actualice la biblioteca.

En mi caso, era una versión anterior de https://github.com/bshaffer/oauth2-server-php . Modifiqué el autocargador de la biblioteca para buscar el archivo de mi clase. Mi archivo de clase tomó el nombre original y extendió una versión copiada de uno de los archivos.


Sí, se llama extend :

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

Prefijo con "sd". ;-)

Tenga en cuenta que cuando extiende una clase para anular los métodos, la firma del método debe coincidir con el original. Entonces, por ejemplo, si el original dijo buggy_function($foo, $bar) , tiene que coincidir con los parámetros en la clase que lo extiende.

PHP es bastante detallado al respecto.


Se llama parche de mono . Pero, PHP no tiene soporte nativo para ello.

Sin embargo, como otros también han señalado, la biblioteca runkit está disponible para agregar soporte al idioma y es el sucesor de classkit . Y, aunque parecía haber sido abandoned por su creador (habiendo declarado que no era compatible con PHP 5.2 y posterior), el proyecto ahora parece tener un nuevo hogar y mantenedor .

Todavía no puedo decir que soy fan de su enfoque. Realizar modificaciones mediante la evaluación de cadenas de código siempre me ha parecido potencialmente peligroso y difícil de depurar.

Aún así, runkit_method_redefine parece ser lo que estás buscando, y un ejemplo de su uso se puede encontrar en /tests/runkit_method_redefine.phpt en el repositorio:

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


Si la biblioteca crea explícitamente la clase incorrecta y no utiliza un localizador o sistema de dependencia, no tiene suerte. No hay forma de reemplazar un método en otra clase a menos que tenga una subclase. La solución podría ser crear un archivo de parche que corrige la biblioteca, para que pueda actualizar la biblioteca y volver a aplicar el parche para corregir ese método específico.


Siempre se está ampliando la clase con un método nuevo y adecuado, y se está llamando a esa clase en lugar de a la incorrecta.

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

(Perdón por el formato de mierda)


Zend Studio y PDT (eclipse based ide) tienen algunas herramientas de refracción integradas. Pero no hay métodos integrados para hacer esto.

Además, no querría tener ningún código malo en su sistema. Dado que podría ser invocado por error.


runkit parece una buena solución, pero no está habilitada por defecto y algunas partes siguen siendo experimentales. Así que arreglé una pequeña clase que reemplaza las definiciones de funciones en un archivo de clase. Ejemplo de uso:

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

Luego incluya la clase a modificar y redefina sus métodos:

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

Luego evalúa el nuevo código:

eval($objPatch->getCode());

Un poco crudo pero funciona!