Development Tips

Writing Modern PHP in 2026: A Practical Guide for Real Projects

By Ken Key Jun 5, 2024 4 min read Updated Oct 16, 2026

Writing Modern PHP in 2026: A Practical Guide for Real Projects
The short answer

Write modern PHP by starting every file with strict types, using readonly properties and constructor promotion for value objects, and leaning on typed signatures so the engine catches mistakes early. PHP 8.5 in 2026 is fast, strongly typed, and fully capable of clean, maintainable production code.

Part of the complete web development guide.

On this page

PHP has been declared dead roughly every year since I started writing it, and yet here we are in 2026 with PHP 8.5 shipping, strong typing everywhere, and a toolchain that rivals anything else on the web. The problem was never the language. It was the pile of copy-pasted, untyped, error-suppressing code that gave it a bad name. This is how I write PHP now.

Writing Modern PHP in 2026: A Practical Guide for Real Projects

Start with strict types and stop guessing

If you take one thing from this article, make it this. Every file starts with declare(strict_types=1);. Without it, PHP quietly coerces values and you spend a Friday night hunting a bug that a type error would have caught in two seconds.

<?php
declare(strict_types=1);

function formatPrice(int $cents, string $currency = 'USD'): string
{
 return number_format($cents / 100, 2) . ' ' . $currency;
}

formatPrice('1200'); // TypeError, and that is the point

That TypeError is not an annoyance. It is the language doing your code review for you. Turn it on and leave it on.

Writing Modern PHP in 2026: A Practical Guide for Real Projects: Use readonly properties and constructor promotion

Use readonly properties and constructor promotion

Value objects used to be twenty lines of boilerplate. Now they are three. If a class holds data that should not change after construction, mark it readonly and let the engine enforce it.

<?php
declare(strict_types=1);

final class InvoiceLine
{
 public function __construct(
 public readonly string $sku,
 public readonly int $quantity,
 public readonly int $unitPriceCents,
 ) {}

 public function subtotalCents(): int
 {
 return $this->quantity * $this->unitPriceCents;
 }
}

Immutable by default means fewer “why did this value change” bugs. The only code that can change it is code you wrote on purpose.

Enums instead of magic strings

Order status, user roles, payment states. These were string constants for a decade and they caused endless typos. PHP enums give you a real type with real cases, and you can attach behavior.

<?php
declare(strict_types=1);

enum OrderStatus: string
{
 case Pending = 'pending';
 case Paid = 'paid';
 case Shipped = 'shipped';
 case Cancelled = 'cancelled';

 public function isFinal(): bool
 {
 return match ($this) {
 self::Shipped, self::Cancelled => true,
 default => false,
 };
 }
}

Now a function that takes OrderStatus cannot be handed "payed". The bug is impossible before it is written.

Let exceptions be exceptions

The @ error suppression operator is a red flag in any codebase. So is a function that returns false on failure with no explanation. Throw a typed exception and let something upstream decide what to do. The match expression is also a clean replacement for long switch blocks.

<?php
declare(strict_types=1);

final class PaymentFailed extends RuntimeException {}

function charge(int $cents): string
{
 if ($cents <= 0) {
 throw new PaymentFailed('Charge amount must be positive.');
 }

 return match (true) {
 $cents < 500 => 'micro',
 $cents < 50000 => 'standard',
 default => 'large',
 };
}

Stop recompiling the same work

Performance in PHP is usually about avoiding repeated work, not micro-tuning loops. Two things I check on almost every project I touch:

  • OPcache. In production, enable it, set a sane opcache.memory_consumption, and set opcache.validate_timestamps=0 so PHP stops checking file mtimes on every request. Deploy and reload.
  • Autoloading. Run composer dump-autoload --optimize --classmap-authoritative on deploy. It builds a class map so the autoloader does not hit the filesystem on every class load.

Neither of those is clever. Both routinely cut response times on real sites more than any code rewrite.

Profile before you optimize anything

I have watched developers rewrite a perfectly fine function because it “looked slow.” Measure first. Xdebug’s profiler or a sampling profiler will tell you where the time actually goes. Nine times out of ten it is a database query in a loop, not the loop itself.

<?php
// Bad: N+1 queries hiding in plain sight
foreach ($orders as $order) {
 $customer = $db->query('SELECT * FROM customers WHERE id = ?', [$order->customerId]);
}

// Better: one query, then map in memory
$ids = array_unique(array_column($orders, 'customerId'));
$customers = $db->query(
 'SELECT * FROM customers WHERE id IN (' . implode(',', array_fill(0, count($ids), '?')) . ')',
 $ids
);

That single change is worth more than a week of chasing milliseconds inside a foreach.

Testing you will actually keep up

Pest has become the default for a lot of new projects because the syntax is pleasant enough that people keep writing tests. Whatever you pick, the goal is the same: cover the logic that would hurt if it broke. Payment math, permission checks, anything with a date. If you are building a site for a business where downtime costs money, tests are cheaper than apologies. That is true whether the work is a small local project or larger custom web development for a company that depends on it.

A short checklist I run on every codebase

  • declare(strict_types=1); in every file
  • Typed properties and return types, no exceptions
  • Enums for any fixed set of values
  • Exceptions over silent false
  • OPcache and optimized autoloading in production
  • Profile before optimizing
  • Tests on the code that would hurt if it broke

None of this is exotic. It is just the difference between code that survives a year of changes and code that gets rewritten every spring.

FAQ

Is PHP still worth learning in 2026?

Yes. It powers a huge share of the web, PHP 8.5 is fast and well-typed, and there is more paid work in it than most newer languages. The demand is for developers who write it well, not developers who avoid it.

What PHP version should I be running?

PHP 8.3 at minimum, ideally 8.4 or 8.5. Every version below 8.1 is end of life and no longer getting security patches, which is a real risk if the site handles any customer data.

Do I need a framework like Laravel or Symfony?

For anything with logins, payments, or an admin panel, yes. A framework handles routing, security, and the boring parts so you can focus on the actual product. For a small script or a single endpoint, plain PHP with Composer is fine.

Have a project like this?

I do custom web development for Long Island businesses. If you need this done right, let’s talk.