PHP Filesystem

Filesystem reference: read/write, tests, permissions, paths.

Intermediate
Key PointsBuild paths with `DIRECTORY_SEPARATOR` or `__DIR__` — never hand-written slashes.

Read and write

FunctionPurposeNote
file_get_contents整个读入字符串大文件别用
file_put_contents字符串写入默认覆盖,FILE_APPEND 追加
fopen / fread / fwrite句柄式读写可控内存
fgets读一行大文件按行处理
file读成行数组FILE_IGNORE_NEW_LINES
readfile直接输出到缓冲区下载文件常用
fgetcsv读 CSV 一行处理引号与逗号
PHP
<?php
$p = __DIR__ . "/demo.txt";

file_put_contents($p, "第一行\n第二行\n");
echo filesize($p), "\n";
echo count(file($p, FILE_IGNORE_NEW_LINES)), "\n";
echo file_get_contents($p);

file_put_contents($p, "追加\n", FILE_APPEND);
echo count(file($p)), "\n";

unlink($p);
echo file_exists($p) ? "还在" : "已删除", "\n";
Output
20
2
第一行
第二行
3
已删除

Tests and info

PHP
<?php
$p = __DIR__ . "/demo.txt";
file_put_contents($p, "x");

var_dump(is_file($p));
var_dump(is_dir($p));
var_dump(is_readable($p));
var_dump(is_writable($p));

echo pathinfo($p, PATHINFO_EXTENSION), "\n";
echo basename($p), "\n";
echo dirname($p) !== "" ? "有目录" : "无目录", "\n";

unlink($p);
Output
bool(true)
bool(false)
bool(true)
bool(true)
txt
demo.txt
有目录
is_file / is_dir results are cached (stat cache). Deleting then testing in the same request can still report true. Call clearstatcache().

Permissions

PHP
<?php
$p = __DIR__ . "/perm.txt";
file_put_contents($p, "x");

printf("%o\n", fileperms($p) & 0777);

chmod($p, 0600);
clearstatcache();
printf("%o\n", fileperms($p) & 0777);

unlink($p);
Output
644
600

fileperms() includes file-type bits, so mask with 0777. Printing it raw gives a long octal string.

Upload and cache directories need 0755, **never 0777**. World-writable means any user on the box can modify it.

Paths

PHP
<?php
$path = "/var/www/demo/a/b/c.txt";

echo basename($path), "\n";
echo dirname($path), "\n";
echo dirname($path, 2), "\n";
print_r(pathinfo($path));

echo implode(DIRECTORY_SEPARATOR, ["a", "b", "c"]), "\n";
echo realpath(__DIR__) !== false ? "可解析" : "不可解析", "\n";
Output
/var/www/demo/a/b/c.txt

Security

  • Never use raw user input as a path —— realpath then verify the prefix
  • **Disable allow_url_fopen** or never let users control URLs
  • Allowlist include paths —— dynamic includes are RCE
  • Rename uploads —— keeping the original name gives up path control
include $_GET["page"] . ".php" is the classic LFI bug — combined with php://filter or log files it becomes RCE. Dynamic includes must come from an allowlist.

Friends & Partners