He creado un complemento y, por supuesto, siendo yo, quería seguir un buen enfoque OO. Ahora lo que he estado haciendo es crear esta clase y luego, justo debajo, crear una instancia de esta clase:
class ClassName {
public function __construct(){
}
}
$class_instance = new ClassName();
Supongo que hay una forma más WP de iniciar esta clase, y luego me encontré con personas diciendo que prefieren tener una init()
función que una __construct()
. Y de manera similar, encontré algunas personas que usan el siguiente gancho:
class ClassName {
public function init(){
}
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );
¿Cuál es generalmente considerada la mejor manera de crear una instancia de clase WP en carga y tener esto como una variable accesible globalmente?
NOTA: Como un punto lateral interesante, he notado que si bien register_activation_hook()
se puede llamar desde dentro __construct
, no se puede llamar desde dentro init()
usando el segundo ejemplo. Quizás alguien pueda iluminarme sobre este punto.
Editar: Gracias por todas las respuestas, claramente hay un poco de debate sobre cómo manejar la inicialización dentro de la clase en sí, pero creo que generalmente hay un consenso bastante bueno sobre add_action( 'plugins_loaded', ...);
cuál es la mejor manera de comenzar ...
Editar: solo para confundir las cosas, también he visto esto usado (aunque no usaría este método yo mismo porque convertir una clase de OO en una función parece derrotar el punto):
// Start up this plugin
add_action( 'init', 'ClassName' );
function ClassName() {
global $class_name;
$class_name = new ClassName();
}
fuente
Respuestas:
Buena pregunta, hay una serie de enfoques y depende de lo que quieras lograr.
A menudo lo hago;
A más a fondo un ejemplo en profundidad, que se produjo como resultado de algunas discusiones recientes sobre este mismo tema dentro de la sala de chat se puede ver en esta GIST por miembro WPSE toscho .
El enfoque del constructor vacío.
Aquí hay un extracto de ventajas / desventajas tomado de la esencia anterior que ejemplifica el enfoque del constructor vacío en su totalidad.
La desventaja en mi opinión es débil, por lo que tendría que ser mi enfoque favorito, sin embargo, no es el único que uso. De hecho, varios otros pesos pesados sin duda intervendrán en este tema con su opinión sobre el tema en breve porque hay algunas buenas opiniones sobre este tema que deberían expresarse.
nota: necesito encontrar el ejemplo esencial de toscho que realizó 3 o 4 comparaciones de cómo crear una instancia de una clase dentro de un complemento que analizó los pros y los contras de cada uno, que el enlace anterior era la forma preferida de hacerlo, pero Los otros ejemplos proporcionan un buen contraste con este tema. Esperemos que toscho todavía tenga eso en el archivo.Nota: La respuesta de WPSE a este tema con ejemplos y comparaciones relevantes. También la mejor solución, por ejemplo, una clase en WordPress.
fuente
update.php
archivo central y no es parte de las acciones predeterminadas habituales en las que se debe confiar cuando se trata de la secuencia de eventos que se activan durante la inicialización y por eso prefiero usar esos ganchos que se aplican, en este casoplugins_loaded
. Esto es a lo que a menudo me refiero como una instantánea rápida de lo que sucede cuando Action Reference . Mi explicación no está completa en su totalidad.register_activation_hook()
debe llamar a esa función antes de que se active laplugins_loaded
acción.Al llegar aquí exactamente 2 años después de la pregunta original, hay algunas cosas que quiero señalar. (No me pidas que señale muchas cosas , nunca).
Gancho adecuado
Para crear una instancia de una clase de complemento, se debe usar el gancho adecuado . No hay una regla general para lo que es, porque depende de lo que haga la clase.
Usar un gancho muy temprano a
"plugins_loaded"
menudo no tiene sentido porque un gancho como ese se dispara para solicitudes de administrador, frontend y AJAX, pero muy a menudo un gancho posterior es mucho mejor porque permite instanciar clases de complementos solo cuando es necesario.Por ejemplo, se puede crear una instancia de una clase que hace cosas para plantillas
"template_redirect"
.En términos generales, es muy raro que una clase necesite ser instanciada antes de
"wp_loaded"
ser despedida.No hay clase de Dios
La mayoría de todas las clases usadas como ejemplos en respuestas anteriores usan una clase llamada como
"Prefix_Example_Plugin"
o"My_Plugin"
... Esto indica que probablemente haya una clase principal para el complemento.Bueno, a menos que un complemento esté hecho por una sola clase (en cuyo caso nombrarlo después del nombre del complemento es absolutamente razonable), crear una clase que administre todo el complemento (por ejemplo, agregar todos los ganchos que necesita un complemento o crear instancias de todas las otras clases de complemento ) puede considerarse una mala práctica, como un ejemplo de un objeto dios .
En la programación orientada a objetos, el código debe tender a ser SÓLIDO donde la "S" significa "Principio de responsabilidad única" .
Significa que cada clase debe hacer una sola cosa. En el desarrollo de plugins de WordPress significa que los desarrolladores deben evitar usar un solo gancho para crear una instancia de una clase de complemento principal , pero se deben usar diferentes ganchos para crear instancias de diferentes clases, de acuerdo con la responsabilidad de la clase.
Evitar ganchos en constructor
Este argumento se ha introducido en otras respuestas aquí, sin embargo, quiero comentar este concepto y vincular esta otra respuesta donde se ha explicado ampliamente en el ámbito de las pruebas unitarias.
Casi 2015: PHP 5.2 es para zombies
Desde el 14 de agosto de 2014, PHP 5.3 llegó a su fin . Definitivamente está muerto. PHP 5.4 será compatible para todo 2015, significa un año más en el momento en que estoy escribiendo.
Sin embargo, WordPress aún admite PHP 5.2, pero nadie debería escribir una sola línea de código que admita esa versión, especialmente si el código es OOP.
Hay diferentes razones:
Si no desea usar el código PHP 5.4+, use al menos 5.3+
Ejemplo
En este punto, es hora de revisar las respuestas anteriores basadas en lo que dije hasta aquí.
Una vez que ya no tengamos que preocuparnos por 5.2, podemos y debemos usar espacios de nombres.
En aras de una mejor explicación del principio de responsabilidad única, mi ejemplo utilizará 3 clases, una que hace algo en el frontend, una en el backend y una tercera utilizada en ambos casos.
Clase de administrador:
Clase frontend:
Interfaz de herramientas:
Y una clase de Herramientas, utilizada por los otros dos:
Al tener estas clases, puedo crear instancias con los ganchos adecuados. Algo como:
Inversión de dependencia e inyección de dependencia
En el ejemplo anterior, utilicé espacios de nombres y funciones anónimas para crear instancias de diferentes clases en diferentes ganchos, poniendo en práctica lo que dije anteriormente.
Observe cómo los espacios de nombres permiten crear clases nombradas sin ningún prefijo.
Apliqué otro concepto que se mencionó indirectamente anteriormente: Inyección de dependencias , es un método para aplicar el Principio de inversión de dependencias , la "D" en acrónimo SÓLIDO.
La
Tools
clase se "inyecta" en las otras dos clases cuando se instancian, por lo que de esta manera es posible separar la responsabilidad.Además,
AdminStuff
yFrontStuff
clases utilizan tipo dando a entender que declarar que necesitan una clase que implementaToolsInterface
.De esta manera, nosotros o los usuarios que usan nuestro código pueden usar diferentes implementaciones de la misma interfaz, haciendo que nuestro código no esté acoplado a una clase concreta sino a una abstracción: de eso se trata exactamente el Principio de Inversión de Dependencia.
Sin embargo, el ejemplo anterior se puede mejorar aún más. A ver cómo.
Cargador automático
Una buena manera de escribir un código OOP mejor legible es no mezclar la definición de tipos (Interfaces, Clases) con otro código y colocar cada tipo en su propio archivo.
Esta regla también es uno de los estándares de codificación PSR-1 1 .
Sin embargo, al hacerlo, antes de poder usar una clase, se necesita el archivo que la contiene.
Esto puede ser abrumador, pero PHP proporciona funciones de utilidad para cargar automáticamente una clase cuando es necesario, utilizando una devolución de llamada que carga un archivo en función de su nombre.
Usar espacios de nombres se vuelve muy fácil, porque ahora es posible hacer coincidir la estructura de la carpeta con la estructura del espacio de nombres.
Eso no solo es posible, sino que también es otro estándar de PSR (o mejor 2: PSR-0 ahora en desuso y PSR-4 ).
Siguiendo esos estándares, es posible utilizar diferentes herramientas que manejan la carga automática, sin tener que codificar un cargador automático personalizado.
Tengo que decir que los estándares de codificación de WordPress tienen diferentes reglas para nombrar archivos.
Entonces, al escribir código para el núcleo de WordPress, los desarrolladores deben seguir las reglas de WP, pero al escribir código personalizado es una elección del desarrollador, pero usar el estándar PSR es más fácil de usar herramientas ya escritas 2 .
Patrones de acceso global, registro y localizador de servicios.
Uno de los mayores problemas al crear instancias de clases de complementos en WordPress es cómo acceder a ellas desde varias partes del código.
WordPress en sí usa el enfoque global : las variables se guardan en un alcance global, haciéndolas accesibles en todas partes. Cada desarrollador de WP escribe la palabra
global
miles de veces en su carrera.Este es también el enfoque que utilicé para el ejemplo anterior, pero es malo .
Esta respuesta ya es demasiado larga para permitirme explicar más por qué, pero leer los primeros resultados en el SERP para "variables globales mal" es un buen punto de partida.
Pero, ¿cómo es posible evitar las variables globales?
Hay diferentes formas
Algunas de las respuestas anteriores aquí usan el enfoque de instancia estática .
Es fácil y bastante bueno, pero obliga a implementar el patrón para cada clase a la que queremos acceder.
Además, muchas veces este enfoque pone el camino para caer en el problema de la clase de dios, porque los desarrolladores hacen accesible una clase principal usando este método, y luego lo usan para acceder a todas las demás clases.
Ya expliqué lo mala que es una clase de dios, por lo que el enfoque de instancia estática es un buen camino cuando un complemento solo necesita hacer accesibles una o dos clases.
Esto no significa que se pueda usar solo para complementos que tengan solo un par de clases, de hecho, cuando el principio de inyección de dependencia se usa correctamente, es posible crear aplicaciones bastante complejas sin la necesidad de hacer que un número global sea accesible. de objetos.
Sin embargo, a veces los complementos deben hacer accesibles algunas clases, y en ese caso el enfoque de instancia estática es abrumador.
Otro enfoque posible es usar el patrón de registro .
Esta es una implementación muy simple:
Con esta clase, es posible almacenar objetos en el objeto de registro mediante una identificación, por lo que tener acceso a un registro es posible tener acceso a todos los objetos. Por supuesto, cuando se crea un objeto por primera vez, debe agregarse al registro.
Ejemplo:
El ejemplo anterior deja en claro que para ser útil, el registro debe ser accesible a nivel mundial. Una variable global para el registro único no es muy mala, sin embargo, para los puristas no globales es posible implementar el enfoque de instancia estática para un registro, o tal vez una función con una variable estática:
La primera vez que se llama a la función, creará una instancia del registro, en llamadas posteriores solo lo devolverá.
Otro método específico de WordPress para hacer que una clase sea accesible globalmente es devolver una instancia de objeto desde un filtro. Algo como esto:
Después de eso, en todas partes se necesita el registro:
Otro patrón que se puede usar es el patrón de localización de servicios . Es similar al patrón de registro, pero los localizadores de servicios se pasan a varias clases mediante inyección de dependencia.
El principal problema con este patrón es que oculta las dependencias de las clases, lo que dificulta el mantenimiento y la lectura del código.
DI contenedores
No importa el método utilizado para hacer que el localizador de registro o servicio sea accesible globalmente, los objetos deben almacenarse allí, y antes de almacenarse deben ser instanciados.
En aplicaciones complejas, donde hay muchas clases y muchas de ellas tienen varias dependencias, la creación de instancias de clases requiere una gran cantidad de código, por lo que aumenta la posibilidad de errores: el código que no existe no puede tener errores.
En los últimos años aparecieron algunas bibliotecas PHP que ayudan a los desarrolladores PHP a crear instancias y almacenar fácilmente instancias de objetos, resolviendo automáticamente sus dependencias.
Estas bibliotecas se conocen como Contenedores de inyección de dependencias porque son capaces de crear instancias de clases que resuelven dependencias y también de almacenar objetos y devolverlos cuando sea necesario, actuando de manera similar a un objeto de registro.
Por lo general, cuando se usan contenedores DI, los desarrolladores deben configurar las dependencias para cada clase de la aplicación, y luego, la primera vez que se necesita una clase en el código, se instancia con las dependencias adecuadas y la misma instancia se devuelve una y otra vez en las solicitudes posteriores. .
Algunos contenedores DI también son capaces de descubrir dependencias automáticamente sin configuración, pero utilizando la reflexión PHP .
Algunos contenedores DI conocidos son:
y muchos otros.
Quiero señalar que para complementos simples, que involucran solo unas pocas clases y las clases no tienen muchas dependencias, probablemente no valga la pena usar contenedores DI: el método de instancia estática o un registro global accesible son buenas soluciones, pero para complementos complejos El beneficio de un contenedor DI se hace evidente.
Por supuesto, incluso los objetos del contenedor DI deben ser accesibles para ser utilizados en la aplicación y para ese propósito es posible usar uno de los métodos vistos anteriormente, variable global, variable de instancia estática, devolver el objeto a través del filtro, etc.
Compositor
Usar el contenedor DI a menudo significa usar código de terceros. Hoy en día, en PHP, cuando necesitamos usar una biblioteca externa (no solo contenedores DI, sino cualquier código que no sea parte de la aplicación), simplemente descargarlo y ponerlo en nuestra carpeta de aplicaciones no se considera una buena práctica. Incluso si somos los autores de esa otra pieza de código.
Desacoplar un código de aplicación de dependencias externas es señal de una mejor organización, una mayor confiabilidad y una mejor cordura del código.
Composer , es el estándar de facto en la comunidad PHP para administrar dependencias PHP. Lejos de ser una corriente principal en la comunidad WP también, es una herramienta que todo desarrollador de PHP y WordPress debería al menos saber, si no usar.
Esta respuesta ya tiene el tamaño de un libro para permitir una discusión más profunda, y también discutir sobre Composer aquí probablemente esté fuera de tema, solo se mencionó por razones de integridad.
Para obtener más información, visite el sitio de Composer y también vale la pena leer este minisitio curado por @Rarst .
1 PSR son reglas estándar de PHP publicadas por PHP Framework Interop Group
2 Composer (una biblioteca que se mencionará en esta respuesta), entre otras cosas, también contiene una utilidad de autocargador.
fuente
Yo uso la siguiente estructura:
Notas:
init
método)Descargo de responsabilidad Todavía no uso pruebas unitarias (hay tantas cosas en myplate ) y escucho que la estática puede ser menos preferible para ellas. Investigue sobre esto si necesita realizar una prueba unitaria.
fuente
Todo depende de la funcionalidad.
Una vez hice un complemento que registraba scripts cuando se llamó al constructor, así que tuve que conectarlo al
wp_enqueue_scripts
gancho.Si desea llamarlo cuando
functions.php
se carga su archivo, también puede crear una instancia usted mismo$class_instance = new ClassName();
como lo mencionó.Es posible que desee considerar la velocidad y el uso de la memoria. No conozco ninguno, pero puedo imaginar que hay ganchos sin llamar en algunos casos. Al crear su instancia en ese enlace, puede guardar algunos recursos del servidor.
fuente
init()
método estático para que la instancia de clase se llame en el ámbito de la clase en lugar de otro ámbito donde posiblemente podría sobrescribir las variables existentes.Sé que esto tiene un par de años, pero mientras tanto, php 5.3 admite métodos anónimos , así que se me ocurrió esto:
y de alguna manera me gusta más. Puedo usar constructores regulares y no necesito definir ningún método "init" u "on_load" que estropee mis estructuras OOP.
fuente