¿Cómo hacer que un plugin sea requerido en un tema de WordPress sin usar declaraciones condicionales PHP al llamar a una función individual de ese plugin?
Uno de mis temas de WordPress requiere algunos plugins de terceros para funcionar correctamente.
La mayoría de las veces solía llamar funciones de plugins de terceros usando declaraciones condicionales como
if(function_exist('plugin_function')) {
plugin_function() // hacer algo
}
Sin embargo, supongamos que necesito usar extensivamente un plugin a través de muchos archivos de mi tema... Me gustaría evitar usar muchas condiciones IF... ¿hay una forma adecuada de requerir que ciertos plugins específicos estén instalados en WP o mejor aún, instalarlos si faltan antes de activar el tema?
gracias

is_plugin_active()
es bastante frágil: dejará de funcionar cuando el autor del plugin cambie el nombre del archivo principal o cuando el usuario renombre el directorio o archivo principal del plugin. Es mejor verificar si existe cierta función pública.
Para evitar tener que hacer esa verificación cada vez que necesites alguna funcionalidad del plugin, podrías mostrar un mensaje en el área de administración:
add_action( 'admin_notices', 'my_theme_dependencies' );
function my_theme_dependencies() {
if( ! function_exists('plugin_function') )
echo '<div class="error"><p>' . __( 'Advertencia: El tema necesita el Plugin X para funcionar', 'my-theme' ) . '</p></div>';
}
Otra alternativa es usar algo como http://tgmpluginactivation.com/

Si el autor del plugin cambia el nombre de una función que verificas con function_exists
, entonces un usuario normal simplemente recibirá el mensaje de que no tiene instalado el plugin del que depende otro plugin. El problema es que el usuario en realidad sí tendrá el plugin instalado y se preguntará por qué no funciona. Ah, y no voy a votar negativamente por eso.

Si te preocupan los votos, entonces deberías votar positivamente la pregunta en sí, ya que es una buena pregunta.

Si el autor del plugin cambia el nombre de la función, recibirá quejas de muchos más usuarios que si cambiara el nombre del archivo.

Y voté negativamente tu respuesta porque, en mi opinión, no abordaba la pregunta. ¿O preferirías que votara negativamente en secreto, sin ofrecer ninguna explicación?

Solo estaba hablando del voto negativo, no del comentario. El comentario en sí está bien, ya que este tema es algo que necesita discusión. Agregaré otra respuesta ya que mis pensamientos sobre eso excederán la longitud de un comentario. Por favor, solo edita la respuesta para que podamos mantener los resultados de la discusión en las revisiones para que todos los sigan. Gracias.

Supongo que la pregunta es si es apropiado votar negativamente una respuesta y luego publicar una propia. Aunque eso es una discusión para Meta.

Aunque esto no evitaría que el tema se rompa cuando el plugin está deshabilitado, recomendaría echar un vistazo a este interesante artículo sobre "Cómo mostrar un aviso de administración para temas requeridos" del plugin. Nunca me ha gustado la idea de que un tema obligue a instalar un plugin, por lo que esta parece ser la siguiente mejor opción.
Otro pensamiento rápido: nunca lo he intentado, pero me pregunto si se podría encontrar alguna forma inteligente de agrupar múltiples hooks en un solo condicional. Tal vez podrías separar todas las funciones condicionales en un archivo diferente y solo requerirlo si if( function_exists( 'plugin_function' ) )
devuelve true
(entendiendo que esta es una verificación imperfecta).

Si solo lo necesitas en una página de plugin, entonces existe is_plugin_active()
. Si lo necesitas fuera, es mejor copiar/pegar la función principal en tu tema y luego reutilizarla:
if ( ! is_admin() )
{
/**
* Verifica si el plugin está activo comprobando la lista active_plugins.
*
* @since 2.5.0
*
* @param string $plugin Ruta base del plugin desde el directorio de plugins.
* @return bool True, si está en la lista de plugins activos. False, si no está en la lista.
*/
function is_plugin_active( $plugin ) {
return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( $plugin );
}
}
El condicional evita cualquier error por definir la función dos veces.

