remove_action veya harici sınıflara sahip remove_filter?


59

Bir eklentinin yöntemlerini bir sınıf içinde kapsüllendirdiği ve ardından bu yöntemlerden birine karşı bir filtre veya eylem kaydettiği bir durumda, bu sınıfa erişemiyorsanız, eylemi veya filtreyi nasıl kaldırırsınız?

Örneğin, bunu yapan bir eklentiniz olduğunu varsayalım:

class MyClass {
    function __construct() {
       add_action( "plugins_loaded", array( $this, 'my_action' ) );
    }

    function my_action() {
       // do stuff...
    }
}

new MyClass();

Şimdi örneğe erişme imkânım olmadığına dikkat çekerek, sınıfı nasıl kaldırabilirim? Bu: remove_action( "plugins_loaded", array( MyClass, 'my_action' ) );doğru yaklaşım gibi görünmüyor - en azından benim durumumda işe yaramadı.


N / P. Aşağıda A sizin için çalışıyor mu?
kaiser

Yanıtlar:


16

Burada yapılacak en iyi şey statik bir sınıf kullanmaktır. Aşağıdaki kod öğretici olmalıdır:

class MyClass {
    function __construct() {
        add_action( 'wp_footer', array( $this, 'my_action' ) );
    }
    function my_action() {
        print '<h1>' . __class__ . ' - ' . __function__ . '</h1>';
    }
}
new MyClass();


class MyStaticClass {
    public static function init() {
        add_action( 'wp_footer', array( __class__, 'my_action' ) );
    }
    public static function my_action() {
        print '<h1>' . __class__ . ' - ' . __function__ . '</h1>';
    }
}
MyStaticClass::init();

function my_wp_footer() {
    print '<h1>my_wp_footer()</h1>';
}
add_action( 'wp_footer', 'my_wp_footer' );

function mfields_test_remove_actions() {
    remove_action( 'wp_footer', 'my_wp_footer' );
    remove_action( 'wp_footer', array( 'MyClass', 'my_action' ), 10 );
    remove_action( 'wp_footer', array( 'MyStaticClass', 'my_action' ), 10 );
}
add_action( 'wp_head', 'mfields_test_remove_actions' );

Bu kodu bir eklentiden çalıştırırsanız, StaticClass yönteminin yanı sıra işlevin wp_footer dosyasından kaldırılacağını fark etmelisiniz.


7
Alınan puan, ancak tüm sınıflar statik olmaya dönüştürülemez.
Geert

Bu cevabı kabul ettim çünkü soruyu en doğrudan cevaplıyor, ancak Otto'nun cevabı en iyi uygulama. Burada açıkça durağan olduğunu beyan etmeniz gerekmediğini düşünüyorum. Deneyim oldu (yanılsam da), sadece işlevi statik dizi ('Sınıfım', 'üye_şlemi' )miş gibi ele alabilir ve genellikle 'statik' anahtar sözcüğü olmadan çalışır.
Tom Auger

@TomAuger hayır yapamazsınız, SADECE statik bir sınıf olarak eklenirse, remove_actionişlevi kullanabilirsiniz , aksi halde işe yaramaz ... bu yüzden statik bir sınıf olmadığında işlemek için kendi işlevimi yazmak zorunda kaldım. Bu cevap sadece sorunuz kendi kodunuzla ilgiliyse en iyisi olacaktır, aksi halde başka birinin kod tabanından başka bir filtre / işlem kaldırmaya çalışacak ve statik olarak değiştiremezsiniz
sMyles

78

Ne zaman bir eklenti yaratsa new MyClass();, onu benzersiz bir isimli değişkene ataması gerekir. Bu şekilde, sınıfın örneğine erişilebilir.

Öyleyse $myclass = new MyClass();, o zaman bunu yapabilirdiniz:

global $myclass;
remove_action( 'wp_footer', array( $myclass, 'my_action' ) );

Bu çalışır çünkü eklentiler global isim alanına dahil edilmiştir, bu yüzden bir eklentinin ana gövdesindeki örtük değişken bildirimleri global değişkenlerdir.

Eklenti yeni sınıfın tanımlayıcısını bir yere kaydetmezse, teknik olarak bu bir hatadır. Nesneye Yönelik Programlamanın genel ilkelerinden biri, bir yerde bir değişken tarafından referans alınmayan nesnelerin, temizleme veya eleme işlemine tabi olmasıdır.

