PHP · PROJECT

Build a PHP Item of the Day from a Text File

Match line 1 to the first day of the month, line 2 to the second, and so on.

Suppose you maintain a website with a featured game, article, or project for each calendar day. You want to edit a simple text file instead of changing PHP code every morning. Put one URL on each line and let the script select the line that matches today’s day number. This is a small project, but it illustrates file paths, date handling, validation, and output escaping in one place.

For a 31-day month, store 31 lines. On shorter months the later entries are not selected; they remain ready for a month with more days. The script below uses the server-side date in a specified time zone, so all visitors see the same daily item during that zone’s calendar day. If your audience spans time zones, decide which clock should define “today” before you publish.

Prepare the data file

Create item-of-the-day.txt beside the PHP file with one local path on each physical line. The example uses paths such as /programming/ and /tools/. Do not put headings or comments between entries: every line number is significant. A blank line is an error for that day, not a reason to shift all later entries upward.

/artificial-intelligence/
/programming/
/web-development/
/tools/
/projects/

Those are only the first five sample entries. Continue until there are 31. You can use an external URL if your feature is meant to send visitors elsewhere, but a local-path-only list is easier to validate and keeps the example on your own site. Keep the file in a private location if you do not want visitors downloading the entire schedule.

Choose the day and its matching line

Use DateTimeImmutable with an explicit DateTimeZone. The format character j returns the day of the month without a leading zero. Array indexes start at zero, so subtract one. For example, the 7th selects $lines[6], which is the seventh physical line.

<?php
$zone = new DateTimeZone('America/Chicago');
$today = new DateTimeImmutable('now', $zone);
$day = (int) $today->format('j');

$path = __DIR__ . '/item-of-the-day.txt';
$lines = file($path, FILE_IGNORE_NEW_LINES);
if ($lines === false || count($lines) !== 31) {
    throw new RuntimeException('Expected exactly 31 lines.');
}

$itemPath = trim($lines[$day - 1]);

The explicit time zone prevents a shared host’s default time zone from quietly changing your daily selection. You can choose another IANA zone if it fits your site better. Do not use FILE_SKIP_EMPTY_LINES or filter the array here: that would break the mapping between line number and calendar day.

Validate before generating a link

A text file is editable data, so check the selected entry before printing it. This example accepts a root-relative path made from letters, digits, underscores, hyphens, and slashes, with a trailing slash. It rejects a blank line, query string, protocol, HTML, or a path containing a dot. Adjust the rule deliberately if your site uses different URL shapes.

<?php
if (!preg_match('~^/(?:[a-z0-9_-]+/)+$~i', $itemPath)) {
    throw new RuntimeException('The selected path is invalid.');
}

$segments = explode('/', trim($itemPath, '/'));
$title = ucwords(str_replace('-', ' ', end($segments)));
$href = htmlspecialchars($itemPath, ENT_QUOTES, 'UTF-8');
$label = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
echo '<a href="' . $href . '">Today’s pick: ' . $label . '</a>';

The title generation is intentionally simple. A slug such as read-text-file becomes “Read Text File,” but acronyms and brand names need editorial control. For a polished feature, store both URL and display title in structured data, or maintain a small mapping of special titles. Do not assume a URL slug is always the title your readers should see.

What happens when something is missing?

Failing with an exception is useful while building, but a public page should have a graceful fallback. You could display a hand-picked evergreen item if the file is absent or the selected line is invalid. Log enough detail on the server for you to find the problem, but avoid showing a server file path or a PHP stack trace to visitors. If you decide to allow fewer than 31 entries, define an explicit fallback rule instead of using an accidental out-of-range index.

Think about caching as well. If the entire page is cached for more than a day, the PHP script may select the correct item while visitors continue seeing yesterday’s cached HTML. Either set a cache duration that expires before the next day in your chosen time zone or render the daily component separately. Testing at noon alone will not reveal a midnight or daylight-saving issue.

Test the boundaries

Temporarily inject a chosen day number into a local development copy to check days 1, 7, 28, 30, and 31. Confirm that the seventh day selects the seventh physical line even if an earlier line is blank. Test a missing file, a duplicate URL, a malformed path, and a time zone different from the server default. Then remove any hard-coded test day before publishing.

For a small hand-maintained feature, a text file is easy to inspect and change. As the feature grows, a CSV file with explicit dates and titles—or a database—may be clearer. The important part is keeping the rule for selecting an item easy to explain: a specific date, a specific line, and a valid destination. For the underlying file operations, see how to read a text file line by line.

Reference: PHP file() and date format characters.