IO #

PHP’s standard I/O library includes hundreds of functions for interacting with the filesystem, network, and streams. You don’t need to memorize all of them — what matters is knowing which functions exist for each need so you don’t rewrite something that’s already built in. This article is a quick reference for the most frequently needed I/O functions: file and directory operations, filesystem information, basic network functions, and some idiomatic patterns that make PHP I/O code cleaner and safer.

File Functions — Reading #

<?php
// file_get_contents — read an entire file into a string
$content = file_get_contents('/path/to/file.txt');
$json    = file_get_contents('https://api.example.com/data'); // URLs also work
$binaryB64 = base64_encode(file_get_contents('image.jpg'));

// file_get_contents with a context (headers, timeout, etc.)
$ctx     = stream_context_create(['http' => ['timeout' => 10]]);
$content = file_get_contents('https://api.example.com', false, $ctx);

// file — read as an array of lines
$lines   = file('data.csv', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $i => $line) {
    echo "$i: $line\n";
}

// readfile — read and output directly (for downloads)
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="report.pdf"');
readfile('/path/to/report.pdf');
exit;

// fopen/fread/fclose — full control, good for large files
$handle = fopen('/path/to/large.csv', 'r');
if ($handle === false) {
    throw new \RuntimeException("Failed to open file");
}

try {
    while (!feof($handle)) {
        $line = fgets($handle, 4096); // one line, max 4KB
        if ($line !== false) {
            // process the line...
        }
    }
} finally {
    fclose($handle);
}

// Read CSV specifically
$handle = fopen('data.csv', 'r');
$header = fgetcsv($handle); // read the header
while (($row = fgetcsv($handle)) !== false) {
    $data = array_combine($header, $row);
    // process $data...
}
fclose($handle);

// Read bytes at a specific position
$handle = fopen('data.bin', 'rb'); // 'b' = binary mode
fseek($handle, 1024);             // jump to byte 1024
$chunk  = fread($handle, 256);    // read 256 bytes
$position = ftell($handle);       // current position: 1280
rewind($handle);                  // back to the start
fclose($handle);

File Functions — Writing #

<?php
// file_put_contents — write a string to a file
$bytes = file_put_contents('output.txt', "File content\n");
echo "Wrote $bytes bytes\n";

// FILE_APPEND — append to the end of the file
file_put_contents('log.txt', date('Y-m-d H:i:s') . " — event\n", FILE_APPEND);

// LOCK_EX — exclusive lock (safe for multi-process)
file_put_contents('data.json', json_encode($data), LOCK_EX);

// Both at once
file_put_contents('app.log', $entry, FILE_APPEND | LOCK_EX);

// fopen/fwrite/fclose — full control
$handle = fopen('output.csv', 'w'); // 'w' = write new, 'a' = append
try {
    fputcsv($handle, ['ID', 'Name', 'Email']); // header
    foreach ($data as $row) {
        fputcsv($handle, [$row['id'], $row['name'], $row['email']]);
    }
} finally {
    fclose($handle);
}

// Complete file modes:
// 'r'  — read only, pointer at the start
// 'r+' — read & write, pointer at the start
// 'w'  — write new/truncate, pointer at the start
// 'w+' — read & write new/truncate
// 'a'  — append, pointer at the end
// 'a+' — read & append
// 'x'  — write new, FAILS if it already exists
// 'x+' — read & write new, FAILS if it already exists
// 'c'  — write, no truncate, pointer at the start
// 'c+' — read & write, no truncate

// Write with manual locking
$handle = fopen('counter.txt', 'c+');
flock($handle, LOCK_EX);
$value = (int) fread($handle, 20);
$value++;
rewind($handle);
ftruncate($handle, 0);
fwrite($handle, (string) $value);
flock($handle, LOCK_UN);
fclose($handle);

File and Directory Information #

<?php
$path = '/var/www/app/public/index.php';

// Check existence and type
var_dump(file_exists($path));    // bool(true/false)
var_dump(is_file($path));        // true if a file (not a directory)
var_dump(is_dir(dirname($path))); // true if a directory
var_dump(is_link($path));        // true if a symlink