Şimdi, özellikle PHP bunu Java'nın yaptığı gibi yapmaz, çünkü PHP sorse yarısı silahlı bir OOP uygulamasıdır. Örnek değişkenler, içinde benzersiz nesne adlarına sahip dizelerdir, bir şeydir. Yalnızca değişken işlev adı etkileşiminin işleçle çalışması nedeniyle çalışırlar ->. Yani sadece yapmak new class()gerçekten mükemmel, sadece aptalca çalışabilir. :)

Yani, sonuçta, asla yapma new class();. $var = new class();$ 'I yapın ve diğer bitlerin referans göstermesi için bir şekilde erişilebilir duruma getirin.

Düzenleme: yıllar sonra

Birçok eklenti yaparken gördüğüm bir şey "Singleton" düzenine benzer bir şey kullanmak. Sınıfın tek örneğini elde etmek için bir getInstance () yöntemi oluştururlar. Bu muhtemelen gördüğüm en iyi çözüm. Örnek eklenti:

class ExamplePlugin
{
    protected static $instance = NULL;

    public static function getInstance() {
        NULL === self::$instance and self::$instance = new self;
        return self::$instance;
    }
}

GetInstance () ilk çağrıldığında sınıfı başlatır ve işaretçisini kaydeder. Bunu eylemlerde bulunmak için kullanabilirsiniz.

Bununla ilgili bir sorun, eğer böyle bir şey kullanırsanız, yapıcı içinde getInstance () yöntemini kullanamazsınız. Bunun nedeni yeni, $ örneğini ayarlamadan önce yapıcıyı çağırmasıdır, bu nedenle yapıcıdan getInstance () öğesini çağırmak sonsuz bir döngüye neden olur ve her şeyi keser.

Çözümlerden biri, yapıcıyı kullanmamak (veya en azından içinde getInstance () öğesini kullanmamak), ancak eylemlerinizi ve benzeri işlemlerinizi ayarlamak için açıkça sınıfta "init" işlevine sahip olmaktır. Bunun gibi, böyle:

public static function init() {
    add_action( 'wp_footer', array( ExamplePlugin::getInstance(), 'my_action' ) );
}

Bunun gibi bir şeyle, dosyanın sonunda, sınıf tanımlandıktan ve böyle devam ettikten sonra, eklentiyi başlatmak bu kadar basit olur:

ExamplePlugin::init();

Init eylemlerinizi eklemeye başlar ve bunu yaparken de sınıfı başlatan ve yalnızca birinin var olduğundan emin olan getInstance () işlevini çağırır. Bir init işleviniz yoksa, sınıfı başlangıçta başlatmak için bunu yaparsınız:

ExamplePlugin::getInstance();

Asıl soruya değinmek için, bu eylem kancasını dışarıdan (aka, başka bir eklentide) çıkarmak şu şekilde yapılabilir:

remove_action( 'wp_footer', array( ExamplePlugin::getInstance(), 'my_action' ) );

Bunu plugins_loadedeylem kancasına bağlı bir şeye koyun ve orijinal eklenti tarafından gerçekleştirilen eylemi geri alın.


3
+1 Tru dat. Bu açıkça en iyi yöntemdir. Ek kodumuzu bu şekilde yazmaya çalışmalıyız.
Tom Auger

3
+1 bu talimatları gerçekten bir singleton desen sınıfında bir filtreyi kaldırmak için bana yardımcı oldu.
Devin Walker

1, ama genellikle için kanca gerektiğini düşünüyorum wp_loadeddeğil, plugins_loadedçok erken denebilecek olan.
EML

4
Hayır, plugins_loadeddoğru yer olurdu. İşlem wp_loadedişlemden sonra initgerçekleşir, böylece eklentiniz herhangi bir işlem gerçekleştirirse init(ve çoğu yaparsa), eklentiyi başlatmak ve ondan önce ayarlamak istersiniz. plugins_loadedKanca bu inşaat aşaması için doğru yerdir.
Otto,


13

Sınıf nesnesine erişiminiz olmadığında filtreleri kaldırmak için oluşturduğum kapsamlı bir şekilde belgelenen işlev: (4.7+ dahil, WordPress 1.2+ ile çalışır):

https://gist.github.com/tripflex/c6518efc1753cf2392559866b4bd1a53

