Skip to content

Support for Php

corneliuhoffman edited this page Jan 12, 2026 · 3 revisions

This document summarizes the PHP language improvements added to Opengrep in 2025.

Supported features

Here are the PHP features now supported, organized by PHP version:

Feature Support Notes
PHP 7.1
- Union types in catch Note
- Class constant visibility Note
PHP 7.4
- Arrow functions Note
PHP 8.0
- Union types Note
- Match expressions Note
- Null coalesce throw Note
- Static return type
PHP 8.1
- Enums Note
- First-class callable syntax Note
PHP 8.2
- Readonly classes Note
- DNF types Note
PHP 8.3
- Dynamic constant fetch Note
PHP 8.4
- Property hooks Note
- Asymmetric visibility Note
Infrastructure
- Thread-safe lexer Note
- Interpolated string parsing Note

Union types

PR #201

The Menhir parser was missing union types. Union types in the tree-sitter parser are translated to tuples, and the Menhir parser now follows the same approach for consistency.

function processInput(int|string $value): int|null {
    if (is_string($value)) {
        return strlen($value);
    }
    return $value > 0 ? $value : null;
}

Arrow functions

PR #205

Added PHP 7.4 arrow function syntax to the Menhir parser. PHP arrow functions (fn($x) => $x + 1) are syntactically different from Facebook's HHVM extension syntax ($x ==> $x + 1), but both are represented the same way in the AST.

$fn = fn($x) => $x + 1;

// Equivalent to:
$fn = function($x) {
    return $x + 1;
};

Interpolated strings

PR #296

Fixed a bug in the tree-sitter parser where interpolated strings were incorrectly parsed as literal strings. For example:

"convert $filename"

was being parsed literally as a string instead of recognizing $filename as an interpolated variable.

Null coalesce with throw

PR #296

The Menhir parser was missing the ?? token for PHP 8.0's null coalesce operator. Additionally, throw was modified to be parsed as an expression so it can be used with ??.

return shell_exec($command)
    ?? throw new RuntimeException('Failed to execute command');

Match expressions

PR #306

Added PHP 8.0 match expressions to the Menhir parser. Also fixed an issue in switch (and match) that prevented ellipses in the body. The following patterns are now valid:

switch($F) {
    ...
}

match ($F) {
    ...
}

Enums

PR #306

Added PHP 8.1 enum definitions including backed enums:

enum Status: string {
    case Active = 'active';
    case Pending = 'pending';
    case Inactive = 'inactive';
}

Multi-catch exception handling

PR #529

PHP 7.1 multi-catch syntax allowing multiple exception types in a single catch block:

try {
    riskyOperation();
} catch (InvalidArgumentException | TypeError | ValueError $e) {
    handleError($e);
}

Class constant visibility

PR #529

PHP 7.1 visibility modifiers for class constants:

class Config {
    public const PUBLIC_CONST = 1;
    protected const PROTECTED_CONST = 2;
    private const PRIVATE_CONST = 3;
}

First-class callable syntax

PR #529

PHP 8.1 first-class callable syntax creates closures from callables:

$fn = strlen(...);           // Closure::fromCallable('strlen')
$fn = $obj->method(...);     // Closure::fromCallable([$obj, 'method'])
$fn = Foo::bar(...);         // Closure::fromCallable([Foo::class, 'bar'])

Note:

In Opengrep patterns, ... retains its ellipsis meaning for pattern matching. In particular in order to wrote a pattern that matches a "callable ..." you need to use Closure::fromCallable

Readonly classes

PR #529

PHP 8.2 readonly classes where all properties are implicitly readonly:

readonly class ImmutablePoint {
    public int $x;
    public int $y;
}

DNF types

PR #529

PHP 8.2 Disjunctive Normal Form types combine union and intersection types:

function process((Countable&Iterator)|null $input): (A&B)|C {
    // ...
}

Dynamic constant fetch

PR #529

PHP 8.3 dynamic class constant fetch allows accessing constants using variable names:

$name = 'VERSION';
echo Config::{$name};

Property hooks

PR #529

PHP 8.4 property hooks allow defining custom get/set behavior directly on properties:

class User {
    // Block syntax
    public string $name {
        get { return $this->name; }
        set { $this->name = strtoupper($value); }
    }

    // Arrow syntax
    public int $age {
        get => $this->age;
        set => max(0, $value);
    }
}

Asymmetric visibility

PR #529

PHP 8.4 asymmetric visibility allows different visibility for reading and writing properties:

class Counter {
    public private(set) int $count = 0;      // Public read, private write
    public protected(set) string $name = ""; // Public read, protected write
}

Thread-safe lexer

PR #123

The PHP primary parser lexer was made thread-safe as part of the transition from parmap to domainslib. This enables parallel file processing without race conditions and improves performance on multi-core systems.