acc-check-file-io
1
总安装量
1
周安装量
#47457
全站排名
安装命令
npx skills add https://github.com/dykyi-roman/awesome-claude-code --skill acc-check-file-io
Agent 安装分布
opencode
1
claude-code
1
Skill 文档
File I/O Patterns Audit
Analyze PHP code for suboptimal or dangerous file I/O patterns.
Detection Patterns
1. Full File Read Into Memory
// CRITICAL: Entire file loaded into memory
$content = file_get_contents('/path/to/large-file.csv'); // 500MB file â OOM!
$lines = file('/path/to/large-file.csv'); // Loads all lines into array!
// CRITICAL: Reading large file to process line by line
$content = file_get_contents($path);
$lines = explode("\n", $content); // Double memory: content + lines array
foreach ($lines as $line) {
$this->process($line);
}
// CORRECT: Stream processing
$handle = fopen($path, 'rb');
while (($line = fgets($handle)) !== false) {
$this->process(trim($line));
}
fclose($handle);
// CORRECT: Generator for large files
function readLines(string $path): \Generator
{
$handle = fopen($path, 'rb');
try {
while (($line = fgets($handle)) !== false) {
yield trim($line);
}
} finally {
fclose($handle);
}
}
2. Missing File Locks
// CRITICAL: Concurrent writes without locking
class FileLogger
{
public function log(string $message): void
{
$handle = fopen($this->path, 'ab');
fwrite($handle, $message . "\n"); // Race condition with concurrent writers!
fclose($handle);
}
}
// CRITICAL: Read-modify-write without lock
$counter = (int) file_get_contents('counter.txt');
$counter++;
file_put_contents('counter.txt', (string) $counter);
// Concurrent requests: both read 5, both write 6 instead of 7
// CORRECT: File locking
$handle = fopen($this->path, 'cb+');
if (flock($handle, LOCK_EX)) {
$counter = (int) stream_get_contents($handle);
$counter++;
ftruncate($handle, 0);
rewind($handle);
fwrite($handle, (string) $counter);
flock($handle, LOCK_UN);
}
fclose($handle);
3. Temp File Not Cleaned Up
// CRITICAL: Temp file created but never deleted
class PdfGenerator
{
public function generate(array $data): string
{
$tmpFile = tempnam(sys_get_temp_dir(), 'pdf_');
file_put_contents($tmpFile, $this->render($data));
$pdf = $this->converter->convert($tmpFile);
// $tmpFile never deleted! Disk fills up over time
return $pdf;
}
}
// CORRECT: Always clean up in finally
class PdfGenerator
{
public function generate(array $data): string
{
$tmpFile = tempnam(sys_get_temp_dir(), 'pdf_');
try {
file_put_contents($tmpFile, $this->render($data));
return $this->converter->convert($tmpFile);
} finally {
if (file_exists($tmpFile)) {
unlink($tmpFile);
}
}
}
}
4. Missing File Handle Cleanup
// CRITICAL: File handle not closed on exception
class CsvProcessor
{
public function process(string $path): array
{
$handle = fopen($path, 'rb');
$results = [];
while (($row = fgetcsv($handle)) !== false) {
$results[] = $this->transform($row); // If this throws, handle leaks!
}
fclose($handle);
return $results;
}
}
// CORRECT: try/finally pattern
class CsvProcessor
{
public function process(string $path): array
{
$handle = fopen($path, 'rb');
try {
$results = [];
while (($row = fgetcsv($handle)) !== false) {
$results[] = $this->transform($row);
}
return $results;
} finally {
fclose($handle);
}
}
}
5. SplFileObject Not Used for CSV
// ANTIPATTERN: Manual CSV parsing
$handle = fopen($path, 'rb');
$header = fgetcsv($handle);
while ($row = fgetcsv($handle)) {
$data = array_combine($header, $row);
}
fclose($handle);
// CORRECT: SplFileObject with generator
function readCsv(string $path): \Generator
{
$file = new \SplFileObject($path, 'rb');
$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
$header = $file->fgetcsv();
foreach ($file as $row) {
if ($row === [null]) continue;
yield array_combine($header, $row);
}
}
6. Unsafe File Path Construction
// CRITICAL: Path traversal + race condition
$path = '/uploads/' . $request->get('filename');
if (file_exists($path)) { // Check
$content = file_get_contents($path); // Use â TOCTOU!
}
// CORRECT: Validate and use realpath
$basePath = realpath('/uploads');
$fullPath = realpath('/uploads/' . basename($request->get('filename')));
if ($fullPath === false || !str_starts_with($fullPath, $basePath)) {
throw new SecurityException('Invalid file path');
}
$content = file_get_contents($fullPath);
7. Writing Large Output Without Streaming
// CRITICAL: Building entire response in memory
class ExportController
{
public function export(): Response
{
$data = $this->repository->findAll(); // 100K rows
$csv = '';
foreach ($data as $row) {
$csv .= implode(',', $row) . "\n"; // String grows to 500MB
}
return new Response($csv);
}
}
// CORRECT: Streaming response
class ExportController
{
public function export(): StreamedResponse
{
return new StreamedResponse(function () {
$handle = fopen('php://output', 'wb');
foreach ($this->repository->findAllIterable() as $row) {
fputcsv($handle, $row);
flush();
}
fclose($handle);
}, 200, ['Content-Type' => 'text/csv']);
}
}
Grep Patterns
# Full file reads
Grep: "file_get_contents\(|file\(" --glob "**/*.php"
# Missing file locks
Grep: "fwrite\(|file_put_contents\(" --glob "**/*.php"
Grep: "flock\(" --glob "**/*.php"
# Temp files
Grep: "tempnam\(|sys_get_temp_dir\(|tmpfile\(" --glob "**/*.php"
Grep: "unlink\(.*tmp" --glob "**/*.php"
# File handles without finally
Grep: "fopen\(" --glob "**/*.php"
Grep: "finally.*fclose" --glob "**/*.php"
# CSV processing
Grep: "fgetcsv\(|SplFileObject" --glob "**/*.php"
# String concatenation for output
Grep: "\\\$.*\.=.*\\\\n|\\\$.*\.= implode" --glob "**/*.php"
Severity Classification
| Pattern | Severity |
|---|---|
| Full file read of unbounded size | ð´ Critical |
| File handle leak on exception | ð´ Critical |
| Write without lock (shared file) | ð Major |
| Temp file not cleaned up | ð Major |
| Building large string in memory | ð Major |
| Missing SplFileObject for CSV | ð¡ Minor |
Output Format
### File I/O: [Description]
**Severity:** ð´/ð /ð¡
**Location:** `file.php:line`
**Impact:** [Memory/Performance/Data integrity]
**Issue:**
[Description of the file I/O problem]
**Code:**
```php
// Problematic pattern
Fix:
// Stream-based / locked / cleaned-up pattern
Expected Improvement: Memory: 500MB â 4KB (streaming)