So far each example has lived in a single file. Real websites have dozens of pages that share the same header, footer and helper functions. Copying that code into every page would be a maintenance nightmare: change the menu once and you would have to edit every file. This lesson shows how to split code into reusable files with PHP include and require, and then how PHP reads and writes ordinary files such as logs, JSON settings and CSV exports.
PHP include and require
Four statements pull the contents of another PHP file into the current one, as if you had pasted it in at that point:
| Statement | If the file is missing | Loads the file more than once? |
|---|---|---|
require | Fatal error: the script stops | Yes, every time it runs |
require_once | Fatal error: the script stops | No, only the first time |
include | Warning: the script carries on | Yes, every time it runs |
include_once | Warning: the script carries on | No, only the first time |
Use require_once for files that define functions, classes or constants: the page cannot work without them, and loading them twice would cause a “cannot redeclare” error. Use require for template parts such as a header that you deliberately print. include is for genuinely optional pieces, which are rare.
Whenever you write a PHP include or require, build the path from __DIR__, a magic constant that holds the folder of the current file. A plain relative path like 'includes/helpers.php' is resolved from the working directory, which changes depending on which page started the request, so it can break as soon as one file includes another.
<?php
declare(strict_types=1);
const SITE_NAME = 'Marina Tea House';
function e(string $text): string
{
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}
function rupees(int $amount): string
{
return 'Rs. ' . number_format($amount);
}(No output. This file only defines a constant and two functions for other pages to use.)
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/helpers.php';
$items = ['Ginger tea' => 30, 'Sulaimani' => 25, 'Bun butter jam' => 60];
?>
<h1><?= e(SITE_NAME) ?> menu</h1>
<ul>
<?php foreach ($items as $name => $price): ?>
<li><?= e($name) ?> – <?= rupees($price) ?></li>
<?php endforeach; ?>
</ul><h1>Marina Tea House menu</h1> <ul> <li>Ginger tea – Rs. 30</li> <li>Sulaimani – Rs. 25</li> <li>Bun butter jam – Rs. 60</li> </ul>
The short helper e() saves typing htmlspecialchars() everywhere, which makes it more likely you will escape every value, as recommended in PHP Forms. The same idea gives you a shared layout: put the top of the page in includes/header.php, the bottom in includes/footer.php, and require them at the start and end of every page. WordPress themes work the same way with get_header() and get_footer().
Watch out: Never build a PHP include path from user input, as in require $_GET['page'] . '.php';. An attacker can change the value to load files you never intended. If pages are chosen by a parameter, map allowed values to file names with a match expression or an array, and reject everything else.
Reading and writing whole files
For small files, two functions do almost everything:
file_get_contents($path)reads the whole file into a string, or returnsfalseon failure.file_put_contents($path, $data, $flags)writes a string to a file, creating it if needed. By default it replaces the contents; theFILE_APPENDflag adds to the end instead, andLOCK_EXstops two visitors writing at the same moment.
file($path) is a handy relative: it returns the file as an array of lines. Check that a file exists with is_file() before reading it, and handle the false return value instead of assuming success.
<?php
declare(strict_types=1);
$logFile = __DIR__ . '/visits.log';
$visits = ['Gokul visited /menu', 'Rehana visited /contact', 'Gokul visited /order'];
foreach ($visits as $line) {
file_put_contents($logFile, $line . "\n", FILE_APPEND | LOCK_EX);
}
$lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
echo "The log has " . count($lines) . " lines.\n";
echo "Last entry: " . end($lines) . "\n";
echo "File size: " . filesize($logFile) . " bytes";
unlink($logFile); // delete the demo fileThe log has 3 lines. Last entry: Gokul visited /order File size: 65 bytes
Storing data as JSON
Plain text is fine for logs, but structured data such as settings is better stored as JSON. json_encode() turns a PHP array into a JSON string, and json_decode($json, true) turns it back into an associative array. Pass JSON_THROW_ON_ERROR so that broken JSON throws an exception instead of quietly returning null.
<?php
declare(strict_types=1);
$file = __DIR__ . '/settings.json';
$settings = ['shopName' => 'Marina Tea House', 'openHour' => 7, 'cities' => ['Chennai', 'Puducherry']];
file_put_contents($file, json_encode($settings, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$loaded = json_decode(file_get_contents($file), true, flags: JSON_THROW_ON_ERROR);
echo "{$loaded['shopName']} opens at {$loaded['openHour']} am\n";
echo "Branches: " . implode(' and ', $loaded['cities']);
unlink($file);Marina Tea House opens at 7 am Branches: Chennai and Puducherry
Working line by line with fopen()
For large files, or for CSV spreadsheets, open the file as a stream with fopen() and process it piece by piece. This keeps memory use low because the whole file is never loaded at once. The mode letter says what you plan to do: 'r' to read, 'w' to write (emptying the file first) and 'a' to append. Always fclose() the handle when you are finished. fputcsv() and fgetcsv() take care of commas and quotes inside values for you. Pass escape: '' to both: it gives standard (RFC 4180) CSV, and since PHP 8.4 leaving the escape argument out triggers a deprecation notice.
<?php
declare(strict_types=1);
$csvFile = __DIR__ . '/orders.csv';
$out = fopen($csvFile, 'w');
fputcsv($out, ['Customer', 'City', 'Amount'], escape: '');
fputcsv($out, ['Nithya', 'Salem', 540], escape: '');
fputcsv($out, ['Abdul', 'Kochi, Kerala', 1200], escape: '');
fclose($out);
$in = fopen($csvFile, 'r');
$header = fgetcsv($in, escape: '');
$total = 0;
while (($row = fgetcsv($in, escape: '')) !== false) {
$order = array_combine($header, $row);
echo "{$order['Customer']} ({$order['City']}): Rs. {$order['Amount']}\n";
$total += (int) $order['Amount'];
}
fclose($in);
echo "Total: Rs. $total";
unlink($csvFile);Nithya (Salem): Rs. 540 Abdul (Kochi, Kerala): Rs. 1200 Total: Rs. 1740
Tip: Keep data files such as logs and JSON settings outside your public web folder, or block access to them in the server configuration. Otherwise anyone who guesses the file name can download them. For anything bigger than a few hundred records, or data that many visitors change at once, use a database instead; that is the topic of the next lesson, PHP MySQL with PDO.
