[Php] Ridefinire i metodi di classe o la classe


Answers

runkit sembra una buona soluzione ma non è abilitata di default e alcune parti sono ancora sperimentali. Quindi ho hackerato insieme una piccola classe che sostituisce le definizioni di funzione in un file di classe. Esempio di utilizzo:

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

Quindi includi la classe da modificare e ridefinisci i suoi metodi:

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

Quindi valuta il nuovo codice:

eval($objPatch->getCode());

Un po 'grezzo ma funziona!

Question

C'è un modo per ridefinire una classe o alcuni dei suoi metodi senza usare l'ereditarietà tipica? Per esempio:

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

Cosa posso fare per sostituire buggy_function() ? Ovviamente questo è quello che mi piacerebbe fare

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

Questo è il mio dilemma: ho aggiornato una libreria di terze parti che rompe il mio codice. Non voglio modificare direttamente la libreria, poiché gli aggiornamenti futuri potrebbero rompere il codice. Sto cercando un modo semplice per sostituire il metodo di classe.

Ho trovato questa library che dice che può farlo, ma sono diffidente perché ha 4 anni.

MODIFICARE:

Avrei dovuto chiarire che non posso rinominare la classe da third_party_library a magical_third_party_library o qualsiasi altra cosa a causa dei limiti del framework.

Per i miei scopi, sarebbe possibile aggiungere semplicemente una funzione alla classe? Penso che puoi farlo in C # con qualcosa chiamato "classe parziale".




Per le persone che stanno ancora cercando questa risposta.

Dovresti usare extends in combinazione con namespaces .

come questo:

namespace MyCustomName;

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

Quindi per usarlo fai così:

use MyCustomName\third_party_library;

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



Zend Studio e PDT (ide basati su eclipse) hanno alcuni strumenti di rifrazione incorporati. Ma non ci sono metodi integrati per farlo.

Inoltre non vorresti avere un codice cattivo nel tuo sistema. Dal momento che potrebbe essere chiamato per errore.







Dato che hai sempre accesso al codice base in PHP, ridefinisci le funzioni principali della classe che vuoi sovrascrivere come segue, questo dovrebbe lasciare intatte le tue interfacce:

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

È possibile che per qualche motivo si usi una variabile privata, ma in tal caso sarà possibile accedere alla funzione estendendo la classe o la logica all'interno della classe. Allo stesso modo è possibile che desideri avere oggetti diversi della stessa classe con funzioni diverse. Se è così, non usare la statica, ma di solito vuoi che sia statica in modo da non duplicare l'uso della memoria per ogni oggetto creato. Il codice 'ranOnce' fa in modo che sia necessario inizializzarlo solo una volta per la classe, non per ogni $myObject = new third_party_library()

Ora, più avanti nel tuo codice o in un'altra classe - ogni volta che la logica raggiunge un punto in cui devi eseguire l'override della funzione - procedi come segue:

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

Come nota a margine, se si eseguono tutte le funzioni di classe in questo modo e si utilizza un archivio di chiavi / valori basato su stringhe come public static $functions['function_name'] = function(...){...}; questo può essere utile per la riflessione. Non tanto in PHP quanto in altri linguaggi, perché puoi già afferrare i nomi di classi e funzioni, ma puoi risparmiare qualche elaborazione e gli utenti futuri della tua classe possono utilizzare le sostituzioni in PHP. Tuttavia, si tratta di un ulteriore livello di riferimento indiretto, per cui eviterei di usarlo su classi primitive ove possibile.




Sì, è chiamato extend :

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

Ho aggiunto il prefisso "sd". ;-)

Tieni presente che quando estendi una classe per sovrascrivere i metodi, la firma del metodo deve corrispondere all'originale. Ad esempio, se l'originale ha detto buggy_function($foo, $bar) , deve corrispondere ai parametri della classe estendendolo.

PHP è piuttosto prolisso a riguardo.






Links