add_action referencia uma classe

38

É possível referenciar uma classe em vez de uma função em 'add_action'? Não consigo entender. Aqui está apenas um exemplo básico da função em questão.

add_action( 'admin_init', 'MyClass' );
class MyClass {
     function __construct() {
          .. This is where stuff gets done ..
     }
}

Então, sim, isso não funciona. Eu também tentei:

$var = new MyClass();
add_action( 'admin_init', array( &$var ) );

E:

$var = new MyClass();
add_action( 'admin_init', array( &$var, '__construct' ) );

E também:

add_action( 'admin_init', 'MyClass::__construct' );

Existe alguma maneira que eu possa fazer isso sem ter que criar uma função separada que carrega a classe? Eu gostaria de poder apenas executar o construtor de classes através do add_action. É tudo o que precisa ser carregado para fazer a bola rolar.

Matthew Ruddy
fonte

Respostas:

69

Não, você não pode 'inicializar' ou instanciar a classe através de um gancho, não diretamente. Sempre é necessário algum código adicional (e não é uma coisa desejável poder fazer isso, pois você está abrindo uma lata de worms para si mesmo.

Aqui está uma maneira melhor de fazer isso:

class MyClass {
     function __construct() {
          add_action( 'admin_init',array( $this, 'getStuffDone' ) );
     }
     function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
$var = new MyClass();

Obviamente, pode-se criar uma classe de interface para simplificá-la ainda mais no caso geral:

class IGetStuffDone {
    function IGetStuffDone(){
        add_action( 'admin_init',array( $this, 'getStuffDone' ) );
    }
    public abstract function getStuffDone();
}

Observe que, como interface, você não pode criar um objeto desse tipo diretamente, mas pode criar uma subclasse, permitindo dizer:

class CDoingThings extends IGetStuffDone {
    function getStuffDone(){
        // doing things
    }
}
$var = new CDoingThings();

O que adicionaria automaticamente todos os ganchos, basta definir o que exatamente está sendo feito em uma subclasse e criá-lo!

Em construtores

Eu não adicionaria um construtor como uma função de gancho, é uma prática ruim e pode levar a muitos eventos incomuns. Também na maioria dos idiomas, um construtor retorna o objeto que está sendo instanciado; portanto, se seu gancho precisar retornar algo como um filtro, ele não retornará a variável filtrada como você deseja, mas retornará o objeto de classe.

Chamar um construtor ou destruidor é uma prática de programação muito, muito, muito ruim, independentemente do idioma em que você esteja, e nunca deve ser feito.

Os construtores também devem construir objetos, para inicializá-los prontos para uso, não para o trabalho real. O trabalho a ser realizado pelo objeto deve estar em uma função separada.

Métodos de classe estática e sem a necessidade de instanciar / inicializar

Se o seu método de classe é estático, você pode passar o nome da classe entre aspas, e não $thiscomo mostrado abaixo:

class MyClass {
     public static function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
add_action( 'admin_init', array('MyClass','getStuffDone' ) );

Encerramentos e PHP 5.3

Infelizmente você não pode evitar a linha que cria a nova classe. A única outra solução para ignorá-lo envolveria o código da placa da caldeira que ainda possui essa linha e exigiria o PHP 5.3 ou superior, por exemplo:

add_action('admin_init',function(){
    $var = new MyClass();
    $var->getStuffDone();
});

Nesse ponto, você também pode pular a classe e apenas usar uma função:

add_action('admin_init',function(){
    // do stuff
});

Mas lembre-se de que você introduziu o espectro de funções anônimas. Não há como remover a ação acima usando remove_action, e isso pode e causa grandes problemas para os desenvolvedores que precisam trabalhar com o código de outras pessoas.

Em Ampersands

Você pode ver ações usadas assim:

array( &$this, 'getStuffDone' );

Isso é ruim . &foi adicionado novamente no PHP 4 quando objetos eram passados ​​como valores, não como referências. O PHP 4 tem mais de uma década e não é suportado pelo WordPress há muito tempo.

Não há razão para usar &thisao adicionar ganchos e filtros, e remover a referência não causará problemas e pode até melhorar a compatibilidade com versões futuras do PHP

Use isto:

array( $this, 'getStuffDone' );
Tom J Nowell
fonte
Está bem. Obrigado por tudo isso; realmente aprendi um pouco. Agora só estou me sentindo confortável no PHP baseado em classe. Foi isso que fiz e funciona, mas você poderia me dizer se é uma má prática / incorreta de alguma forma? Eu iniciei a classe dentro de uma função estática, dentro da própria classe. Em seguida, referenciou a função estática no add_action. Veja este link: pastebin.com/0idyPwwY
Matthew Ruddy
sim, você poderia fazer dessa maneira, embora eu evite usar $classcomo seu nome de variável, essas palavras tendem a ser reservadas. Eu acho que você está saindo do seu caminho para evitar dizer algo semelhante $x = new Y();no escopo global e está adicionando complexidade onde nada é necessário. Sua tentativa de reduzir a quantidade de código gravado envolveu escrever mais código!
Tom J Nowell
Eu indicaria que em todos os casos acima, seria melhor usar uma função do que uma classe, pois essa classe será descartada de qualquer maneira e serve ao mesmo propósito. É um desperdício de recursos. Lembre-se, há um custo para criar e destruir um objeto, você deseja poucos objetos e sua vida útil longa
Tom J Nowell
Bom ponto. Mudei o jeito que eu estava olhando para isso. Acho que vou chamá-lo no escopo global, evitando o código extra.
Matthew Ruddy
11
Gostaria de acrescentar que, se acontecer de você ter colocado a sua classe em um namespace, você terá de acrescentar que add_action demais ou () / add_filter () não vai encontrá-lo - assim:add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );
jschrab
10

Classe de exemplo

Notas:

  • Inicie a aula apenas uma vez
    • Ligue para a prioridade 0, para que você possa usar o mesmo gancho com a prioridade padrão posteriormente
    • Embrulhe em um ! class_existspara evitar chamá-lo duas vezes e coloque o chamador init dentro
  • Tornar a initfunção e a classe varstatic
  • Chame o construtor de dentro do seu init, quando você chamar a classe new self.

Aqui está um exemplo

if ( ! class_exists( 'WPSESampleClass' ) )
{
    // Init the class on priority 0 to avoid adding priority inside the class as default = 10
    add_action( 'init', array ( 'WPSESampleClass', 'init' ), 0 );

class WPSESampleClass
{
    /**
     * The Class Object
     */
    static private $class = null;

    public static function init()
    {
        if ( null === self::$class ) 
            self :: $class = new self;

        return self :: $class;
    }

    public function __construct()
    {
        // do stuff like add action calls:
        add_action( 'init', array( $this, 'cb_fn_name' ) );
    }

    public function cb_fn_name()
    {
        // do stuff 
    }
} // END Class WPSESampleClass

} // endif;

Php 5+

Por favor , deixe de &fora. Já estamos além do php4. :)

kaiser
fonte
2
if (!class_exists("AllInOneWoo")){
    class AllInOneWoo {
        function __construct(){
            add_action('admin_menu', array($this, 'all_in_one_woo') );
        }
        function all_in_one_woo(){
            $page_title = 'All In One Woo';
            $menu_title = 'All In One Woo';
            $capability = 'manage_options';
            $menu_slug  = 'all-in-one-woo-menu';
            $function   = array($this, 'all_in_one_woo_menu');
            $icon_url   = 'dashicons-media-code';
            $position   = 59;

            add_menu_page($page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position);
        }
        function all_in_one_woo_menu(){?>
            <div class="wrap">
                <h1><?php _e('All In One Woo', 'all_in_one_woo'); ?></h1>
            </div>
        <?php }
    }// end class
}// end if

if (class_exists("AllInOneWoo")){       
    $all_in_one_woo = new AllInOneWoo();
}
Zakir Sajib
fonte
Por favor edite sua resposta , e adicionar uma explicação: por que isso poderia resolver o problema?
Fuxia
0

Você pode acionar eventos em sua classe sem a necessidade de carregá-lo inicialmente . Isso é útil se você não deseja carregar a classe completa antecipadamente, mas precisa acessar os filtros e ações do WordPress.

Aqui está um exemplo muito simples

<?php
class MyClass
{
    public static function init()
    {
        add_filter( 'the_content', array('MyClass', 'myMethod') );
    }

    public static function myMethod($content)
    {
        $content = $content . 'Working without loading the object';
    }
}

MyClass::init();

Esta é uma versão muito simplificada da resposta de Kaiser, mas mostra em termos simples o comportamento. Pode ser útil para quem olha esse estilo pela primeira vez.

Outros métodos ainda podem iniciar o objeto, se necessário. Pessoalmente, estou usando esse método para permitir que partes opcionais do meu plug-in enfileirem scripts, mas apenas acionando o objeto em uma solicitação AJAX.

John Reid
fonte
0

Isso funciona para mim:

class foo
{
    public static function bar()
    {
        echo 'it works!';
    }
}

add_action('foo_bar', array('foo', 'bar'));
Jonathan
fonte
0

De um modo geral, você não adicionaria uma classe inteira a um gancho. Os add_action()/ add_filter()hooks esperam funções de retorno de chamada , que podem ser referenciadas de dentro de uma classe .

Digamos que você tenha uma init()função dentro da sua classe, que deseja conectar ao gancho do WordPress init.

Faça sua add_action()ligação dentro da sua classe e identifique o retorno de chamada da seguinte forma:

add_action( 'init', array( $this, 'init' ) );

(Observação: estou supondo que sua classe esteja com espaço para nome adequado; caso contrário, certifique-se de colocar um espaço para nome em suas funções de retorno de chamada.)

Chip Bennett
fonte
E se a classe atual ainda não tiver sido iniciada? Eu estava tentando usar add_action para realmente iniciar, então não tenho que adicionar $ var = new MyClass (); de antemão em outro lugar.
Matthew Ruddy
Você não pode simplesmente escrever um retorno de chamada para instanciar sua turma init(ou onde você precisar)?
Chip Bennett
0

Você deve conseguir passar o nome da classe em vez do objeto instanciado:

add_action( 'init', array( 'MyClass', '__construct' ) );

(Em teoria, sua outra solução também deve funcionar

$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );

Não sei direito por que não. Talvez se você não ligar por referência?)

Boone Gorges
fonte
O primeiro não funciona. Apenas faz a página ficar em branco. O segundo funciona, mas apenas porque a classe foi iniciada na primeira linha. Isso meio que anula o propósito de adicionar a ação, porque a classe já foi iniciada. Mas não faz o que estou tentando fazer, que é iniciar a classe através da ação. No código em que estou trabalhando, a ação não é 'admin_init', mas uma ação personalizada dentro de outra classe. Eu não quero que a função 'MyClass' seja iniciada se a ação na outra classe não estiver lá. Desculpe se estou perdendo alguma coisa; aprendendo como eu ir
Matthew Ruddy
Sim, eu estava errado. Isso só funciona se você estiver chamando um initmétodo estático . Veja wordpress.stackexchange.com/a/48093/14052
Boone Gorges