İsimsiz bir nesne olan bir filtre nasıl kaldırılır?


62

Dosyamda functions.phpaşağıdaki filtreyi kaldırmak istiyorum, ancak sınıftan beri nasıl yapılacağından emin değilim. Neye benzemeli remove_filter()?

add_filter('comments_array',array( &$this, 'FbComments' ));

Buradaki 88 hatta .


Sen kaldırmalısınız &hesabınızla ilgili &$thisbir PHP 4 şey,
Tom J Nowell

Yanıtlar:


79

Bu çok iyi bir soru. Eklenti API'sinin karanlık kalbine ve en iyi programlama uygulamalarına kadar iner.

Aşağıdaki cevap için, kolay okunan kod ile sorunu göstermek için basit bir eklenti oluşturdum.

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

Şimdi şunu görüyoruz:

görüntü tanımını buraya girin

WordPress , filtre için bir ada ihtiyaç duyar . Biz bir tane sağlamadık, bu yüzden WordPress çağırıyor _wp_filter_build_unique_id()ve bir tane yaratıyor. Kullandığı için bu ad öngörülebilir değildir spl_object_hash().

Eğer var_export()devam $GLOBALS['wp_filter'][ 'wp_footer' ]edersek şuan böyle bir şey alırız:

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,
    ),
  ),
)

Kötü eylemimizi bulmak ve kaldırmak için, kancaya ilişkin filtrelerden geçmemiz gerekir (bir eylem sadece çok basit bir filtredir), bunun bir dizi olup olmadığını ve nesnenin sınıfın bir örneği olup olmadığını kontrol edin. Ardından önceliği alır ve filtreyi kaldırırız , gerçek tanıtıcıyı görmeden .

Tamam, hadi bunu bir işleve koyalım:

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

Bu işlevi ne zaman çağırırız? Özgün nesnenin ne zaman oluşturulduğundan emin olmanın hiçbir yolu yoktur. Belki bazen daha önce 'plugins_loaded'? Belki sonra?

Nesnenin ilişkilendirildiği aynı kancayı kullanıyoruz ve önceliğe çok erken atlıyoruz 0. Gerçekten emin olmanın tek yolu bu. Yöntemi nasıl kaldıracağımız şöyle 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'
    );
}

Sonuç:

görüntü tanımını buraya girin

Ve bu eylemi sorunuzdan kaldırmalıdır (test edilmedi):

add_action( 'comments_array', 'kill_FbComments', 0 );

function kill_FbComments()
{
    remove_anonymous_object_filter(
        'comments_array',
        'SEOFacebookComments',
        'FbComments'
    );
}

Sonuç

  • Her zaman tahmin edilebilir kod yaz. Filtreleriniz ve işlemleriniz için okunabilir adlar ayarlayın. Herhangi bir kancayı çıkarmayı kolaylaştırın.
  • Nesnenizi, öngörülebilir bir eylemde, örneğin üzerinde oluşturun 'plugins_loaded'. Sadece eklentiniz WordPress tarafından çağrıldığında değil.


@MikeSchinkel İlgili fikir , bugüne kadar pratikte denemedim.
fuxia

İlginç. Cevabınızı çok iyi buluyorum, ancak son kararınız oldukça zayıf. Benim düşünceme göre, sınıf örnekleri genel olarak, WordPress eklentisini yükler yüklenmez başlatılmalıdır. Ardından, sınıf örneğinin yapıcısı herhangi bir gerçek eylem gerçekleştirmemelidir, yalnızca eylemler ve filtreler ekleyin. Bu şekilde, sınıf örneğinizden eylemleri ve filtreleri kaldırmak isteyen eklentiler plugins_loadedçağrıldıklarında gerçekten eklendiklerinden emin olabilirler , bu tam olarak ne plugins_loadediçindir. Tabii ki, sınıf örneğinin hâlâ erişilebilir olması gerekiyor, muhtemelen tekil desen.
engelen

@engelen Bu eski bir cevap. Bugünlerde geri aramaları kaldırmak için bir eylem önerebilirim. Ama bir Singleton değil, bu birçok nedenden dolayı bir anti-patern .
fuxia

Bu cevap aynı zamanda eylemleri kaldırmak için de işe remove_action()
yarar

0

Emin değilim ama bir singleton kullanmayı deneyebilirsiniz.
Nesne başvurusunu sınıfınızın statik özelliğinde saklamanız ve ardından statik değişkeni statik bir yöntemle döndürmeniz gerekir. Bunun gibi bir şey:

class MyClass{
    private static $ref;
    function MyClass(){
        $ref = &$this;
    }
    public static function getReference(){
        return self::$ref;
    }
}

0

Nesneyi bildiğiniz sürece (ve PHP 5.2 veya daha üstünü kullanıyorsanız - mevcut kararlı PHP sürüm 5.5, 5.4 hala desteklenir, 5.3 kullanım ömrü sona erer), yalnızca yöntemle kaldırabilirsiniz remove_filter(). Hatırlamanız gereken tek şey nesne, yöntem adı ve önceliğidir (kullanılıyorsa):

remove_filter('comment_array', [$this, 'FbComments']);

Ancak kodunuzda küçük bir hata yaparsınız. PHP ve (!) 'De gerekli olan ve uzun süre gecikmiş $thisolan ampersand ile ön ek yapmayın &. Bu, kancalarınızla başa çıkmanızı sorunlu hale getirebilir, bu yüzden sadece yoldan bırakın

add_filter('comments_array', [$this, 'FbComments]));

Ve bu kadar.


1
$thisDışarıdan erişiminiz yok (başka bir eklenti / tema).
fuxia
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.