Actualizar
Resolví el problema y publiqué una respuesta. Sin embargo, mi solución no es 100% ideal. Preferiría eliminar solo symlink
el cache
con clearstatcache(true, $target)
o clearstatcache(true, $link)
pero eso no funciona.
También preferiría evitar el almacenamiento en caché de enlaces simbólicos en primer lugar o eliminar el enlace simbólico del caché inmediatamente después de generarlo. Lamentablemente, no tuve suerte con eso. Por alguna razón, clearstatcache(true)
después de crear un enlace simbólico no funciona, todavía se almacena en caché.
Con mucho gusto otorgaré la recompensa a cualquiera que pueda mejorar mi respuesta y resolver esos problemas.
Editar
Intenté optimizar mi código generando un archivo cada vez que clearstatcache
se ejecuta, por lo que solo necesito borrar el caché una vez para cada enlace simbólico. Por alguna razón, esto no funciona. clearstatcache
necesita ser llamado cada vez que a symlink
está incluido en el camino, pero ¿por qué? Debe haber una manera de optimizar la solución que tengo.
Estoy usando PHP 7.3.5
con nginx/1.16.0
. A veces file_get_contents
devuelve el valor incorrecto cuando se usa a symlink
. El problema es que después de eliminar y recrear un enlace simbólico, su valor anterior permanece en la memoria caché. A veces se devuelve el valor correcto, a veces el valor anterior. Parece aleatorio.
Intenté borrar el caché o evitar el almacenamiento en caché con:
function symlink1($target, $link)
{
realpath_cache_size(0);
symlink($target, $link);
//clearstatcache(true);
}
Realmente no quiero deshabilitar el almacenamiento en caché, pero todavía necesito una precisión del 100% con file_get_contents.
Editar
No puedo publicar mi código fuente, ya que es demasiado largo y complejo, así que he creado un ejemplo mínimo y reproducible (index.php) que recrea el problema:
<h1>Symlink Problem</h1>
<?php
$dir = getcwd();
if (isset($_POST['clear-all']))
{
$nos = array_values(array_diff(scandir($dir.'/nos'), array('..', '.')));
foreach ($nos as $no)
{
unlink($dir.'/nos/'.$no.'/id.txt');
rmdir($dir.'/nos/'.$no);
}
foreach (array_values(array_diff(scandir($dir.'/ids'), array('..', '.'))) as $id)
unlink($dir.'/ids/'.$id);
}
if (!is_dir($dir.'/nos'))
mkdir($dir.'/nos');
if (!is_dir($dir.'/ids'))
mkdir($dir.'/ids');
if (isset($_POST['submit']) && !empty($_POST['id']) && ctype_digit($_POST['insert-after']) && ctype_alnum($_POST['id']))
{
$nos = array_values(array_diff(scandir($dir.'/nos'), array('..', '.')));
$total = count($nos);
if ($total <= 100)
{
for ($i = $total; $i >= $_POST['insert-after']; $i--)
{
$id = file_get_contents($dir.'/nos/'.$i.'/id.txt');
unlink($dir.'/ids/'.$id);
symlink($dir.'/nos/'.($i + 1), $dir.'/ids/'.$id);
rename($dir.'/nos/'.$i, $dir.'/nos/'.($i + 1));
}
echo '<br>';
mkdir($dir.'/nos/'.$_POST['insert-after']);
file_put_contents($dir.'/nos/'.$_POST['insert-after'].'/id.txt', $_POST['id']);
symlink($dir.'/nos/'.$_POST['insert-after'], $dir.'/ids/'.$_POST['id']);
}
}
$nos = array_values(array_diff(scandir($dir.'/nos'), array('..', '.')));
$total = count($nos) + 1;
echo '<h2>Ids from nos directory</h2>';
foreach ($nos as $no)
{
echo ($no + 1).':'.file_get_contents("$dir/nos/$no/id.txt").'<br>';
}
echo '<h2>Ids from using symlinks</h2>';
$ids = array_values(array_diff(scandir($dir.'/ids'), array('..', '.')));
if (count($ids) > 0)
{
$success = true;
foreach ($ids as $id)
{
$id1 = file_get_contents("$dir/ids/$id/id.txt");
echo $id.':'.$id1.'<br>';
if ($id !== $id1)
$success = false;
}
if ($success)
echo '<b><font color="blue">Success!</font></b><br>';
else
echo '<b><font color="red">Failure!</font></b><br>';
}
?>
<br>
<h2>Insert ID after</h2>
<form method="post" action="/">
<select name="insert-after">
<?php
for ($i = 0; $i < $total; $i++)
echo '<option value="'.$i.'">'.$i.'</option>';
?>
</select>
<input type="text" placeholder="ID" name="id"><br>
<input type="submit" name="submit" value="Insert"><br>
</form>
<h2>Clear all</h2>
<form method="post" action="/">
<input type="submit" name="clear-all" value="Clear All"><br>
</form>
<script>
if (window.history.replaceState)
{
window.history.replaceState( null, null, window.location.href );
}
</script>
Parecía muy probable que fuera un problema con la Nginx
configuración. No tener estas líneas puede causar el problema:
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
Aquí está mi Nginx
configuración (puede ver que he incluido las líneas anteriores):
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.websemantica.co.uk;
root "/path/to/site/root";
index index.php;
location / {
try_files $uri $uri/ $uri.php$is_args$query_string;
}
location ~* \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $realpath_root$fastcgi_path_info;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param HTTPS $https;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
fastcgi_index index.php;
fastcgi_read_timeout 3000;
}
if ($request_uri ~ (?i)^/([^?]*)\.php($|\?)) {
return 301 /$1$is_args$args;
}
rewrite ^/index$ / permanent;
rewrite ^/(.*)/$ /$1 permanent;
}
Actualmente tengo el ejemplo anterior en vivo en https://www.websemantica.co.uk .
Intente agregar algunos valores en el formulario. Debería mostrarse Success!
en azul cada vez. A veces se muestra Failure!
en rojo. Se puede tomar un buen número de actualizaciones de página para cambiar de Success!
a Failure!
o viceversa. Eventualmente, se mostrará Success!
cada vez, por lo tanto, debe haber algún tipo de problema de almacenamiento en caché.
realpath
página de funciones . Tal vez podría ayudarte.realpath
confile_get_conents
y sin suerte. Todavía a veces se carga desde el caché.realpath
algo comoclearstatcache(true); file_get_conents(realpath($fileName));
Respuestas:
Depende demasiado del nivel del sistema operativo. Entonces, ¿qué tal tratar de pensar fuera de la caja? ¿Qué tal intentar leer la ubicación real del archivo
readlink
y usar esa ruta de ubicación real?fuente
Este es el comportamiento deseado de PHP, puede ver esto aquí porque PHP utiliza
realpath_cache
para almacenar las rutas de los archivos debido a las mejoras de rendimiento para que pueda reducir las operaciones de disco.Para evitar este comportamiento, tal vez pueda intentar borrar
realpath_cache
antes de usar laget_file_contents
funciónPuedes probar algo como esto:
Puede leer más para clearstatcache en PHP doc.
fuente
Hay dos cachés.
Primero el caché del sistema operativo y luego el caché de PHP.
En la mayoría de los casos
clearstatcache(true)
antesfile_get_contents(...)
hace el trabajo.Pero a veces también necesita borrar el caché del sistema operativo. En el caso de Linux, allí puedo pensar en dos lugares para borrar. PageCache (1) y dentries / inodes (2).
Esto aclara ambos:
Nota: Esto es bueno para la resolución de problemas, pero no para las llamadas frecuentes en producción, ya que borra todo el caché del sistema operativo y le cuesta al sistema unos momentos de reabastecimiento de caché.
fuente
¿Cómo eliminas el enlace simbólico? Eliminar un archivo (o un enlace simbólico) debería borrar automáticamente el caché.
De lo contrario, podría ver lo que sucede si hace:
Si esto no resuelve el problema, ¿podría ser un problema con nginx como en este problema? ?
Intente registrar todas las operaciones en un archivo de registro para ver qué sucede realmente .
o tal vez...
... ¿podrías prescindir de los enlaces simbólicos por completo ? Por ejemplo, almacene en una base de datos, memcache, archivo SQLite o incluso un archivo JSON el mapeo entre "nombre de archivo" y "objetivo de enlace simbólico real". Utilizando, por ejemplo, redis u otros almacenes de claves, puede asociar el "nombre de archivo" con el objetivo del enlace simbólico real y omitir por completo la resolución del sistema operativo.
Dependiendo del caso de uso, esto podría resultar incluso más rápido que usar enlaces simbólicos.
fuente
Hubo dos problemas que causaron el problema.
Primer problema
Ya publiqué como y edito en la pregunta. Es un problema con la configuración de Nginx.
Estas líneas:
necesario reemplazado por:
Segundo problema
El segundo problema era que necesitaba llamar
clearstatcache
antes de llamarfile_get_contents
. Solo quiero llamarclearstatcache
cuando sea absolutamente necesario, así que escribí una función que solo borra el caché cuando el directorio incluye unsymlink
.fuente
Estoy dejando mi primera respuesta, ya que sigue siendo una respuesta válida. Estoy mejorando la respuesta de @DanBray mediante la implementación de clearstatcache (verdadero, $ nombre de archivo).
Probé el código anterior con mi servidor web y funcionó.
fuente
file_get_contents1
es parte del marco que he creado, por lo que se usa mucho, lo que hace que la optimización sea importante.$dir_go=readdir("$realPath")
devuelve nuloWhile($dir_go!==null)
@DanBrayIntente colocar el código dentro de un elemento que se actualiza continuamente utilizando Jquery, así como forzar la revalidación y borrar la captura estática. Este código ha sido modificado de la respuesta original @naveed .
form.php:
profile.php:
fuente