¿Cómo reemplazo una función declarada dentro de la clase de un plugin en functions.php?
Quiero modificar una función en un plugin. Está declarada en el archivo principal del plugin de esta manera:
class WCPGSK_Main {
...
public function wcpgsk_email_after_order_table($order) {
...
}
}
Y es llamada desde allí así:
add_action( 'woocommerce_email_after_order_table', array($this, 'wcpgsk_email_after_order_table') );
Supongo que sería posible reemplazarla si tuviera acceso a la clase en functions.php. Entonces podría escribir algo como esto:
$wcpgsk = new WCPGSK_Main;
remove_action( 'woocommerce_email_after_order_table', array($wcpgsk, 'wcpgsk_email_after_order_table') );
function customized_wcpgsk_email_after_order_table($order) {
...
}
add_action( 'woocommerce_email_after_order_table', array($wcpgsk, 'customized_wcpgsk_email_after_order_table') );
Mi idea para obtener acceso a la clase en el archivo functions.php fue incluir el archivo donde se declara la clase en functions.php:
require_once('/wp-content/plugins/woocommerce-poor-guys-swiss-knife/woocommerce-poor-guys-swiss-knife.php');
$wcpgsk = new WCPGSK_Main;
...
Pero esto no funciona porque el archivo del plugin se incluye cuando el plugin se está inicializando en WordPress, supongo.
¿Hay alguna manera de reescribir la función sin tocar los archivos del plugin?
Si tu plugin está registrado de esta manera:
class Test_Class_Parent {
function __construct() {
add_action('wp_head',array($this,'test_method'));
}
function test_method() {
echo 'Echoed from the parent';
}
}
$p = new Test_Class_Parent();
Entonces deberías poder eliminar el filtro accediendo al global:
class Test_Class_Child extends Test_Class_Parent {
function __construct() {
$this->unregister_parent_hook();
add_action('wp_head',array($this,'test_method'));
}
function unregister_parent_hook() {
global $p;
remove_action('wp_head',array($p,'test_method'));
}
function test_method() {
echo 'Echoed from the child';
}
}
$c = new Test_Class_Child();
De lo contrario, necesitarás recorrer el global
$wp_filter
para encontrar la clave de registro:
class Test_Class_Child extends Test_Class_Parent {
function __construct() {
$this->unregister_parent_hook();
add_action('wp_head',array($this,'test_method'));
}
function unregister_parent_hook() {
global $wp_filter;
if (!empty($wp_filter['wp_head'])) {
foreach($wp_filter['wp_head'] as $cb) {
foreach ($cb as $k => $v) {
if (
isset($v['function'])
&& is_a($v['function'][0],'Test_Class_Parent')
&& isset($v['function'][1])
&& 'test_method' == $v['function'][1]
) {
remove_action('wp_head',$k);
}
}
}
}
}
function test_method() {
echo 'Echoed from the child';
}
}
$c = new Test_Class_Child();
Esto consume muchos recursos y realmente no debería hacerse a menos que no tengas otra opción.

Esto debería funcionar:
add_action( 'woocommerce_init', 'remove_wcpgsk_email_order_table' );
function remove_wcpgsk_email_order_table() {
global $wcpgsk;
remove_action( 'woocommerce_email_after_order_table', array( $wcpgsk, 'wcpgsk_email_after_order_table' ) );
}

existe una función remove_action: https://codex.wordpress.org/Function_Reference/remove_action

Sí, eso es lo que me faltó, este plugin tiene una variable a la que se puede acceder como global. Mi estupidez. Gracias por tu respuesta, esto funciona en este caso particular (para este plugin).

Ese plugin hace que su función de inicialización wcpgsk_init()
sea pluggable, por lo que otra forma de sobrescribirla es definirla primero en un plugin must-use (ya que es demasiado tarde en el "functions.php" de tu tema). Así que podrías poner tu sobrescritura en "wp-content/mu-plugins/functions.php":
function wcpgsk_init() {
global $wcpgsk, $wcpgsk_about, $wcpgsk_options, $wcpgsk_session, $wcpgsk_woocommerce_active;
//solo continuar cargando
if ( $wcpgsk_woocommerce_active && version_compare( WOOCOMMERCE_VERSION, "2.0" ) >= 0 ) {
$FILE = WP_PLUGIN_DIR . '/woocommerce-poor-guys-swiss-knife/woocommerce-poor-guys-swiss-knife.php'; // Fake __FILE__
$dirname = dirname( $FILE ) . '/';
$wcpgsk_options = get_option('wcpgsk_settings', true);
require_once( $dirname . 'classes/woocommerce-poor-guys-swiss-knife.php' );
require_once( $dirname . 'classes/woocommerce-poor-guys-swiss-knife-about.php' );
require_once( $dirname . 'wcpgsk-af.php' );
if ( !is_admin() ) :
add_action( 'plugins_loaded', 'wcpgsk_load_wcsession_helper' );
endif;
// Tu sobrescritura.
class My_WCPGSK_Main extends WCPGSK_Main {
public function wcpgsk_email_after_order_table($order) {
echo "O la la";
}
}
define( 'WCRGSK_DOMAIN', WCPGSK_DOMAIN ); // ¡Corrección de typo! (¿WooCommerce Rich Guys Swiss Knife?)
//cargar en nuestro global
$wcpgsk = new My_WCPGSK_Main( $FILE );
$wcpgsk->version = '2.2.4';
$wcpgsk->wcpgsk_hook_woocommerce_filters();
} elseif ( version_compare( WOOCOMMERCE_VERSION, "2.0" ) < 0 ) {
add_action( 'admin_notices', 'wcpgsk_woocommerce_version_message', 0 ) ;
return;
} else {
return;
}
}
Pero una forma aún mejor de sobrescribirlo es instalar runkit
(https://github.com/padraic/runkit) y luego simplemente reemplazarlo directamente en el "functions.php" de tu tema:
add_action( 'init', function () {
$code = <<<'EOD'
echo "O la la";
EOD;
runkit_method_redefine(
'WCPGSK_Main',
'wcpgsk_email_after_order_table',
'$order',
$code,
RUNKIT_ACC_PUBLIC
);
} );
(Eso es una broma, por cierto.)
