Estoy desarrollando un tema de WordPress usando un motor de plantillas. Quiero que mi código sea lo más compatible posible con la funcionalidad principal de WP.
Algún contexto primero
Mi primer problema fue encontrar una manera de resolver la plantilla a partir de una consulta WP. Lo resolví usando una biblioteca mía, Brain \ Hierarchy .
En cuanto get_template_part()
y otras funciones que las cargas parciales gustaría get_header()
, get_footer()
y similares, que era bastante fácil de envoltura de escritura a la funcionalidad parcial motor de plantillas.
La cuestión
Mi problema ahora es cómo cargar la plantilla de comentarios.
La función de WordPress comments_template()
es una función de ~ 200 líneas que hace muchas cosas, que también quiero hacer para obtener la máxima compatibilidad con el núcleo.
Sin embargo, tan pronto como llamo comments_template()
, un archivo es require
d, es el primero de:
- el archivo en la constante
COMMENTS_TEMPLATE
, si está definido comments.php
en la carpeta del tema, si se encuentra/theme-compat/comments.php
en WP incluye carpeta como último recurso alternativo
En resumen, no hay forma de evitar que la función cargue un archivo PHP, lo cual no es deseable para mí, porque necesito renderizar mis plantillas y no simplemente usarlas require
.
Solución actual
En este momento, estoy enviando un comments.php
archivo vacío y estoy usando el 'comments_template'
enlace de filtro, para saber qué plantilla quiere cargar WordPress, y uso la función de mi motor de plantillas para cargar la plantilla.
Algo como esto:
function engineCommentsTemplate($myEngine) {
$toLoad = null; // this will hold the template path
$tmplGetter = function($tmpl) use(&$toLoad) {
$toLoad = $tmpl;
return $tmpl;
};
// late priority to allow filters attached here to do their job
add_filter('comments_template', $tmplGetter, PHP_INT_MAX);
// this will load an empty comments.php file I ship in my theme
comments_template();
remove_filter('comments_template', $tmplGetter, PHP_INT_MAX);
if (is_file($toLoad) && is_readable($toLoad)) {
return $myEngine->render($toLoad);
}
return '';
}
La pregunta
Esto funciona, es compatible con el núcleo, pero ... ¿hay alguna manera de hacerlo funcionar sin tener que enviar un vacío comments.php
?
Porque no me gusta
comments_template
filtro oCOMMENTS_TEMPLATE
constante para personalizar la plantilla. Lo cual no es fundamental, pero, como dije, quería mantener la mayor compatibilidad posible con el núcleo.Solución: utilice un archivo temporal, con un nombre de archivo único
Después de muchos saltos y gatear en los rincones más sucios de PHP, reformulé la pregunta para que simplemente:
como el código en el núcleo solo es
Entonces la pregunta se resolvió más rápido:
y eso es. Tal vez sería mejor usar
wp_upload_dir()
en su lugar:Otra opción podría ser usar
get_temp_dir()
qué envolturasWP_TEMP_DIR
. Sugerencia: extrañamente se recurre a él/tmp/
para que los archivos no se conserven entre reinicios, lo que/var/tmp/
sí. Se puede hacer una simple comparación de cadenas al final y verificar el valor de retorno y luego arreglarlo en caso de que sea necesario, lo cual no es así:Ahora para probar rápidamente si se producen errores para un archivo temporal sin contenido:
Y: Sin errores → trabajando.
EDITAR: Como señaló @toscho en los comentarios, todavía hay una mejor manera de hacerlo:
Nota: Según una nota de los usuarios en los documentos de php.net , el
sys_get_temp_dir()
comportamiento difiere entre sistemas. Por lo tanto, el resultado elimina la barra inclinada final y luego se agrega nuevamente. Como se corrigió el error central # 22267 , ahora también debería funcionar en los servidores Win / IIS.Su función refactorizada (no probada):
Bonificación Nr.1:
tmpfile()
volveráNULL
. Si, en serio.Bonificación Nr.2:
file_exists( __DIR__ )
regresaráTRUE
. Sí, de verdad ... en caso de que lo hayas olvidado.^ Esto conduce a un error real en el núcleo de WP.
Para ayudar a otros a ir en modo explorador y encontrarlos (mal a piezas indocumentadas), resumiré rápidamente lo que probé:
Intento 1: archivo temporal en la memoria
El primer intento que hice fue crear una secuencia a un archivo temporal, usando
php://temp
. De los documentos PHP:El código:
Hallazgo: No, no funciona.
Intento 2: usar un archivo temporal
Hay
tmpfile()
, ¿por qué no usar eso?Sí, eso de este atajo.
Intento 3: usar un contenedor de flujo personalizado
Luego pensé que podría construir un envoltorio de flujo personalizado y registrarlo usando
stream_wrapper_register()
. Entonces podría usar una plantilla virtual de esa secuencia para engañar al núcleo para que crea que tenemos un archivo. Ejemplo de código a continuación (ya eliminé la clase completa y el historial no tiene suficientes pasos ...)Una vez más, este volvió
NULL
afile_exists()
.Probado con PHP 5.6.20
fuente
stream_stat()
? Creo que esto es lofile_exists()
que llamará para hacer su verificación ... php.net/manual/en/streamwrapper.stream-stat.phptempnam()
. El uso de un trabajo cron funcionará, pero es una sobrecarga adicional ...tempnam( sys_get_temp_dir(), 'comments.php' )
se escribe una vez , puede reutilizar el nombre del archivo y el archivo está vacío , por lo que no utiliza muchos recursos. Además, es fácil de entender en su código. Con mucho, la mejor solución, en mi humilde opinión.Como @AlainSchlesser sugirió seguir la ruta (y como las cosas que no funcionan siempre me molestan ), volví a intentar construir un contenedor de flujo para archivos virtuales. No pude resolverlo (léase: leer los valores de retorno en los documentos) por mi cuenta, pero lo resolví con la ayuda de @HPierce en SO .
Solo necesita registrar la nueva clase como nuevo protocolo:
Esto luego permite crear un archivo virtual (no existente):
Su función puede ser refactorizada para:
como el
file_exists()
check in core regresaTRUE
yrequire $file
no arroja ningún error.Tengo que tener en cuenta que estoy muy contento de cómo resultó esto, ya que podría ser realmente útil con las pruebas unitarias.
fuente