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

The PHP Type System

PHP is dynamically typed but has gained an ever stricter type system since PHP 7. Here you see the types, the key versions and why strict comparisons prevent security bugs.

Last updated 5 min readPHP knowledge

In short: PHP is dynamically typed: the type of a value is only determined at runtime, and unless told otherwise PHP converts values automatically when needed. Since PHP 7, however, parameters, return values and properties can be given types, and declare(strict_types=1) makes the check strict. Anyone who knows the rules and uses strict comparisons with === avoids a whole class of logic and security errors.

Dynamically typed, but not typeless

In PHP you do not have to specify a type for $preis = 19.90;. PHP recognises that it is a floating-point number. If the value is later combined with a string, PHP converts it. This behaviour is called type juggling, that is, automatic type conversion. It makes getting started easy but can lead to surprising results.

For decision-makers this is relevant because many older PHP applications were written without type declarations. Such code is harder to review and modernise. Newer projects use types consistently, which makes errors visible earlier and lets static analysis with tools such as PHPStan or Psalm become truly effective.

The PHP types at a glance

The PHP manual names nine internal types that any expression can have. They are usually divided into three groups:

Group Types Example
Scalar types bool, int, float, string true, 42, 3.5, "Text"
Compound types array, object, callable [1, 2], new DateTime()
Special types null, resource null, an opened file

For type checks, the manual recommends the is_*() functions such as is_int(). For inspection, get_debug_type() is suitable; since PHP 8.0.0 it returns the commonly used type names, whereas the older gettype() returns, for example, "integer" and "double".

Besides automatic conversion there is explicit conversion: with a cast such as (int) $wert or with settype(), the code itself decides which type a value should have. This is common for input from forms, because everything that arrives via $_GET or $_POST is initially a string or an array. A cast does not check whether the value made sense, though: "abc" silently becomes 0. For input, a genuine check with filter_var() is therefore usually the better choice.

Classes and interfaces are also types in PHP. A function can therefore require that a parameter be an object of a particular class or satisfy a particular interface. How classes, interfaces and enums work together is explained on the page Object orientation in PHP.

Type declarations: from PHP 7.1 to 8.3

Type declarations define which values a function accepts and returns. The type system has grown over many versions. According to the manual's changelog, these capabilities were added, among others:

Version New
7.1.0 nullable types (?int), void, iterable
7.2.0 object
7.4.0 typed class properties
8.0.0 union types (several permitted types in one declaration), mixed, return type static
8.1.0 intersection types (A&B), return type never
8.2.0 DNF types, literal type true, null and false as standalone types
8.3.0 typed class, interface, trait and enum constants

DNF stands for disjunctive normal form and allows combinations such as (A&B)|null. Anyone planning PHP versions can also see from this table which language features an application requires.

strict_types: conversion or TypeError

Without further declaration, PHP works in coercive mode. A function with int parameters then also accepts 1.5 and turns it into 1; since PHP 8.1 this lossy conversion is marked as deprecated. With declare(strict_types=1) at the top of the file, PHP throws a TypeError instead.

<?php
declare(strict_types=1);

function summe(int $a, int $b): int
{
    return $a + $b;
}

echo summe(1, 2);      // 3
echo summe(1.5, 2.5);  // TypeError, instead of silently 3

Two subtleties are important. First, strict_types applies to calls made from the file in which it appears, not to the functions defined there. If a file without strict_types calls the same function, values are converted. Second, an integer where a float is expected is permitted even in strict mode.

Loose and strict comparisons

The == operator compares after type conversion; === compares value and type without conversion. In most cases the manual recommends === and !==, because the conversion rules are not always obvious.

<?php
var_dump(0 == "a");    // PHP 7: true,  PHP 8: false
var_dump("1" == "01"); // true, both are numeric strings
var_dump("1" === "01");// false, same type, but a different string
var_dump(1 == true);   // true
var_dump(1 === true);  // false

With PHP 8.0, comparisons between numbers and non-numeric strings became more sensible. Older applications can behave differently after the upgrade, for example in switch blocks. The match expression, likewise introduced with PHP 8.0, always compares strictly. How loose comparisons lead to security vulnerabilities, for example in token or password checks, is described in the article Type juggling: loose comparisons as a security risk.

Types, upgrades and PHP2Shell

A strict type system makes application code more robust, but it does not protect against flaws in the interpreter itself. The PHP2Shell vulnerabilities CVE-2026-109618 and CVE-2026-109619 lie in PHP's memory and request handling. They are fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35; afterwards all PHP processes must restart.

The type system still plays a role, namely during the upgrade. Applications still stuck on PHP 8.1 or older no longer receive official updates from the PHP project. Well-typed code is much easier to move to a supported version, because tools find incompatibilities early. For older projects, our migration to a supported PHP version can help.

Tip

Enable strict_types step by step in new and reworked files. That way type safety grows without old code breaking all at once.

How does PHP SOS help?

  • We check which PHP version your application needs at a minimum and compare that with the fixed versions.
  • We run static analysis over the code and flag places with loose comparisons in security-relevant checks.
  • We plan upgrades so that changed comparison rules are tested beforehand.
  • We apply the PHP update, restart all workers and document the status.

Frequently asked questions

Is PHP strongly or weakly typed?

PHP is dynamically typed: the type of a variable is only determined at runtime. Without strict_types, PHP converts values when needed. With type declarations and strict_types, however, code can be made very strict.

What does declare(strict_types=1) do?

It switches off the automatic conversion of scalar values for calls made from that file. If a value does not match the declared type, PHP throws a TypeError. The only exception is an integer where a float is expected.

Why is 0 == "a" false in PHP 8 but true in PHP 7?

Up to PHP 7, a non-numeric string compared with a number was converted to a number, so "a" became 0. Since PHP 8.0, PHP no longer compares numerically in this case, and the result is false.

Do types protect against vulnerabilities in PHP itself?

No. Types prevent errors in your application code. Flaws in the interpreter such as PHP2Shell are closed only by a PHP update. Modern type features do require current PHP versions, though, and are therefore a good reason to upgrade.

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