Nota: Esta respuesta está aquí solo para facilitar la discusión entre @scribu y @kaiser. Moderadores: Por favor no borren. Usuarios/Lectores: Por favor no voten. Si quieren seguir la discusión, revisen el historial de revisiones/ediciones. Si quieren unirse a la discusión, editen la Respuesta. Si la discusión llega a un resultado, entonces será marcada como tal. Gracias.
Escenarios
También hay diferentes escenarios que tienen distinto peso, donde podrías tener una dependencia de plugin. (Los ejemplos son solo ficticios). La palabra "Plugin (padre)" puede intercambiarse con "Tema" desde el punto de vista del padre.
- (fuerte) Un plugin hijo que solo extiende la funcionalidad o altera la visualización (y similar) de un plugin existente y por lo tanto no puede existir sin el padre. Ejemplo: BuddyPress » BuddyPress-FunkyCommentDisplay
- (normal) Un plugin que tiene funcionalidad extendida cuando un plugin hijo está activado. Ejemplo: jQueryAttachmentCarousel » jQuerySlideDeck
- (suave) Un plugin que solo añade una característica. Ejemplo: DisneyWonderlandTheme » MickeysSocialLinks
A continuación intento esbozar qué pasa cuando actualizas el "otro" plugin y la verificación ya no funciona.
- Ad 1) El plugin no podría existir sin BuddyPress activado » Todo está completamente roto.
- Ad 2) El plugin no podría ofrecer la opción de cambiar de Carousel a SlideDeck » Muestra cosas raras (asumo que los estilos están modificados para SlideDeck).
- Ad 3) MickeysSocialLinks desaparecen.
Verificación
Hay, en mi opinión, tres posibilidades para verificar si un plugin está activo:
- A. ¿Existe la carpeta?
- B. ¿Existe el archivo principal - opción
'active_plugins'
? - C. ¿Existe una función particular?
Si tomo mi Plugin de Verificación de Enlaces Internos como ejemplo, que no ofrece una API pública y no está pensado para ser extendido, entonces no vería razón (como autor) para no cambiar los nombres de funciones internas cuando quiera o solo por voluntad. Así que si alguien intentara aprovecharse de este plugin, entonces las cosas simplemente se romperían (dependiendo de la funcionalidad y lo apretado del acoplamiento) al actualizar. Lo mismo aplica para nombres de archivo. No tendría una razón real (aparte de que el plugin se desactivaría al actualizar) para no cambiar el nombre del archivo. Lo único que me detendría de cambiar el nombre de la carpeta es que la verificación y notificación de actualización funciona contra el nombre del archivo - si está alojado en el repositorio oficial.
Así que diría, de más débil (fácil de cambiar) a más fuerte (mucho habla en contra de cambiarlo) parte de un plugin (padre) sería:
función » nombre del archivo principal » carpeta
Cuando dije que verificar una función es menos frágil que usar is_plugin_active()
asumí que la función en cuestión es una que el autor del plugin fomenta explícitamente. El mejor ejemplo de esto sería la etiqueta de plantilla wp_pagenavi()
ofrecida por el plugin WP-PageNavi.
La dificultad en definir dependencias es que no hay una forma estándar de identificar plugins de forma única que no involucre nombres de archivo.
Más reflexiones sobre el tema:
Creo que hasta ahora podemos resumirlo en tres puntos:
- Hemos hablado de temas ligeramente diferentes
- Estamos de acuerdo en que no hay una forma infalible de evitar lo que pensé que sería el tema
- Desde tu entendimiento de la pregunta, ofreciste una forma válida de proceder
La forma más inteligente (hasta ahora) que se me ocurre, que ya he visto en algunos (demasiado pocos) plugins:
// dentro del archivo del plugin:
add_action( 'plugin_custom_hook', 'plugin_trigger' );
// dentro de alguna plantilla:
do_action( 'plugin_custom_hook' );
Sin pensar demasiado en detalle sobre ello, pero supongo que podrías enganchar tu aviso en una verificación en el filtro 'all' y comprobar dentro de current filter si se activó cuando estás en el hook shutdown
...?
Usar hooks funcionaría bien para dependencias 'normales' y 'débiles'. El único inconveniente es que todavía necesitarías usar
function_exists()
ois_plugin_active()
si quieres detenerte si la dependencia no se cumple. Usar el filtro 'all' para eso sería demasiado costoso en mi opinión.
@scibu Esto iba dirigido a "tu" tema. (Ya dejé de hablar del mío). :)
Básicamente, si necesitas una dependencia - y tienes un autor simpático - entonces él podría ofrecer un hook en lugar de/como reemplazo de una etiqueta de plantilla. Porque el plugin solo se engancharía a él si el hook estuviera presente, o simplemente no haría nada. Y por otro lado no tendrías un error cuando el plugin no está presente.
Aquí está la parte difícil (o más bien una P): Para escribir un aviso de administrador para informar al usuario sobre la dependencia "Necesitas instalar »DisneyWonderLinks«", podrías verificar array_keys( $GLOBALS['wp_filter']['template_tag_like_hook'] )
. No estoy seguro si esto funcionaría, pero que yo sepa el array debería ser accesible en ambos lados (público/admin).
Eso no funcionaría. Solo porque un callback está registrado en un hook no significa que el hook se activará cuando se espera. Lo único que funcionaría más o menos es usar el hook 'shutdown', que mencionaste antes:
add_action( 'shutdown', function() {
if ( !did_action( 'template_tag_like_hook' ) )
echo 'Problema.';
} );
Por supuesto, esto se imprimiría al final, después de la etiqueta </html>
, en el front-end (ya que ahí es donde normalmente se usan las etiquetas de plantilla), lo cual no es de mucha utilidad.
Podrías intentar guardar el mensaje en wp_options y luego mostrarlo en el área de administración, pero eso abriría una nueva lata de gusanos: invalidación, plugins de caché, etc.

Para que conste, esta es una forma bastante poco ortodoxa de usar la funcionalidad del sitio. Me recuerda a http://c2.com/cgi/wiki

Sí, lo es. Pero no tenía idea de cómo podríamos continuar la discusión sin ocultarla de lectores posteriores.

No me di cuenta de que publicar la pregunta generaría toda una discusión :) Pero es realmente interesante y les agradezco a ambos por su esfuerzo y tiempo dando consejos y proporcionando un debate reflexivo. Creo que el consejo de scribu (uno de los muchos que hay) sobre usar la clase TGM Activation podría ofrecer una solución a mi respuesta al menos desde un punto de vista puramente práctico, voy a investigarlo. Sin embargo, seguiré pendiente de toda la discusión porque otros métodos propuestos también tienen sentido en ciertos escenarios y es muy interesante para mí leerlos, ¡gracias!
