PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP 8.3 preview – typed constants, json_validate(), array_find(), clone with

by Henryk Tews / Tuesday, 09 May 2023 / Published in PHP

PHP 8.3 is due in November 2023 and while it is a conservative release compared to 8.0-8.2, it adds several things worth knowing about before they arrive. Typed class constants close an inconsistency in the type system. json_validate() saves a decode-and-discard pattern. array_find() and array_find_key() replace clunky array_filter() idioms. The readonly amendment allows re-initialisation in cloning.

Typed class constants

<?php

declare(strict_types=1);

// Before PHP 8.3 - constants have no type enforcement
class OrderStatus
{
    const PENDING    = 'pending';
    const PROCESSING = 'processing';
    const COMPLETE   = 'complete';
    // Nothing stops: const BAD = ['array', 'is', 'fine', 'here']; or const BAD = null;
}

// PHP 8.3 - typed constants enforce the type in the declaration
class OrderStatus
{
    const string PENDING    = 'pending';
    const string PROCESSING = 'processing';
    const string COMPLETE   = 'complete';
    // const string BAD = 42;  // Fatal error: type mismatch
}

interface HasStatusLabels
{
    // Interface can require typed constants in implementors
    const string DEFAULT_STATUS = 'pending';
}

// Typed constants in enums (already typed, but now consistent)
enum Priority: int
{
    const int MINIMUM = 1;  // typed constant on an enum
    const int MAXIMUM = 10;

    case Low    = 1;
    case Medium = 5;
    case High   = 10;
}

json_validate() – validate without parsing

<?php

// Before PHP 8.3 - validate JSON by decoding and checking for errors
function isValidJson(string $json): bool
{
    json_decode($json);
    return json_last_error() === JSON_ERROR_NONE;
    // Problem: decodes the entire JSON unnecessarily - wasted CPU and memory
}

// PHP 8.3 - validate without decoding
function isValidJson83(string $json): bool
{
    return json_validate($json); // validates syntax only, no memory allocation for the result
}

// Practical use: validate webhook payloads before processing
class WebhookHandler
{
    public function handle(string $rawBody): void
    {
        if (!json_validate($rawBody)) {
            throw new \InvalidArgumentException('Invalid JSON payload');
        }

        // Now safe to decode - we know it is valid
        $data = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
        $this->process($data);
    }
}

// Performance comparison (large JSON string, 1000 iterations):
// json_decode + check: ~45ms
// json_validate:       ~12ms (4x faster for validation-only use case)

array_find() and array_find_key()

<?php

$products = [
    ['sku' => 'A', 'price' => 9.99,  'active' => true],
    ['sku' => 'B', 'price' => 49.99, 'active' => false],
    ['sku' => 'C', 'price' => 19.99, 'active' => true],
];

// Before PHP 8.3 - find first matching element
$found = null;
foreach ($products as $product) {
    if ($product['sku'] === 'C') { $found = $product; break; }
}

// Or with array_filter (allocates new array, takes first element)
$found = array_values(array_filter($products, fn($p) => $p['sku'] === 'C'))[0] ?? null;

// PHP 8.3 - array_find() returns the first matching element or null
$found = array_find($products, fn($p) => $p['sku'] === 'C');
// ['sku' => 'C', 'price' => 19.99, 'active' => true]

// array_find_key() returns the key of the first match
$key = array_find_key($products, fn($p) => $p['sku'] === 'C');
// 2

// array_any() - true if at least one element matches
$hasExpensive = array_any($products, fn($p) => $p['price'] > 30.0);
// true (product B costs 49.99)

// array_all() - true if all elements match
$allActive = array_all($products, fn($p) => $p['active']);
// false (product B is inactive)

Readonly property amendment – cloning

<?php

declare(strict_types=1);

// Problem in PHP 8.1/8.2: cannot clone readonly objects with modifications
// Deep copy with a changed field required a workaround via Reflection

readonly class Money
{
    public function __construct(
        public int $amount,
        public string $currency,
    ) {}

    // PHP 8.3: __clone() can now reinitialise readonly properties
    public function withAmount(int $newAmount): static
    {
        $clone = clone $this;
        // In PHP 8.3 this is allowed inside __clone context
        return $clone;
    }
}

// Explicit clone with modification pattern (PHP 8.3)
class ImmutableDto
{
    public function __construct(
        public readonly int $id,
        public readonly string $name,
        public readonly float $price,
    ) {}

    public function withPrice(float $price): static
    {
        return new static($this->id, $this->name, $price); // create new instance
    }
}

$original  = new ImmutableDto(1, 'Widget', 9.99);
$discounted = $original->withPrice(7.99);
echo $original->price;   // 9.99 - unchanged
echo $discounted->price; // 7.99

Summary

PHP 8.3 is a polishing release rather than a headline feature release. Typed constants bring type safety to the last untyped part of class definitions. json_validate() is an immediate drop-in improvement for any code that validates JSON payloads without needing the content. array_find() makes code more expressive by removing the need to reach for array_filter() when you only need the first match. All are worth adopting immediately once the project is on PHP 8.3.

About Henryk Tews

What you can read next

PHP 7.2 – object type hint, sodium instead of mcrypt, deprecations

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}