PHP

744 readers
2 users here now

Welcome to /c/php! This is a community for PHP developers and enthusiasts to share and discuss anything related to PHP. From the latest updates and tutorials, to your burning questions and amazing personal projects, we welcome all contributions.

Let's foster an environment of respect, learning, and mutual growth. Whether you're an experienced PHP developer, a beginner, or just interested in learning more about PHP, we're glad to have you here!

Let's code, learn, and grow together!

founded 2 years ago
MODERATORS
1
 
 

cross-posted from: https://chrastecky.dev/post/24

Changing a Readonly Property

So, you know how readonly properties are, well… read-only? Turns out they’re not!

I stumbled upon this mechanism just as PHP started deprecating it — but hey, if you ignore the deprecation warnings, you can still use it up until PHP 9!

A bit of theory first: readonly properties can only be assigned inside a class constructor. After that, they’re supposed to be immutable.

final readonly class ReadonlyClass
{
    public string $someProp;

    public function __construct()
    {
        $this->someProp = 'unchangeable!';
    }
}

The only official way to set such a property outside the constructor is via reflection — but even then, only if the property hasn’t been initialized yet:

final readonly class ReadonlyClass
{
    public string $someProp;
}
$test = new ReadonlyClass();
$reflection = new ReflectionClass(ReadonlyClass::class)->getProperty('someProp');
$reflection->setValue($test, 'changed once!');
var_dump($test->someProp);
$reflection->setValue($test, 'changed twice?');

This produces the predictable result:

string(13) "changed once!"

Fatal error: Uncaught Error: Cannot modify readonly property ReadonlyClass::$someProp

Changing It Multiple Times

Enough stalling — let’s dive in! The magical object that can modify a readonly property (and much more) is ArrayObject.

Normally, you’d use ArrayObject to wrap an array. But it also accepts any object as the backing value — and that’s where the fun begins. Once you know how PHP stores properties internally (which is actually pretty simple), chaos follows.

Let’s start with this class:

final readonly class ReadonlyClass
{
    public string $someProp;
    private string $somePrivateProp;
    protected string $someProtectedProp;

    public function __construct()
    {
        $this->someProp = 'unchangeable?';
        $this->somePrivateProp = 'unchangeable?';
        $this->someProtectedProp = 'unchangeable?';
    }

    public function getSomePrivateProp(): string
    {
        return $this->somePrivateProp;
    }

    public function getSomeProtectedProp(): string
    {
        return $this->someProtectedProp;
    }
}

Now we create an instance and wrap it in an ArrayObject:

$instance = new ReadonlyClass();
$arrayObj = new ArrayObject($instance);

And now comes the fun part:

// simply use the property name for public properties
$arrayObj['someProp'] = 'changeable public!';
// use "\0[FQN]\0[Property name]" for private properties
$arrayObj["\0ReadonlyClass\0somePrivateProp"] = 'changeable private!';
// use "\0*\0[Property name]" for protected properties
$arrayObj["\0*\0someProtectedProp"] = 'changeable protected!';

var_dump($instance->someProp, $instance->getSomePrivateProp(), $instance->getSomeProtectedProp());

This prints:

string(18) "changeable public!"
string(19) "changeable private!"
string(21) "changeable protected!"

And just like that, you’ve changed an unchangeable property. You can modify it as many times as you want. So… what other arcane tricks are possible?

Changing an Enum Value

Enums are basically fancy objects that represent a specific named instance — optionally with a value. The key difference from old userland implementations is that PHP guarantees every enum case is a unique instance that’s always equal to itself, no matter where it’s referenced from.

In other words, an enum is really just a class, and ->value or ->name are plain properties.

enum MyEnum: string {
    case A = 'a';
    case B = 'b';
}

$arrayObj = new ArrayObject(MyEnum::A);
$arrayObj['value'] = 'b';
$arrayObj['name'] = 'C';

var_dump(MyEnum::A->value);
var_dump(MyEnum::A->name);

This prints exactly what you’d expect after reading the previous examples:

string(1) "b"
string(1) "C"

Even more amusing: Running var_dump(MyEnum::A); now prints enum(MyEnum::C).

It won’t actually make it equal to another enum case, but if you use the value somewhere and reconstruct it using MyEnum::from(), you’ll get back MyEnum::B.

If you try to serialize and deserialize it, you’ll get an error — because MyEnum::C doesn’t exist:

var_dump(MyEnum::from(MyEnum::A->value));
var_dump(unserialize(serialize(MyEnum::A)));

The first prints enum(MyEnum::B), while the second throws a warning: Undefined constant MyEnum::C.

Breaking Types

ArrayObject is so powerful that even the type system trembles before it. Types? Mere suggestions!

final class TestTypedClass
{
    public string $str = 'test';
    public bool $bool = true;
    public int $int = 42;
}

$instance = new TestTypedClass();
$arrayObj = new ArrayObject($instance);

$arrayObj['str'] = 5;
$arrayObj['bool'] = 'hello';
$arrayObj['int'] = new stdClass();

var_dump($instance->str, $instance->bool, $instance->int);

Output:

int(5)
string(5) "hello"
object(stdClass)#3 (0) {
}

So if you ever thought “Hmm, this boolean could really use more than two possible values” — now you know how!

Dynamic Properties Everywhere

Some internal classes like Closure, Generator, and DateTime disallow dynamic properties. Nevermore!

$closure = fn () => true;
$arrayObject = new ArrayObject($closure);
$arrayObject['test'] = 'hello';