/**
 * Remove Class Filter Without Access to Class Object
 *
 * In order to use the core WordPress remove_filter() on a filter added with the callback
 * to a class, you either have to have access to that class object, or it has to be a call
 * to a static method.  This method allows you to remove filters with a callback to a class
 * you don't have access to.
 *
 * Works with WordPress 1.2+ (4.7+ support added 9-19-2016)
 * Updated 2-27-2017 to use internal WordPress removal for 4.7+ (to prevent PHP warnings output)
 *
 * @param string $tag         Filter to remove
 * @param string $class_name  Class name for the filter's callback
 * @param string $method_name Method name for the filter's callback
 * @param int    $priority    Priority of the filter (default 10)
 *
 * @return bool Whether the function is removed.
 */
function remove_class_filter( $tag, $class_name = '', $method_name = '', $priority = 10 ) {
    global $wp_filter;

    // Check that filter actually exists first
    if ( ! isset( $wp_filter[ $tag ] ) ) return FALSE;

    /**
     * If filter config is an object, means we're using WordPress 4.7+ and the config is no longer
     * a simple array, rather it is an object that implements the ArrayAccess interface.
     *
     * To be backwards compatible, we set $callbacks equal to the correct array as a reference (so $wp_filter is updated)
     *
     * @see https://make.wordpress.org/core/2016/09/08/wp_hook-next-generation-actions-and-filters/
     */
    if ( is_object( $wp_filter[ $tag ] ) && isset( $wp_filter[ $tag ]->callbacks ) ) {
        // Create $fob object from filter tag, to use below
        $fob = $wp_filter[ $tag ];
        $callbacks = &$wp_filter[ $tag ]->callbacks;
    } else {
        $callbacks = &$wp_filter[ $tag ];
    }

    // Exit if there aren't any callbacks for specified priority
    if ( ! isset( $callbacks[ $priority ] ) || empty( $callbacks[ $priority ] ) ) return FALSE;

    // Loop through each filter for the specified priority, looking for our class & method
    foreach( (array) $callbacks[ $priority ] as $filter_id => $filter ) {

        // Filter should always be an array - array( $this, 'method' ), if not goto next
        if ( ! isset( $filter[ 'function' ] ) || ! is_array( $filter[ 'function' ] ) ) continue;

        // If first value in array is not an object, it can't be a class
        if ( ! is_object( $filter[ 'function' ][ 0 ] ) ) continue;

        // Method doesn't match the one we're looking for, goto next
        if ( $filter[ 'function' ][ 1 ] !== $method_name ) continue;

        // Method matched, now let's check the Class
        if ( get_class( $filter[ 'function' ][ 0 ] ) === $class_name ) {

            // WordPress 4.7+ use core remove_filter() since we found the class object
            if( isset( $fob ) ){
                // Handles removing filter, reseting callback priority keys mid-iteration, etc.
                $fob->remove_filter( $tag, $filter['function'], $priority );

            } else {
                // Use legacy removal process (pre 4.7)
                unset( $callbacks[ $priority ][ $filter_id ] );
                // and if it was the only filter in that priority, unset that priority
                if ( empty( $callbacks[ $priority ] ) ) {
                    unset( $callbacks[ $priority ] );
                }
                // and if the only filter for that tag, set the tag to an empty array
                if ( empty( $callbacks ) ) {
                    $callbacks = array();
                }
                // Remove this filter from merged_filters, which specifies if filters have been sorted
                unset( $GLOBALS['merged_filters'][ $tag ] );
            }

            return TRUE;
        }
    }

    return FALSE;
}

/**
 * Remove Class Action Without Access to Class Object
 *
 * In order to use the core WordPress remove_action() on an action added with the callback
 * to a class, you either have to have access to that class object, or it has to be a call
 * to a static method.  This method allows you to remove actions with a callback to a class
 * you don't have access to.
 *
 * Works with WordPress 1.2+ (4.7+ support added 9-19-2016)
 *
 * @param string $tag         Action to remove
 * @param string $class_name  Class name for the action's callback
 * @param string $method_name Method name for the action's callback
 * @param int    $priority    Priority of the action (default 10)
 *
 * @return bool               Whether the function is removed.
 */
function remove_class_action( $tag, $class_name = '', $method_name = '', $priority = 10 ) {
    remove_class_filter( $tag, $class_name, $method_name, $priority );
}

2
Soru - Bunu 4.7’de test ettiniz mi? Geri aramaların yepyeni filtrelere kaydedilme biçiminde bazı değişiklikler oldu. Kodunuza
Tom Auger

