Eu criei um plugin e, claro, sendo eu, eu queria ter uma boa abordagem de OO. Agora, o que eu tenho feito é criar esta classe e, logo abaixo, criar uma instância dessa classe:
class ClassName {
public function __construct(){
}
}
$class_instance = new ClassName();
Estou assumindo que existe uma maneira mais WP de iniciar essa classe, e então me deparei com pessoas dizendo que elas preferem ter uma init()
função do que uma __construct()
. Da mesma forma, encontrei algumas pessoas usando o seguinte gancho:
class ClassName {
public function init(){
}
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );
Qual é geralmente considerada a melhor maneira de criar uma instância de classe WP em carga e tê-la como uma variável acessível globalmente?
NOTA: Como um ponto interessante, notei que embora register_activation_hook()
possa ser chamado de dentro do __construct
, ele não pode ser chamado de dentro do init()
usando o segundo exemplo. Talvez alguém possa me esclarecer sobre esse ponto.
Edit: Obrigado por todas as respostas, há claramente um bom debate sobre como lidar com a inicialização dentro da própria classe, mas acho que geralmente há um consenso bastante bom que add_action( 'plugins_loaded', ...);
é a melhor maneira de realmente começar ...
Edit: Apenas para confundir as coisas, eu também já vi isso usado (embora eu não usasse esse método porque transformar uma classe OO agradavelmente em uma função parece derrotar o objetivo):
// Start up this plugin
add_action( 'init', 'ClassName' );
function ClassName() {
global $class_name;
$class_name = new ClassName();
}
fonte
Respostas:
Boa pergunta, existem várias abordagens e isso depende do que você deseja alcançar.
Eu costumo fazer;
Um exemplo mais aprofundado e aprofundado, que surgiu como resultado de algumas discussões recentes sobre esse mesmo tópico dentro da sala de bate-papo, pode ser visto nesta síntese pelo membro da WPSE toscho .
A abordagem de construtor vazio.
Aqui está um trecho de vantagens / desvantagens retiradas da essência acima, que exemplifica a abordagem do construtor vazio na íntegra.
A desvantagem, na minha opinião, é fraca, e é por isso que teria que ser minha abordagem preferida, mas não a única que eu uso. De fato, vários outros pesos pesados irão, sem dúvida, abordar esse tópico com sua opinião sobre o assunto em breve, porque há algumas boas opiniões em torno desse tópico que devem ser expressas.
note: Preciso encontrar o exemplo essencial da toscho que realizou 3 ou 4 comparações de como instanciar uma classe em um plug-in que analisava os prós e os contras de cada um, que o link acima era a maneira preferida de fazê-lo, mas os outros exemplos fornecem um bom contraste para este tópico. Espero que toscho ainda tenha isso registrado.Nota: A WPSE Responde a este tópico com exemplos e comparações relevantes. Também a melhor solução, por exemplo, uma aula no WordPress.
fonte
update.php
arquivo principal e não faz parte das ações padrão habituais que devem ser consideradas quando se refere à sequência de eventos que são acionados durante a inicialização e, por esse motivo, prefiro para usar os ganchos que se aplicam, neste casoplugins_loaded
. É a isso que frequentemente me refiro como um instantâneo rápido do que acontece quando o Action Reference . Minha explicação não está completa na sua totalidade.register_activation_hook()
precisará chamar essa função antes que aplugins_loaded
ação seja acionada.Chegando aqui exatamente 2 anos depois que a pergunta original foi feita, há algumas coisas que quero destacar. (Não me peça para apontar muitas coisas , sempre).
Gancho adequado
Para instanciar uma classe de plug-in, o gancho adequado deve ser usado. Não existe uma regra geral para isso, pois depende do que a classe faz.
Usar um gancho muito cedo, como não
"plugins_loaded"
costuma fazer sentido, porque um gancho desse tipo é acionado para solicitações de administrador, frontend e AJAX, mas muitas vezes um gancho posterior é muito melhor porque permite instanciar classes de plug-in apenas quando necessário.Por exemplo, uma classe que faz coisas para modelos pode ser instanciada
"template_redirect"
.De um modo geral, é muito raro que uma classe precise ser instanciada antes de
"wp_loaded"
ser demitida.Classe sem Deus
A maioria de todas as classes usadas como exemplos em respostas mais antigas usa uma classe chamada like
"Prefix_Example_Plugin"
ou"My_Plugin"
... Isso indica que provavelmente existe uma classe principal para o plugin.Bem, a menos que um plug-in seja feito por uma única classe (nesse caso, nomeá-lo após o nome do plug-in é absolutamente razoável), crie uma classe que gerencie todo o plug-in (por exemplo, adicionando todos os ganchos de que um plug-in precisa ou instanciando todas as outras classes de plug-in ) pode ser considerada uma má prática, como um exemplo de um objeto divino .
Na programação orientada a objetos, o código deve tender a ser SOLID, onde "S" significa "princípio de responsabilidade única" .
Isso significa que toda classe deve fazer uma única coisa. No desenvolvimento de plug-ins do WordPress, significa que os desenvolvedores devem evitar usar um único gancho para instanciar uma classe principal de plug - ins, mas diferentes ganchos devem ser usados para instanciar classes diferentes, de acordo com a responsabilidade da classe.
Evite ganchos no construtor
Esse argumento foi introduzido em outras respostas aqui, no entanto, quero comentar esse conceito e vincular essa outra resposta, onde foi amplamente explicado no âmbito do teste de unidade.
Quase 2015: o PHP 5.2 é para zumbis
Desde 14 de agosto de 2014, o PHP 5.3 atingiu seu fim de vida . Definitivamente está morto. O PHP 5.4 será suportado por todo o ano de 2015, ou seja, por mais um ano no momento em que estou escrevendo.
No entanto, o WordPress ainda suporta PHP 5.2, mas ninguém deve escrever uma única linha de código que suporte essa versão, especialmente se o código for OOP.
Existem diferentes razões:
Se você não quiser usar o código PHP 5.4+, use pelo menos 5.3+
Exemplo
Neste ponto, é hora de revisar as respostas mais antigas com base no que eu disse até aqui.
Uma vez que não precisamos mais nos preocupar com o 5.2, podemos e devemos usar namespaces.
Para melhor explicar o princípio da responsabilidade única, meu exemplo usará 3 classes, uma que faz algo no front-end, uma no back-end e uma terceira usada nos dois casos.
Classe de administrador:
Classe de front-end:
Interface de ferramentas:
E uma classe Tools, usada pelos outros dois:
Tendo essas aulas, posso instancia-las usando ganchos adequados. Algo como:
Inversão de Dependência e Injeção de Dependência
No exemplo acima, usei namespaces e funções anônimas para instanciar classes diferentes em ganchos diferentes, colocando em prática o que eu disse acima.
Observe como os namespaces permitem criar classes nomeadas sem nenhum prefixo.
Eu apliquei outro conceito que foi indiretamente mencionado acima: Injeção de Dependência , é um método para aplicar o Princípio de Inversão de Dependência , o "D" na sigla SOLID.
A
Tools
classe é "injetada" nas outras duas classes quando são instanciadas, portanto é possível separar a responsabilidade.Além disso,
AdminStuff
eFrontStuff
classes usam dicas de tipo para declarar que precisam de uma classe que implementaToolsInterface
.Dessa maneira, nós mesmos ou usuários que usamos nosso código podemos usar implementações diferentes da mesma interface, tornando nosso código não acoplado a uma classe concreta, mas a uma abstração: é exatamente disso que se trata o Princípio da Inversão de Dependência.
No entanto, o exemplo acima pode ser melhorado. Vamos ver como.
Carregador automático
Uma boa maneira de escrever um código OOP mais legível é não misturar a definição de tipos (interfaces, classes) com outro código e colocar todos os tipos em seu próprio arquivo.
Esta regra também é um dos padrões de codificação PSR-1 1 .
No entanto, para fazer isso, antes de poder usar uma classe, é necessário exigir o arquivo que a contém.
Isso pode ser esmagador, mas o PHP fornece funções utilitárias para carregar automaticamente uma classe quando necessário, usando um retorno de chamada que carrega um arquivo com base em seu nome.
Usando espaços para nome, fica muito fácil, porque agora é possível combinar a estrutura da pasta com a estrutura do espaço para nome.
Isso não é apenas possível, mas também é outro padrão PSR (ou melhor, 2: PSR-0 agora obsoleto e PSR-4 ).
Seguindo esses padrões, é possível fazer uso de diferentes ferramentas que lidam com o carregamento automático, sem a necessidade de codificar um carregador automático personalizado.
Devo dizer que os padrões de codificação do WordPress têm regras diferentes para nomear arquivos.
Portanto, ao escrever o código para o núcleo do WordPress, os desenvolvedores precisam seguir as regras do WP, mas ao escrever o código personalizado, é uma opção do desenvolvedor, mas o uso do padrão PSR é mais fácil de usar as ferramentas já escritas 2 .
Padrões de acesso global, registro e localizador de serviço.
Um dos maiores problemas ao instanciar classes de plug-in no WordPress é como acessá-las em várias partes do código.
O próprio WordPress usa a abordagem global : as variáveis são salvas no escopo global, tornando-as acessíveis em qualquer lugar. Todo desenvolvedor de WP digita a palavra
global
milhares de vezes em sua carreira.Essa também é a abordagem que usei para o exemplo acima, mas é ruim .
Essa resposta já é longa demais para permitir que eu explique melhor o porquê, mas ler os primeiros resultados no SERP para "variáveis globais más" é um bom ponto de partida.
Mas como é possível evitar variáveis globais?
Existem maneiras diferentes.
Algumas das respostas mais antigas aqui usam a abordagem de instância estática .
É fácil e muito bom, mas obriga a implementar o padrão para todas as classes que queremos acessar.
Além disso, muitas vezes essa abordagem coloca o caminho para cair no problema da classe god, porque os desenvolvedores tornam acessível uma classe principal usando esse método e depois o usam para acessar todas as outras classes.
Eu já expliquei o quão ruim é uma classe god, portanto, a abordagem de instância estática é um bom caminho a percorrer quando um plug-in precisa apenas tornar acessível uma ou duas classes.
Isso não significa que ele possa ser usado apenas para plugins com apenas algumas classes; de fato, quando o princípio de injeção de dependência é usado corretamente, é possível criar aplicativos bastante complexos sem a necessidade de tornar globalmente acessível um grande número de objetos.
No entanto, algumas vezes os plugins precisam tornar acessíveis algumas classes e, nesse caso, a abordagem de instância estática é esmagadora.
Outra abordagem possível é usar o padrão de registro .
Esta é uma implementação muito simples:
Usando essa classe, é possível armazenar objetos no objeto de registro por um ID, portanto, tendo acesso a um registro, é possível ter acesso a todos os objetos. Obviamente, quando um objeto é criado pela primeira vez, ele precisa ser adicionado ao registro.
Exemplo:
O exemplo acima deixa claro que, para ser útil, o registro precisa estar acessível globalmente. Uma variável global para o único registro não é muito ruim; no entanto, para puristas não globais, é possível implementar a abordagem de instância estática para um registro ou talvez uma função com uma variável estática:
A primeira vez que a função é chamada, ela instancia o registro e, nas chamadas subsequentes, apenas o devolve.
Outro método específico do WordPress para tornar uma classe acessível globalmente está retornando uma instância de objeto de um filtro. Algo assim:
Depois disso, em qualquer lugar, o registro é necessário:
Outro padrão que pode ser usado é o padrão do localizador de serviço . É semelhante ao padrão do registro, mas os localizadores de serviço são passados para várias classes usando injeção de dependência.
O principal problema desse padrão é que ele oculta as dependências de classes, dificultando a manutenção e a leitura do código.
DI Containers
Independentemente do método usado para tornar o localizador de registro ou serviço globalmente acessível, os objetos precisam ser armazenados lá e, antes de serem armazenados, precisam ser instanciados.
Em aplicações complexas, onde existem muitas classes e muitas delas possuem várias dependências, instanciar classes requer muito código; portanto, a possibilidade de erros aumenta: o código que não existe não pode ter erros.
Nos últimos anos, surgiram algumas bibliotecas PHP que ajudam os desenvolvedores a instanciar e armazenar facilmente instâncias de objetos, resolvendo automaticamente suas dependências.
Essas bibliotecas são conhecidas como contêineres de injeção de dependência porque são capazes de instanciar classes resolvendo dependências e também para armazenar objetos e devolvê-los quando necessário, agindo de maneira semelhante a um objeto de registro.
Geralmente, ao usar contêineres DI, os desenvolvedores precisam configurar as dependências para cada classe do aplicativo e, na primeira vez em que uma classe é necessária no código, ela é instanciada com dependências apropriadas e a mesma instância é retornada repetidamente em solicitações subsequentes. .
Alguns contêineres DI também são capazes de descobrir automaticamente dependências sem configuração, mas usando a reflexão do PHP .
Alguns recipientes DI bem conhecidos são:
e muitos outros.
Quero ressaltar que, para plugins simples, que envolvem apenas poucas classes e classes não possuem muitas dependências, provavelmente não vale a pena usar contêineres DI: o método de instância estática ou um registro acessível global são boas soluções, mas para plugins complexos o benefício de um contêiner DI se torna evidente.
Obviamente, mesmo objetos de contêineres DI precisam estar acessíveis para serem usados no aplicativo e, para esse propósito, é possível usar um dos métodos vistos acima, variável global, variável estática de instância, objeto retornado via filtro e assim por diante.
Compositor
Usar o contêiner DI geralmente significa usar código de terceiros. Atualmente, no PHP, quando precisamos usar uma biblioteca externa (não apenas os contêineres DI, mas qualquer código que não faça parte do aplicativo), basta fazer o download e colocá-lo na pasta do aplicativo não é considerado uma boa prática. Mesmo se formos os autores desse outro pedaço de código.
A dissociação de um código de aplicativo de dependências externas é sinal de melhor organização, melhor confiabilidade e melhor sanidade do código.
Compositor , é o padrão de fato na comunidade PHP para gerenciar dependências do PHP. Longe de ser popular também na comunidade WP, é uma ferramenta que todo desenvolvedor de PHP e WordPress deve conhecer, se não usar.
Essa resposta já é do tamanho de um livro para permitir uma discussão mais aprofundada, e também discutir o Composer aqui provavelmente está fora de tópico, foi mencionado apenas por uma questão de completude.
Para mais informações, visite o site do Composer e também vale a pena ler este minisite com curadoria de @Rarst .
1 PSR são regras de padrões PHP lançadas pelo PHP Framework Interop Group
2 O Composer (uma biblioteca que será mencionada nesta resposta), entre outras coisas, também contém um utilitário de carregamento automático.
fonte
Eu uso a seguinte estrutura:
Notas:
init
método)Isenção de responsabilidade Ainda não uso testes de unidade ( muitas coisas no myplate ) e ouvi dizer que a estática pode ser menos preferível para eles. Faça sua pesquisa sobre isso, se você precisar fazer o teste unitário.
fonte
Tudo depende da funcionalidade.
Certa vez, criei um plug-in que registrava scripts quando o construtor era chamado, então tive que conectá-lo ao
wp_enqueue_scripts
gancho.Se você quiser chamá-lo quando seu
functions.php
arquivo for carregado, você também poderá criar uma instância$class_instance = new ClassName();
como mencionou.Você pode considerar a velocidade e o uso da memória. Não conheço nenhum, mas posso imaginar que há ganchos desnecessários em alguns casos. Ao criar sua instância nesse gancho, você poderá economizar alguns recursos do servidor.
fonte
init()
método estático para que a instância da classe seja chamada no escopo da classe, em vez de outro escopo em que você possa sobrescrever as variáveis existentes.Eu sei que isso tem alguns anos, mas enquanto isso o php 5.3 suporta métodos anônimos , então eu vim com isso:
e de alguma forma eu gosto mais. Eu posso usar construtores regulares e não preciso definir nenhum método "init" ou "on_load" que atrapalhe minhas estruturas OOP.
fonte