Risposte:
Questa è un'ottima domanda. Passa al cuore oscuro dell'API del plug-in e alle migliori pratiche di programmazione.
Per la seguente risposta ho creato un semplice plugin per illustrare il problema con un codice di facile lettura.
<?php # -*- coding: utf-8 -*-
/* Plugin Name: Anonymous OOP Action */
if ( ! class_exists( 'Anonymous_Object' ) )
{
/**
* Add some actions with randomized global identifiers.
*/
class Anonymous_Object
{
public function __construct()
{
add_action( 'wp_footer', array ( $this, 'print_message_1' ), 5 );
add_action( 'wp_footer', array ( $this, 'print_message_2' ), 5 );
add_action( 'wp_footer', array ( $this, 'print_message_3' ), 12 );
}
public function print_message_1()
{
print '<p>Kill me!</p>';
}
public function print_message_2()
{
print '<p>Me too!</p>';
}
public function print_message_3()
{
print '<p>Aaaand me!</p>';
}
}
// Good luck finding me!
new Anonymous_Object;
}
Ora vediamo questo:
WordPress ha bisogno di un nome per il filtro. Non ne abbiamo fornito uno, quindi WordPress chiama _wp_filter_build_unique_id()
e ne crea uno. Questo nome non è prevedibile perché utilizza spl_object_hash()
.
Se eseguiamo un var_export()
on $GLOBALS['wp_filter'][ 'wp_footer' ]
otteniamo qualcosa del genere ora:
array (
5 =>
array (
'000000002296220e0000000013735e2bprint_message_1' =>
array (
'function' =>
array (
0 =>
Anonymous_Object::__set_state(array(
)),
1 => 'print_message_1',
),
'accepted_args' => 1,
),
'000000002296220e0000000013735e2bprint_message_2' =>
array (
'function' =>
array (
0 =>
Anonymous_Object::__set_state(array(
)),
1 => 'print_message_2',
),
'accepted_args' => 1,
),
),
12 =>
array (
'000000002296220e0000000013735e2bprint_message_3' =>
array (
'function' =>
array (
0 =>
Anonymous_Object::__set_state(array(
)),
1 => 'print_message_3',
),
'accepted_args' => 1,
),
),
20 =>
array (
'wp_print_footer_scripts' =>
array (
'function' => 'wp_print_footer_scripts',
'accepted_args' => 1,
),
),
1000 =>
array (
'wp_admin_bar_render' =>
array (
'function' => 'wp_admin_bar_render',
'accepted_args' => 1,
),
),
)
Per trovare e rimuovere la nostra azione malvagia dobbiamo passare attraverso i filtri associati per l'hook (un'azione è solo un filtro molto semplice), verificare se si tratta di un array e se l'oggetto è un'istanza della classe. Quindi prendiamo la priorità e rimuoviamo il filtro, senza mai vedere l'identificatore reale .
Va bene, mettiamolo in una funzione:
if ( ! function_exists( 'remove_anonymous_object_filter' ) )
{
/**
* Remove an anonymous object filter.
*
* @param string $tag Hook name.
* @param string $class Class name
* @param string $method Method name
* @return void
*/
function remove_anonymous_object_filter( $tag, $class, $method )
{
$filters = $GLOBALS['wp_filter'][ $tag ];
if ( empty ( $filters ) )
{
return;
}
foreach ( $filters as $priority => $filter )
{
foreach ( $filter as $identifier => $function )
{
if ( is_array( $function)
and is_a( $function['function'][0], $class )
and $method === $function['function'][1]
)
{
remove_filter(
$tag,
array ( $function['function'][0], $method ),
$priority
);
}
}
}
}
}
Quando chiamiamo questa funzione? Non c'è modo di sapere con certezza quando viene creato l'oggetto originale. Forse a volte prima 'plugins_loaded'
? Forse più tardi?
Usiamo lo stesso hook a cui è associato l'oggetto e saltiamo molto presto con priorità 0
. Questo è l'unico modo per essere veramente sicuri. Ecco come rimuoveremo il metodo print_message_3()
:
add_action( 'wp_footer', 'kill_anonymous_example', 0 );
function kill_anonymous_example()
{
remove_anonymous_object_filter(
'wp_footer',
'Anonymous_Object',
'print_message_3'
);
}
Risultato:
E ciò dovrebbe rimuovere l'azione dalla tua domanda (non testata):
add_action( 'comments_array', 'kill_FbComments', 0 );
function kill_FbComments()
{
remove_anonymous_object_filter(
'comments_array',
'SEOFacebookComments',
'FbComments'
);
}
'plugins_loaded'
. Non solo quando il tuo plugin viene chiamato da WordPress.plugins_loaded
viene chiamato, il che è esattamente ciò che plugins_loaded
serve. Naturalmente, l'istanza di classe deve ancora essere accessibile, possibilmente tramite un modello singleton.
remove_action()
Non sono sicuro ma puoi provare a usare un singleton.
È necessario memorizzare il riferimento all'oggetto in una proprietà statica della classe e quindi restituire quella variabile statica da un metodo statico. Qualcosa come questo:
class MyClass{
private static $ref;
function MyClass(){
$ref = &$this;
}
public static function getReference(){
return self::$ref;
}
}
Finché conosci l'oggetto (e usi PHP 5.2 o versioni successive - l'attuale versione stabile di PHP è 5.5, 5.4 è ancora supportata, 5.3 è fine vita), puoi semplicemente rimuoverla con il remove_filter()
metodo. Tutto quello che devi ricordare è l'oggetto, il nome metodo e la priorità (se utilizzato):
remove_filter('comment_array', [$this, 'FbComments']);
Tuttavia fai un piccolo errore nel tuo codice. Non $this
aggiungere il prefisso con la e commerciale &
, necessaria in PHP 4 (!) Ed è in ritardo da molto tempo. Questo può rendere problematica la gestione dei tuoi ganci, quindi lascialo fuori dai piedi:
add_filter('comments_array', [$this, 'FbComments]));
E questo è tutto.
$this
dall'esterno (un altro plugin / tema).
&
dal tuo&$this
, è una cosa di PHP 4