Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

Object-Oriented Programming in PHP

Modern PHP applications are almost always object-oriented. This page explains the building blocks, the new features of PHP 8.0 to 8.4 and why magic methods matter for security.

Last updated 5 min readPHP knowledge

In short: Object-oriented programming (OOP) bundles data and the related functions into classes. For this, PHP offers classes, abstract classes, interfaces, traits and, since PHP 8.1, enums. PHP 8.0 to 8.4 added many language features, such as constructor promotion, readonly, property hooks and asymmetric visibility. Important for security: well-encapsulated code is easier to review, and magic methods make insecure deserialization dangerous. OOP does not help against flaws in the interpreter such as PHP2Shell.

What object-oriented programming means in PHP

Instead of using loose functions and global variables, a class describes a thing from the business domain: an order, a customer, an invoice. It defines which data belongs to it (properties) and what can be done with it (methods). An object is a concrete instance of that class.

For decision-makers this has practical value: object-oriented code with clear classes is easier to test, review and hand over to service providers. Old PHP applications made of many individual scripts with global variables, by contrast, are hard to maintain and error-prone during upgrades.

A central idea is encapsulation: a class exposes to the outside only the methods that others really need and protects its internal state. An order can then, for example, be marked as paid only through a method that first checks whether that is permitted at all. Bundling such rules in one place is a gain for security as well.

The building blocks at a glance

Building block Purpose Short example
Class blueprint for objects with properties and methods class Rechnung { ... }
Abstract class common base, cannot be instantiated directly abstract class Zahlart { ... }
Interface contract: which methods a class must offer interface Versandfaehig { ... }
Trait reusable methods for classes without a common parent class trait MitZeitstempel { ... }
Enum fixed set of permitted values as a dedicated type, since PHP 8.1 enum Status { case Offen; ... }

Visibilities (public, protected, private) regulate who may access properties and methods. Classes are organised in namespaces and loaded via an autoloader; how this works is shown on the page Namespaces and autoloading.

Modern PHP: the new features from 8.0 to 8.4

The following language features are documented in the PHP manual and in the release announcements respectively:

Version New feature
8.0 constructor promotion, nullsafe operator ?->, ::class on objects
8.1 readonly properties, enums, new in initializers, final for class constants
8.2 readonly classes; dynamic properties are deprecated
8.3 attribute #[\Override], typed class constants
8.4 property hooks, asymmetric visibility, new without enclosing parentheses

An example that combines several of these features:

<?php
declare(strict_types=1);

enum Status: string
{
    case Offen = 'offen';
    case Bezahlt = 'bezahlt';
}

final class Bestellung
{
    public function __construct(
        public readonly int $id,
        public private(set) Status $status = Status::Offen,  // from PHP 8.4
    ) {}

    public function alsBezahltMarkieren(): void
    {
        $this->status = Status::Bezahlt;
    }
}

$b = new Bestellung(42);
$b->alsBezahltMarkieren();
echo $b->status->value;              // bezahlt
$s = Status::tryFrom('storniert');   // null instead of an error

readonly prevents the ID from being changed after creation. private(set) allows reading from outside but writing only within the class. Guarantees like these in the code make errors less likely that would otherwise only surface in operation. The types behind this are explained on the page The PHP type system.

Upgrade pitfalls in object-oriented code

Two changes are particularly relevant for older applications. Since PHP 8.2.0, creating dynamic properties is deprecated, that is, properties that are not declared in the class, unless the class carries the attribute #[\AllowDynamicProperties]. Older code often does this unknowingly and then fills the logs with notices. In addition, according to the manual, typed properties must be initialised before they are first read, otherwise PHP throws an Error.

The attribute #[\Override] from PHP 8.3 is helpful: it marks a method as an override of a parent method. If that method no longer exists, for example after a rename in a library, PHP reports an error instead of silently letting the method run into the void. Checks like this make upgrades more predictable; for stuck legacy projects, our migration to a supported PHP version helps.

Objects and memory

In PHP, objects are passed around via identifiers (handles). If you pass an object to a function, it works with the same object, not with a copy. This is efficient but easily leads to circular references, for example when an order knows its line items and each line item knows its order again. Reference counting alone does not clean up such cycles; PHP has a garbage collector of its own for that. Details are on the page Memory management in PHP.

OOP and security: magic methods and deserialization

PHP has magic methods that are called automatically, such as __construct, __destruct, __toString, __get or __wakeup. This is convenient but has a downside: if an application converts foreign data back into objects with unserialize(), an attacker can determine which classes are created and thereby trigger existing code in magic methods. How to prevent this is described in the guide Insecure deserialization in PHP. In short: for data exchange use json_encode() and json_decode(), and use unserialize() only for your own, trustworthy data, ideally with the allowed_classes option.

Against PHP2Shell, however, good object-oriented code does not help. CVE-2026-109618 and CVE-2026-109619 lie in the interpreter, and according to the PHP team an exploit was demonstrated for mod_php before application code runs. The update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and the restart of all PHP processes are mandatory.

How does PHP SOS help?

  • We assess the structure of your application and show which parts make an upgrade to a supported PHP version harder.
  • We search specifically for risky patterns such as unserialize() on foreign data and dangerous magic methods.
  • We review custom developments as part of a security audit and penetration test.
  • We apply the PHP update, restart all workers and test the application afterwards.

Frequently asked questions

Is PHP an object-oriented language?

PHP supports both procedural and object-oriented code. Frameworks, CMS cores and shop systems today are predominantly built in an object-oriented way, with classes, interfaces and namespaces.

What is the difference between an interface and an abstract class?

An interface only describes which methods a class must offer. An abstract class can additionally contain code and properties already, but cannot be instantiated directly. A class can have several interfaces but only one parent class.

Since when have there been enums in PHP?

Enumerations arrived with PHP 8.1. They replace collections of constants with a dedicated type that permits only the defined cases. Backed enums additionally have an int or string value.

Why are magic methods relevant to security?

PHP calls methods such as __destruct or __wakeup automatically, for example when unserializing data. If foreign data reaches unserialize() unchecked, it can trigger unexpected behaviour in existing classes.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help