Piszemy trait, który pozwoli nam zmienić dowolną klasę na tablicę asocjacyjną albo jej JSONowy format. Do dzieła.

Struktura wygląda tak:

trait toArray {
    public function toArray(){
       //(...)
    }

    public function toJSON(){
        //(...)
    }
}
class Person {
    use toArray;
    public function __construct(public $name, public $age)
    {
        
    }
}

class Item {
    use toArray;
    public function __construct(public $name, public $price)
    {
        
    }
}

Musimy napisać funkcję, która zamieni pola obiektu na tablicę asocjacyjną:

public function toArray(){
        $vars = get_object_vars($this);
        $output_array = [];
        foreach($vars as $key => $value){
            if(!is_null($value))
                $output_array[$key] = $value;
        }
        return $output_array;
    }

Pola pozyskujemy z get_object_vars, przekazując mu $this. Dalej działamy standardowo. Możemy już napisać metodę toJSON:

public function toJSON(){
        return json_encode([__CLASS__ => $this->toArray()]);
    }

Teraz możemy wypróbować działanie naszych metod:

$john = new Person("John", 22);
print_r($john->toArray());
//Array ( [name] => John [age] => 22 )
echo $john->toJSON();
//{"Person":{"name":"John","age":22}}

Zawsze też warto mieć z tyłu głowy myślenie, czy aby nie wynajdujemy koła na nowo i nie mamy już gotowych narzędzi, aby osiągnąć to, czego chcemy:

trait toArray {
    public function toArray(){
        return (array)get_object_vars($this);
    }

    public function toJSON(){
        return json_encode([__CLASS__ => (array)get_object_vars($this)]);
    }
}

Z drugiej strony – warto umieć tworzyć logikę różnych działań samemu, nie polegać całkowicie na gotowych rozwiązaniach, bo te mogą mieć jakieś wady.