Actualizar
Resolví el problema y publiqué una respuesta. Sin embargo, mi solución no es 100% ideal. Preferiría eliminar solo symlinkel cachecon 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 clearstatcachese ejecuta, por lo que solo necesito borrar el caché una vez para cada enlace simbólico. Por alguna razón, esto no funciona. clearstatcachenecesita ser llamado cada vez que a symlinkestá incluido en el camino, pero ¿por qué? Debe haber una manera de optimizar la solución que tengo.
Estoy usando PHP 7.3.5con nginx/1.16.0. A veces file_get_contentsdevuelve 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 Nginxconfiguració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 Nginxconfiguració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é.

realpathpágina de funciones . Tal vez podría ayudarte.realpathconfile_get_conentsy sin suerte. Todavía a veces se carga desde el caché.realpathalgo 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
readlinky usar esa ruta de ubicación real?fuente
Este es el comportamiento deseado de PHP, puede ver esto aquí porque PHP utiliza
realpath_cachepara 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_cacheantes de usar laget_file_contentsfunció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
clearstatcacheantes de llamarfile_get_contents. Solo quiero llamarclearstatcachecuando 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_contents1es 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