evet, oldukça emin bu 4.7 kıracak
gmazzap

Ahh! Hayır, yapmadım ama teşekkür ederim, bu konuyu inceleyip güncelleyeceğim, böylece uyumlu olacak (gerekirse)
sMyles 17:16 '

1
@ TomAuger kafaları için teşekkürler! Fonksiyonu güncelledim, WordPress 4.7+ üzerinde çalışıp çalışmadığını test ettim (geriye dönük uyumluluk devam ediyor)
sMyles 20:16 '

1
Sadece çekirdek iç kaldırma yöntemini kullanmak için bu güncelleme (orta yinelemeyi işlemek ve php uyarılarını önlemek için)
sMyles

2

Çözümler eski gibi görünüyor, kendime yazmak zorunda kaldım ...

function remove_class_action ($action,$class,$method) {
    global $wp_filter ;
    if (isset($wp_filter[$action])) {
        $len = strlen($method) ;
        foreach ($wp_filter[$action] as $pri => $actions) {
            foreach ($actions as $name => $def) {
                if (substr($name,-$len) == $method) {
                    if (is_array($def['function'])) {
                        if (get_class($def['function'][0]) == $class) {
                            if (is_object($wp_filter[$action]) && isset($wp_filter[$action]->callbacks)) {
                                unset($wp_filter[$action]->callbacks[$pri][$name]) ;
                            } else {
                                unset($wp_filter[$action][$pri][$name]) ;
                            }
                        }
                    }
                }
            }
        }
    }
}

0

@Digerkam cevabına dayanan bu fonksiyon. $def['function'][0]String ise karşılaştırmalı karşılaştırma eklendi ve sonunda benim için işe yaradı.

Ayrıca kullanmak $wp_filter[$tag]->remove_filter()daha istikrarlı hale getirmelidir.

function remove_class_action($tag, $class = '', $method, $priority = null) : bool {
    global $wp_filter;
    if (isset($wp_filter[$tag])) {
        $len = strlen($method);

        foreach($wp_filter[$tag] as $_priority => $actions) {

            if ($actions) {
                foreach($actions as $function_key => $data) {

                    if ($data) {
                        if (substr($function_key, -$len) == $method) {

                            if ($class !== '') {
                                $_class = '';
                                if (is_string($data['function'][0])) {
                                    $_class = $data['function'][0];
                                }
                                elseif (is_object($data['function'][0])) {
                                    $_class = get_class($data['function'][0]);
                                }
                                else {
                                    return false;
                                }

                                if ($_class !== '' && $_class == $class) {
                                    if (is_numeric($priority)) {
                                        if ($_priority == $priority) {
                                            //if (isset( $wp_filter->callbacks[$_priority][$function_key])) {}
                                            return $wp_filter[$tag]->remove_filter($tag, $function_key, $_priority);
                                        }
                                    }
                                    else {
                                        return $wp_filter[$tag]->remove_filter($tag, $function_key, $_priority);
                                    }
                                }
                            }
                            else {
                                if (is_numeric($priority)) {
                                    if ($_priority == $priority) {
                                        return $wp_filter[$tag]->remove_filter($tag, $function_key, $_priority);
                                    }
                                }
                                else {
                                    return $wp_filter[$tag]->remove_filter($tag, $function_key, $_priority);
                                }
                            }

                        }
                    }
                }
            }
        }

    }

    return false;
}

Örnek kullanım:

Tam eşleşme

add_action('plugins_loaded', function() {
    remove_class_action('plugins_loaded', 'MyClass', 'my_action', 0);
});

Herhangi bir öncelik

add_action('plugins_loaded', function() {
    remove_class_action('plugins_loaded', 'MyClass', 'my_action');
});

Herhangi bir sınıf ve herhangi bir öncelik

add_action('plugins_loaded', function() {
    remove_class_action('plugins_loaded', '', 'my_action');
});

0

Bu genel bir cevap değil, Avada temasına ve diğer insanların yararlı bulabileceğini düşündüğüm WooCommerce'a özgü :

function remove_woo_commerce_hooks() {
    global $avada_woocommerce;
    remove_action( 'woocommerce_single_product_summary', array( $avada_woocommerce, 'add_product_border' ), 19 );
}
add_action( 'after_setup_theme', 'remove_woo_commerce_hooks' );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.