__set on a class that declares the property does nothing

__set is only called for inaccessible or undefined properties, so declaring the property you meant to intercept disables the magic silently.

class Bag
{
    public array $data = [];
    private string $name = '';

    public function __set(string $k, $v): void
    {
        $this->data[$k] = $v;
    }
}

$b->anything = 1;   // __set runs
$b->data = [];      // does NOT — public, so accessible
$b->name = 'x';     // DOES — private, so inaccessible
                    // from outside

The private case is the one that confuses everybody: assigning to a private property from outside the class is inaccessible, so the magic method fires and the real property is untouched, leaving two things with the same name holding different values. Typed properties made this worse, because an unset typed property is also considered uninitialised rather than undefined, and the interaction between the two rules is not something anybody should have to reason about at three in the morning.