var_dump($closure->test);
// prints string(5) "hello"

Crashing PHP

And finally — my favourite one! Ever wanted to cause a segmentation fault? Try this:

$exception = new Exception("Hello there!");
$arrayObject = new ArrayObject($exception);
$arrayObject["\0Exception\0trace"] = -1;

var_dump($exception->getTraceAsString());

That gave me one beautiful Segmentation fault (core dumped)!

So, how did you like these all-powerful ArrayObject shenanigans?

2
3
4
 
 

(Nextcloud is written in PHP)

cross-posted from: https://mander.xyz/post/40795258

5
 
 

Following conversations with SerheyDolgushev we decided that it doesn't make sense to maintain independent forks of the Tolerant PHP Parser. -- https://github.com/phan/phan-tolerant/pull/15

6
 
 

Setting up xdebug with swoole could be a bit of a hustle especially if application is containerized and you are running swoole and xdebug in a docker container with IDE on the host.

Here’s what worked for me.

Here I’m using hyperf framework, but I think issues and instructions should be similar for other swoole based frameworks (mezzio, resonance etc). Swoole 5.0.1 + PHP 8.1 natively support xdebug.

7
 
 

I'm using 8.1/8.2 for production ...but I do have some pet projects in 8.4 that I might upgrade

8
9
 
 

Cool article

10
11
 
 

Ohh yeah, good old days when we had to ftp/sftp into the server to upload the PHP files.

I recommend subscribing to his channel.

12
 
 

cross-posted from: https://chrastecky.dev/post/16

Starting with PHP 8.5, you'll be able to do the following:

 public function __construct(
    final public string $someProperty,
) {}

This wasn't possible before, as promoted properties couldn't be declared final.

Perhaps the more interesting part is that you can now omit the visibility modifier if you include final. In that case, the property will default to public:

 public function __construct(
    final string $someProperty, // this property will be public
) {}

Personally, I’m not a fan of this behavior — I prefer explicit over implicit. Fortunately, it can be enforced by third-party tools like code style fixers. Still, I would have preferred if the core required the visibility to be specified.

What do you think? Do you like this change, or would you have preferred a stricter approach?

13
 
 

cross-posted from: https://chrastecky.dev/post/13

This change is quite straightforward, so this won’t be a long article. PHP 8.5 adds support for annotating non-class, compile-time constants with attributes. Compile-time constants are those defined using the const keyword, not the define() function.

Attributes can now include Attribute::TARGET_CONSTANT among their valid targets. Additionally, as the name suggests, Attribute::TARGET_ALL now includes constants as well. The ReflectionConstant class has been updated with a new method, getAttributes(), to support retrieving these annotations.

One particularly useful aspect of this change is that the built-in #[Deprecated] attribute can now be applied to compile-time constants.

As promised, this was a short post, since the change is relatively simple. See you next time—hopefully with a more exciting new feature in PHP 8.5!

14
 
 

cross-posted from: https://chrastecky.dev/post/15

PHP has long had a levenshtein() function, but it comes with a significant limitation: it doesn’t support UTF-8.

If you’re not familiar with the Levenshtein distance, it’s a way to measure how different two strings are — by counting the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into another.

For example, the following code returns 2 instead of the correct result, 1:

var_dump(levenshtein('göthe', 'gothe'));

There are workarounds — such as using a pure PHP implementation or converting strings to a custom single-byte encoding — but they come with downsides, like slower performance or non-standard behavior.

With the new grapheme_levenshtein() function in PHP 8.5, the code above now correctly returns 1.

Grapheme-Based Comparison

What makes this new function especially powerful is that it operates on graphemes, not bytes or code points. For instance, the character é (accented 'e') can be represented in two ways: as a single code point (U+00E9) or as a combination of the letter e (U+0065) and a combining accent (U+0301). In PHP, you can write these as:

$string1 = "\u{00e9}";
$string2 = "\u{0065}\u{0301}";

Even though these strings are technically different at the byte level, they represent the same grapheme. The new grapheme_levenshtein() function correctly recognizes this and returns 0 — meaning no difference.

This is particularly useful when working with complex scripts such as Japanese, Chinese, or Korean, where grapheme clusters play a bigger role than in Latin or Cyrillic alphabets.

Just for fun: what do you think the original levenshtein() function will return for the example above?

var_dump(levenshtein("\u{0065}\u{0301}", "\u{00e9}"));
15
2
PHP 8.3.22 (www.php.net)
submitted 5 months ago* (last edited 5 months ago) by cm0002@lemmy.world to c/php@programming.dev
16
17
18
19
20
 
 

June 17, 2025

  • 13:30–19:00 CET/CEST
  • 07:30–13:00 EST/EDT
  • 11:30–17:00 UTC
21
 
 

Internals link: https://externals.io/message/127120

Note: I am not the RFC author.

22
 
 

Note: I am not the RFC author.

23
24
25
 
 

I noticed that if you have too few pm_children set then some requests hang until timeout. This surprised me - I'd expect an immediate error, but it's more like a tarpit! For ages I was thinking my server was not performant, then I noticed via top that it wasn't doing or waiting while the browser was.

I have two questions:

  1. If you have pm_max_children=1 and you occupy that and submit another request, what actually happens? (I'm proxying through nginx.) HTTP doesn't have a "40_ Come back later".

  2. (if life deals you lemons...) if you can generate a tarpit that doesn't use server resources, this could be quite useful to know about too!

view more: next ›