// Access permissions
var_dump(is_readable($path));    // readable by the current process
var_dump(is_writable($path));    // writable
var_dump(is_executable($path));  // executable

// File information
echo filesize($path);                           // size in bytes
echo date('Y-m-d H:i:s', filemtime($path));    // last modification time
echo date('Y-m-d H:i:s', filectime($path));    // inode change time
echo date('Y-m-d H:i:s', fileatime($path));    // last access time
echo fileowner($path);                          // owner UID
echo filegroup($path);                          // group GID
echo decoct(fileperms($path) & 0777);          // octal permission: "755"

// stat — complete info
$stat = stat($path);
echo $stat['size'];    // file size
echo $stat['mtime'];   // modification time (Unix timestamp)

// Path information
echo basename($path);                           // "index.php"
echo basename($path, '.php');                   // "index"
echo dirname($path);                            // "/var/www/app/public"
echo dirname($path, 2);                         // "/var/www/app" (2 levels up)
echo realpath('../relative/path');              // resolved absolute path
echo pathinfo($path, PATHINFO_EXTENSION);       // "php"
echo pathinfo($path, PATHINFO_FILENAME);        // "index"
echo pathinfo($path, PATHINFO_DIRNAME);         // "/var/www/app/public"

$info = pathinfo($path); // everything at once
// ['dirname'=>'...', 'basename'=>'index.php', 'extension'=>'php', 'filename'=>'index']

// Directory size (recursive)
function dirSize(string $dir): int
{
    $size = 0;
    foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $file) {
        if ($file->isFile()) {
            $size += $file->getSize();
        }
    }
    return $size;
}

echo round(dirSize('/var/www/app') / 1024 / 1024, 2) . " MB\n";

File Operations — Copy, Move, Delete #

<?php
// copy — copy a file
copy('/source/file.txt', '/dest/file.txt');

// rename — move or rename (works across directories on the same filesystem)
rename('/old/path/file.txt', '/new/path/file.txt');

// unlink — delete a file
unlink('/path/to/file.txt');

// Delete with confirmation
if (file_exists('/path/to/file.txt')) {
    if (!unlink('/path/to/file.txt')) {
        throw new \RuntimeException("Failed to delete file");
    }
}

// touch — create an empty file or update the timestamp
touch('/path/to/file.txt');                // create if missing, update mtime if present
touch('/path/to/file.txt', time() - 3600); // set mtime to 1 hour ago

// chmod / chown — change permissions and owner
chmod('/path/to/file.php', 0644);   // rw-r--r--
chmod('/path/to/dir',      0755);   // rwxr-xr-x
chown('/path/to/file', 'www-data'); // needs privileges
chgrp('/path/to/file', 'www-data');

// Symbolic operations
symlink('/real/path/file.txt', '/link/path/link.txt'); // create a symlink
readlink('/link/path/link.txt'); // "/real/path/file.txt"

Directory Operations #

<?php
// mkdir — create a directory
mkdir('/new/dir');                        // one level
mkdir('/new/nested/dir', 0755, true);     // recursive, with permissions

// rmdir — delete a directory (must be empty)
rmdir('/empty/dir');

// getcwd — current working directory
echo getcwd(); // "/var/www/app"

// chdir — change the working directory
chdir('/tmp');

// scandir — list directory contents
$entries = scandir('/var/www/app');
// ['.', '..', 'composer.json', 'public', 'src', 'vendor']

$clean = array_filter(
    scandir('/var/www/app'),
    fn($e) => !in_array($e, ['.', '..'])
);

// glob — pattern matching for files
$phpFiles = glob('/var/www/app/src/**/*.php'); // all PHP files
$csvFiles = glob('/data/*.csv');
$multi    = glob('/tmp/{*.log,*.txt}', GLOB_BRACE);

// RecursiveDirectoryIterator — recursive traversal
$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('/var/www/app', RecursiveDirectoryIterator::SKIP_DOTS)
);

