Solução: use um arquivo temporário - com um nome de arquivo exclusivo
Depois de muitos pulos e rastejando nos cantos mais sujos do PHP, eu reformulei a pergunta para:
Como um truque PHP pode retornar TRUE
para file_exists( $file )
?
como o código no núcleo é apenas
file_exists( apply_filters( 'comments_template', $template ) )
Então a questão foi resolvida mais rapidamente:
$template = tempnam( __DIR__, '' );
e é isso. Talvez seja melhor usar wp_upload_dir()
:
$uploads = wp_upload_dir();
$template = tempname( $uploads['basedir'], '' );
Outra opção pode ser usar get_temp_dir()
que agrupa WP_TEMP_DIR
. Dica: Ele estranhamente volta para /tmp/
, então os arquivos não serão preservados entre as reinicializações, o que /var/tmp/
faria. Pode-se fazer uma comparação de cadeia simples no final e verificar o valor de retorno e, em seguida, corrigir isso caso seja necessário - o que não é nesse caso:
$template = tempname( get_temp_dir(), '' )
Agora, teste rapidamente se há erros lançados em um arquivo temporário sem conteúdo:
<?php
error_reporting( E_ALL );
$template = tempnam( __DIR__, '' );
var_dump( $template );
require $template;
E: Sem erros → trabalhando.
EDITAR: Como @toscho apontou nos comentários, ainda há uma maneira melhor de fazê-lo:
$template = tempnam( trailingslashit( untrailingslashit( sys_get_temp_dir() ) ), 'comments.php' );
Nota: De acordo com uma nota de usuários em documentos do php.net , o comportamento sys_get_temp_dir()
difere entre os sistemas. Portanto, o resultado remove a barra final e, em seguida, é adicionado novamente. Como o bug principal # 22267 é fixo, isso também deve funcionar nos servidores Win / IIS.
Sua função refatorada (não testada):
function engineCommentsTemplate( $engine )
{
$template = null;
$tmplGetter = function( $original ) use( &$template ) {
$template = $original;
return tempnam(
trailingslashit( untrailingslashit( sys_get_temp_dir() ) ),
'comments.php'
);
};
add_filter( 'comments_template', $tmplGetter, PHP_INT_MAX );
comments_template();
remove_filter( 'comments_template', $tmplGetter, PHP_INT_MAX );
if ( is_file( $template ) && is_readable( $template ) ) {
return $engine->render( $template );
}
return '';
}
Bônus Nr.1: tmpfile()
retornará NULL
. Sim, realmente.
Bônus Nr.2: file_exists( __DIR__ )
retornará TRUE
. Sim, realmente… caso você tenha esquecido.
^ Isso leva a um erro real no núcleo do WP.
Para ajudar outras pessoas a entrar no modo de explorador e a encontrá-las (mal a partes não documentadas), resumirei rapidamente o que tentei:
Tentativa 1: arquivo temporário na memória
A primeira tentativa que fiz foi criar um fluxo em um arquivo temporário usando php://temp
. Da documentação do PHP:
A única diferença entre os dois é que php://memory
sempre armazenará seus dados na memória, enquanto php://temp
usará um arquivo temporário quando a quantidade de dados armazenados atingir um limite predefinido (o padrão é 2 MB). A localização desse arquivo temporário é determinada da mesma maneira que a função sys_get_temp_dir()
.
O código:
$handle = fopen( 'php://temp', 'r+' );
fwrite( $handle, 'foo' );
rewind( $handle );
var_dump( file_exist( stream_get_contents( $handle, 5 ) );
Encontrar: Não, não funciona.
Tentativa 2: usar um arquivo temporário
Há tmpfile()
, então por que não usar isso?
var_dump( file_exists( tmpfile() ) );
// boolean FALSE
Sim, muito sobre esse atalho.
Tentativa 3: usar um wrapper de fluxo personalizado
Em seguida, achei que poderia criar um wrapper de fluxo personalizado e registre-o usando stream_wrapper_register()
. Então, eu poderia usar um modelo virtual desse fluxo para fazer o núcleo acreditar que temos um arquivo. Exemplo de código abaixo (eu já excluí a classe completa e o histórico não tem etapas suficientes…)
class TemplateStreamWrapper
{
public $context;
public function stream_open( $path, $mode, $options, &$opened )
{
// return boolean
}
}
stream_wrapper_register( 'vt://comments', 'TemplateStreamWrapper' );
// … etc. …
Novamente, isso retornou NULL
on file_exists()
.
Testado com PHP 5.6.20