# Object-Oriented Programming in PHP

> Object-oriented PHP: classes, interfaces, traits and enums, new language features from PHP 8.0 to 8.4 and the link to deserialization and security.

Source: https://phpsos.de/en/php/objektorientierung/  
As of: 25/10/2026  
Provider: PHP SOS, PHP emergency help and server security. Immediate help via WhatsApp: https://wa.me/491736494914?text=Hello%20PHP%20SOS%2C%20we%20need%20help%20with%20our%20PHP%20server.%20Company%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**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](https://phpsos.de/en/php/namespaces-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
<?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](https://phpsos.de/en/php/typsystem/).

### 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](https://phpsos.de/en/leistungen/php-migration-legacy/) 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](https://phpsos.de/en/php/speicherverwaltung/).

## 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](https://phpsos.de/en/ratgeber/deserialisierung-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](https://phpsos.de/en/leistungen/sicherheitsaudit-penetrationstest/).
- 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

- [PHP Manual: Classes and objects, basics](https://www.php.net/manual/de/language.oop5.basic.php)
- [PHP Manual: Properties](https://www.php.net/manual/en/language.oop5.properties.php)
- [PHP Manual: Visibility](https://www.php.net/manual/en/language.oop5.visibility.php)
- [PHP 8.1 Release](https://www.php.net/releases/8.1/en.php)