foreach ($iterator as $file) {
    if ($file->getExtension() === 'php') {
        echo $file->getPathname() . "\n";
    }
}

// DirectoryIterator — one level only
$dir = new DirectoryIterator('/var/www/app');
foreach ($dir as $item) {
    if ($item->isDot()) continue;
    echo ($item->isDir() ? '[DIR] ' : '[FILE] ') . $item->getFilename() . "\n";
}

// Disk information
echo disk_free_space('/');   // available bytes
echo disk_total_space('/');  // total bytes
$percent = (1 - disk_free_space('/') / disk_total_space('/')) * 100;
echo round($percent, 1) . "% used\n";

Temporary Files #

<?php
// tmpfile — a temporary file automatically deleted when closed
$tmp = tmpfile();
fwrite($tmp, "temporary data\n");
rewind($tmp);
$content = stream_get_contents($tmp);
fclose($tmp); // the file is automatically deleted

// tempnam — a unique temporary file name
$fileName = tempnam(sys_get_temp_dir(), 'app_'); // /tmp/app_abc123
file_put_contents($fileName, $data);
// process...
unlink($fileName); // must delete manually

// sys_get_temp_dir — the OS temp directory
echo sys_get_temp_dir(); // /tmp (Linux) or C:\Windows\Temp (Windows)

// php://memory — an in-memory buffer
$mem = fopen('php://memory', 'r+');
fwrite($mem, "temporary data");
rewind($mem);
$content = stream_get_contents($mem);
fclose($mem);

// php://temp — memory up to 2MB, then a disk file
$temp = fopen('php://temp/maxmemory:2097152', 'r+');
fwrite($temp, $largeData);

Network Functions #

<?php
// gethostbyname — resolve a hostname to an IP
$ip = gethostbyname('example.com'); // "93.184.216.34"

// gethostbyaddr — reverse DNS (IP to hostname)
$host = gethostbyaddr('8.8.8.8'); // "dns.google"

// dns_get_record — fetch DNS records
$records = dns_get_record('example.com', DNS_A | DNS_MX);
foreach ($records as $record) {
    echo $record['type'] . ": " . ($record['ip'] ?? $record['target'] ?? '') . "\n";
}

// checkdnsrr — check whether a DNS record exists
var_dump(checkdnsrr('example.com', 'MX')); // bool(true)
var_dump(checkdnsrr('notexist.invalid', 'A')); // bool(false)

// Validate an email with DNS
function emailValid(string $email): bool
{
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return false;
    }
    $domain = substr($email, strrpos($email, '@') + 1);
    return checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A');
}

// ip2long and long2ip — IP conversion
$long = ip2long('192.168.1.1');  // 3232235777
$ip   = long2ip(3232235777);     // "192.168.1.1"

// Useful for storing IPs in a database as integers (more efficient)
$pdo->prepare("INSERT INTO logs (ip_addr) VALUES (?)")->execute([ip2long($_SERVER['REMOTE_ADDR'])]);

// Useful $_SERVER keys for networking
echo $_SERVER['REMOTE_ADDR'];       // client IP
echo $_SERVER['HTTP_USER_AGENT'];   // browser user agent
echo $_SERVER['REQUEST_METHOD'];    // GET/POST/PUT/DELETE/etc.
echo $_SERVER['REQUEST_URI'];       // /path?query=string
echo $_SERVER['HTTP_HOST'];         // example.com
echo $_SERVER['SERVER_NAME'];       // server name
echo $_SERVER['HTTPS'];             // 'on' if HTTPS

CLI Input/Output Functions #

<?php
// Output to stdout and stderr
fwrite(STDOUT, "Normal message\n");
fwrite(STDERR, "Error message\n");

// Or just echo/print to stdout
echo "Hello from CLI\n";
print("This is also stdout\n");

// Reading from stdin
$input = fgets(STDIN);              // read one line
$input = trim(fgets(STDIN));        // with the newline trimmed

