PHP Composer Autoload PSR-4 Example (Step by Step)
Wire Composer PSR-4 autoload in PHP: map App\ to src/App/, require vendor/autoload.php, dump-autoload, and fix the usual class-not-found mistakes.
To use UK local time in PHP, set the default timezone to Europe/London with date_default_timezone_set('Europe/London') (or date.timezone in php.ini), prefer DateTimeImmutable with an explicit DateTimeZone, store timestamps in UTC in the database, and convert to London only when you display them. Europe/London observes GMT in winter and BST (UTC+1) in summer, so a fixed +00:00 offset is wrong for half the year. Never assume the server’s default is London — on many hosts it is UTC.
I care about this because UK date formats (dd/mm/yyyy) and British Summer Time show up in almost every form and report I build. The snippets below were run on PHP 8.4; the sample conversion uses 5 Oct 2026 13:47 UTC, which is 14:47 BST in London and 19:47 in Asia/Dhaka.
<?php
declare(strict_types=1);
echo date_default_timezone_get(), PHP_EOL;
date_default_timezone_set('Europe/London');
echo date_default_timezone_get(), PHP_EOL;
echo date('Y-m-d H:i:s T'), PHP_EOL;
Put date_default_timezone_set('Europe/London'); in a front controller or bootstrap file so every script agrees. Alternatively set in php.ini:
date.timezone = Europe/London
If you omit both, PHP 8+ typically falls back in a way that may warn or use UTC depending on configuration — either way, be explicit. Calling date() without a known zone is how “it works on my laptop†bugs ship to production.
Europe/London is an IANA timezone name. It includes the historical rules for daylight saving. Prefer it over GB or hard-coded offsets.
<?php
declare(strict_types=1);
$winter = new DateTimeImmutable('2026-01-15 12:00:00', new DateTimeZone('Europe/London'));
$summer = new DateTimeImmutable('2026-07-15 12:00:00', new DateTimeZone('Europe/London'));
echo $winter->format('Y-m-d H:i:s T (P)'), PHP_EOL;
echo $summer->format('Y-m-d H:i:s T (P)'), PHP_EOL;
Output:
2026-01-15 12:00:00 GMT (+00:00)
2026-07-15 12:00:00 BST (+01:00)
Same civil clock time, different offsets. If you store "2026-07-15 12:00:00" without a zone and later interpret it as UTC, you are one hour wrong for users in Britain. That breaks reminders, “open at 9am†logic, and working-day counters (see also my JavaScript working days with UK bank holidays project notes for the calendar side).
<?php
declare(strict_types=1);
$utc = new DateTimeImmutable('2026-10-05 13:47:00', new DateTimeZone('UTC'));
$london = $utc->setTimezone(new DateTimeZone('Europe/London'));
$dhaka = $utc->setTimezone(new DateTimeZone('Asia/Dhaka'));
echo $utc->format('Y-m-d H:i:s T'), PHP_EOL;
echo $london->format('Y-m-d H:i:s T'), PHP_EOL;
echo $dhaka->format('Y-m-d H:i:s T'), PHP_EOL;
echo $london->format('d/m/Y H:i'), PHP_EOL;
Full CLI dump from the test script (including defaults on this machine):
Default timezone: UTC
date(): 2026-10-05 13:45:46 UTC
After set Europe/London: Europe/London
Winter: 2026-01-15 12:00:00 GMT (+00:00)
Summer: 2026-07-15 12:00:00 BST (+01:00)
UTC: 2026-10-05 13:47:00 UTC
London: 2026-10-05 14:47:00 BST
Dhaka: 2026-10-05 19:47:00 +06
UK display: 05/10/2026 14:47
Naive UTC parse of local-looking string: 2026-03-29T01:30:00+00:00
Explicit Europe/London: 2026-03-29T02:30:00+01:00 -> BST
Stored UTC: 2026-10-05 13:45:46
Shown London: 05 Oct 2026, 14:45 BST
setTimezone returns a new DateTimeImmutable with the same instant and a different wall clock. That is the safe mental model: one instant, many displays.
Recommended pattern for apps (especially if you ever gain users outside the UK):
Europe/London, or their preference) to UTC, store Y-m-d H:i:s in UTC or a Unix timestamp.Europe/London, format for humans.<?php
declare(strict_types=1);
// User typed a UK local datetime in a form
$local = DateTimeImmutable::createFromFormat(
'd/m/Y H:i',
'05/10/2026 14:47',
new DateTimeZone('Europe/London')
);
if ($local === false) {
throw new RuntimeException('Invalid date');
}
$forDb = $local->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i:s');
// INSERT with PDO binding $forDb
// Later, read $row['created_at'] assumed UTC
$shown = (new DateTimeImmutable($row['created_at'] ?? $forDb, new DateTimeZone('UTC')))
->setTimezone(new DateTimeZone('Europe/London'))
->format('d M Y, H:i T');
Use PDO prepared statements for the insert. Format UK-style with d/m/Y for forms and d M Y for prose. For currency on the same invoices, a separate concern is JavaScript currency formatting in the browser — keep server and client zones consistent.
date() and strtotime() use the default timezone. They are fine for quick scripts once the default is set. For application code I prefer DateTimeImmutable because:
createFromFormat with a zone parses UK form input reliably.<?php
$naive = new DateTimeImmutable('2026-03-29 01:30:00'); // uses default zone
$explicit = new DateTimeImmutable('2026-03-29 01:30:00', new DateTimeZone('Europe/London'));
Around the spring-forward hour, civil times can be skipped or ambiguous. Explicit zones plus storing UTC remove most of the drama. If you need recurring “every Monday at 9:00 London timeâ€, libraries like Laravel’s Carbon build on the same PHP timezone database — the rules above still apply.
date.timezone = Europe/London in php.ini or date_default_timezone_set in bootstrap.DATETIME/TIMESTAMP with a clear convention) or Unix integers.Europe/London.c format) so clients are not guessing.+00:00 for Britain year-round.strtotime('now') with a later date_default_timezone_set.01/02/2026 as February in US libraries and January in UK forms — be explicit with createFromFormat('d/m/Y', ...).time() for display without formatting through a timezone.DateTimeImmutable objects.Either works if you are disciplined. Many teams default PHP to UTC and convert to Europe/London only in the presentation layer so servers and queues stay consistent. UK-only apps often default to Europe/London for convenience. Pick one convention and document it.
DateTimeZone::listIdentifiers() returns the IANA list. For a UK-only product you may not need a picker; hard-code London and say so in the UI.
TIMESTAMP columns convert between the session time_zone and UTC storage. That can surprise you if the MySQL session zone differs from PHP’s. Many apps use DATETIME and treat the value as UTC in application code to keep one source of truth. Whichever you choose, test inserts and reads explicitly.
Store UTC. Keep a timezone preference per user (Asia/Dhaka, Europe/London). On output, setTimezone(new DateTimeZone($userTz)). Do not store two copies of the same instant.
The cron expression or scheduler is probably using the server’s UTC clock while you mentally planned BST. Either schedule in UTC and convert the civil time yourself, or run the scheduler in Europe/London and document it. Re-check after the last Sunday in March and October.
It sets the default for date functions in that process. It does not change MySQL’s session time zone or JavaScript’s Date behaviour in the browser. Align each layer.
<?php
declare(strict_types=1);
function uk_datetime(DateTimeInterface $utcInstant): string
{
return (new DateTimeImmutable('now', new DateTimeZone('UTC')))
->setTimestamp($utcInstant->getTimestamp())
->setTimezone(new DateTimeZone('Europe/London'))
->format('d/m/Y H:i');
}
function parse_uk_form_datetime(string $value): DateTimeImmutable
{
$dt = DateTimeImmutable::createFromFormat(
'!d/m/Y H:i',
$value,
new DateTimeZone('Europe/London')
);
$errors = DateTimeImmutable::getLastErrors();
if ($dt === false || ($errors['warning_count'] ?? 0) || ($errors['error_count'] ?? 0)) {
throw new InvalidArgumentException('Use UK time as dd/mm/yyyy hh:mm');
}
return $dt;
}
The ! in the format resets unused fields so you do not inherit the current clock’s seconds by accident. Always check getLastErrors() — createFromFormat can return a DateTimeImmutable even when the input was partially nonsense.
If a meeting is “every week at 10:00 London timeâ€, add seven calendar days in the London zone, not +604800 seconds from a UTC instant, when you want to keep the civil time stable. Conversely, for “exactly 48 hours laterâ€, add an interval in UTC. Decide which meaning the product needs; banks and universities often want civil time in Europe/London.
Browsers parse date strings inconsistently. Prefer sending ISO-8601 with offset from PHP (format('c')) after converting to London or UTC explicitly. For display-only UK formatting in JS, see format date dd/mm/yyyy. Do not ship a naive new Date('2026-10-05') and expect midnight in London — that form is treated as UTC in modern engines.
Your VPS may be in Frankfurt or New York while users are in Britain. That is fine. PHP’s timezone database does not care where the machine sits. Set the default (or convert explicitly) and keep the OS clock in NTP sync. Cron in UTC plus conversion is usually clearer than changing the system timezone on the server.
<?php
$a = new DateTimeImmutable('2026-10-05 14:47:00', new DateTimeZone('Europe/London'));
$b = new DateTimeImmutable('2026-10-05 13:47:00', new DateTimeZone('UTC'));
var_export($a == $b); // true — same instant
var_export($a->format('c') === $b->format('c')); // false — different strings
Compare objects or timestamps, not formatted strings. Sorting log lines alphabetically by d/m/Y also fails (month/day chaos); sort by UTC instants then format for display.
<?php
$todayLondon = new DateTimeImmutable('today', new DateTimeZone('Europe/London'));
$start = $todayLondon->setTime(0, 0);
$end = $todayLondon->setTime(23, 59, 59);
// convert $start/$end to UTC for querying a UTC column
“Today†depends on the zone. A request at 01:00 BST on Tuesday is still Monday evening in UTC — using UTC midnight for a UK newspaper site would be wrong.
Write: “All database timestamps are UTC. PHP default timezone is Europe/London for formatting. Forms accept UK civil time.†Future you will thank present you when DST confusion returns in March.
User enters 20/06/2026 19:30 as the start of a meetup in London.
<?php
$startLocal = parse_uk_form_datetime('20/06/2026 19:30');
// 2026-06-20 19:30:00 BST
$startUtc = $startLocal->setTimezone(new DateTimeZone('UTC'));
// 2026-06-20 18:30:00 UTC
$pdo->prepare('INSERT INTO events (title, starts_at) VALUES (?, ?)')
->execute(['PHP meetup', $startUtc->format('Y-m-d H:i:s')]);
On the event page:
<?php
$utc = new DateTimeImmutable($row['starts_at'], new DateTimeZone('UTC'));
echo $utc->setTimezone(new DateTimeZone('Europe/London'))->format('l j F Y, g:ia T');
// Saturday 20 June 2026, 7:30pm BST
Visitors in Dhaka can convert the same UTC row to Asia/Dhaka without you storing a second column. That is why UTC storage wins once you leave a single-zone hobby script.
Freeze time in tests with a fixed DateTimeImmutable injected into your services, or libraries that allow clock mocking. Do not call new DateTimeImmutable('now') deep inside domain code without a way to override it — DST regression tests become impossible.
| Format | Example (London) | Use |
|--------|------------------|-----|
| d/m/Y H:i | 05/10/2026 14:47 | Forms and compact UI |
| d M Y, H:i T | 05 Oct 2026, 14:47 BST | Readable prose |
| c | 2026-10-05T14:47:00+01:00 | APIs |
| U | Unix timestamp | Storage alternative |
Combine with DateTimeZone('Europe/London') on the way out and UTC on the way in. If you only remember one sentence from this article, remember that.
In the UK, clocks go forward on the last Sunday in March and back on the last Sunday in October. PHP’s timezone database knows the exact instants. Your job is not to hard-code those dates — your job is to use Europe/London so PHP applies them. Hard-coded “BST means +1†tables in application code rot.
Set or convert with Europe/London, store UTC when data might leave one country, format d/m/Y for British humans, and never hard-code a year-round +00:00 for the UK. The tested winter/summer pair at the top of this article is the proof that the offset moves — your code should move with it.
Setting the default is convenient for date() and strtotime(). Explicit DateTimeZone arguments are safer when code is copied between projects with different defaults. Use both: a London default for the UK app, and explicit zones at system boundaries (database UTC, user preferences, API payloads).
Be explicit about Europe/London, store UTC when data might travel, and format d/m/Y for British users. That removes most of the “works in January, breaks in July†class of bugs.
// note
This is a learning note from studying the web. It is one small topic, written so I can remember it. It is not a course and not a claim that I have finished the subject.
If a sentence is wrong, say so from the contact page and name this title. Drafts never appear here. Related notes, when they exist, are other published posts, and the same sample rule applies to each of them.
Wire Composer PSR-4 autoload in PHP: map App\ to src/App/, require vendor/autoload.php, dump-autoload, and fix the usual class-not-found mistakes.
Validate PHP uploads with UPLOAD_ERR_OK, finfo MIME detection, size limits and random names — and ignore $_FILES type and the original filename.
A tested PHP login with sessions and PDO: password_verify, session_regenerate_id on success, a protected dashboard, and a logout that clears the session cookie.