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_contentsandfile_put_contentsfor small-to-medium files;fopen/fread/fwrite/fclosefor 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_EXfor log files written by many concurrent processes —FILE_APPENDadds to the end,LOCK_EXprevents corruption.realpath()returns an absolute, resolved path including symlinks — use it before comparing paths or before sensitive filesystem operations.RecursiveDirectoryIterator+RecursiveIteratorIteratorfor 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 withunlink().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.