// Read a password without echo (Linux/macOS)
system('stty -echo');               // turn off echo
echo "Password: ";
$password = trim(fgets(STDIN));
system('stty echo');                // turn echo back on
echo "\n";

// Command line arguments
$script = $argv[0];                 // script name
$args   = array_slice($argv, 1);    // arguments after the script name
$count  = $argc;                    // argument count

// Example: php script.php --env=production --debug
foreach ($argv as $arg) {
    if (str_starts_with($arg, '--')) {
        [$key, $val] = array_pad(explode('=', ltrim($arg, '-'), 2), 2, true);
        $options[$key] = $val;
    }
}

// getopt — more structured argument parsing
$options = getopt('v', ['verbose', 'env:', 'port::', 'help']);
// 'v'      — short flag without a value
// 'env:'   — value required (--env=production or --env production)
// 'port::' — value optional (--port or --port=8080)

if (isset($options['help'])) {
    echo "Usage: php script.php --env=production [--port=8080]\n";
    exit(0);
}

// Exit codes — important for CI/CD and shell scripting
exit(0);  // success
exit(1);  // generic error
// PHP exit code 0 = success, non-zero = error

// Detect whether running in the CLI
if (PHP_SAPI === 'cli') {
    echo "Running on the command line\n";
} else {
    echo "Running on a web server\n";
}

Other Useful Functions #

<?php
// URL parsing
$url   = 'https://user:[email protected]:8080/path/to/page?key=val&foo=bar#anchor';
$parts = parse_url($url);
// [
//   'scheme' => 'https', 'host' => 'example.com', 'port' => 8080,
//   'user' => 'user', 'pass' => 'pass',
//   'path' => '/path/to/page', 'query' => 'key=val&foo=bar', 'fragment' => 'anchor'
// ]

echo parse_url($url, PHP_URL_HOST);  // "example.com"
echo parse_url($url, PHP_URL_PATH);  // "/path/to/page"

// parse_str — parse a query string into an array
parse_str('name=Budi&age=28&hobbies[]=read&hobbies[]=code', $result);
// ['name' => 'Budi', 'age' => '28', 'hobbies' => ['read', 'code']]

// http_build_query — the reverse of parse_str
$query = http_build_query(['name' => 'Budi Santoso', 'age' => 28]);
// "name=Budi+Santoso&age=28"

$url = 'https://api.example.com/search?' . http_build_query([
    'q'      => 'gaming laptop',
    'limit'  => 10,
    'offset' => 0,
]);

// header — send HTTP headers
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('X-Custom-Header: value');
header('Location: https://example.com/redirect', replace: true, response_code: 301);

// Check whether headers have already been sent
if (headers_sent($file, $line)) {
    echo "Headers already sent at $file line $line";
}

// ob_* — output buffering
ob_start();
echo "This is captured first";
$output = ob_get_clean(); // get the buffer and clear it

// Useful for generating a template into a string
ob_start();
include 'template.php';
$html = ob_get_clean();

Summary #

  • file_get_contents and file_put_contents for small-to-medium files; fopen/fread/fwrite/fclose for large files with per-chunk streaming so you don’t run out of memory.
  • Important file modes: 'r' (read), 'w' (write new/truncate), 'a' (append), 'x' (write new, fails if it exists), 'c' (write without truncating).
  • FILE_APPEND | LOCK_EX for log files written by many concurrent processes — FILE_APPEND adds to the end, LOCK_EX prevents corruption.
  • realpath() returns an absolute, resolved path including symlinks — use it before comparing paths or before sensitive filesystem operations.
  • RecursiveDirectoryIterator + RecursiveIteratorIterator for recursive directory traversal that’s more flexible than manual recursive functions.
  • tmpfile() creates a temporary file automatically deleted when the handle closes; tempnam() only creates a name — the file must be deleted manually with unlink().
  • getopt() for structured CLI argument parsing — 'param:' requires a value, 'param::' makes it optional, no colon means a boolean flag.
  • http_build_query() for building query strings safely — it automatically URL-encodes values and handles nested arrays.

← Previous: Strings   Next: Math →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact