CodeBlanks Logo Icon CodeBlanks

Challenge - Reflection app

PHP course · Expert · Lesson 51/54

Build an automatic serializer that works with reflection and attributes.

Serialization with reflection and attributes

In this lesson you write the ReflectionSerializer class. ReflectionClass reads the structure of a class; getProperties() lists its properties. The getAttributes() call on each property returns the attached attributes as a ReflectionAttribute array.

Attributes are classes marked with #[Attribute]. An attribute like #[Field("id")] tells the serializer which key to use. newInstance() builds the attribute instance so you can read its value. You declare which properties serialize with attributes instead of hard-coding the field list.

Reading attributes

The typical attribute reading flow is: filter with $prop->getAttributes(Field::class), then build the instance with $attrs[0]->newInstance(). You read fields like name off the instance.

This pattern is declarative: serialization behavior lives next to the property, not in a separate map. Adding a new field means writing #[Field("new")] above it; the serializer stays untouched. This is a textbook application of the open-closed principle.

Exercise Write the ReflectionSerializer class

The starting code already has the ETL pipeline. Now define the Field attribute class marked with #[Attribute]; it should expose public string $name = "" as a promoted property. Then write the Product class: id and name properties tagged with #[Field("id")] and #[Field("name")].

Finally write ReflectionSerializer::serialize(object $obj): array. Read the class with new ReflectionClass($obj), iterate getProperties(), call getAttributes(Field::class) on each and skip when empty. Build the Field instance with newInstance(); if name is empty use the property name, otherwise use the name value. The demo prints Product(7, "kale") via json_encode.

Show example solution
:serialize use new ReflectionClass($obj), iterate getProperties(), call getAttributes(Field::class) on each, use newInstance() to build the Field instance and use its name as the key. Write the demo Product class too, then print the result with json_encode.
Example solution:
<?php
// ETL pipeline + Reflection: write ReflectionSerializer

function readRows(array $rows): Generator {
    foreach ($rows as $row) {
        yield $row;
    }
}

function transform(iterable $rows, callable $fn): Generator {
    foreach ($rows as $row) {
        yield $fn($row);
    }
}

function pipeline(array $rows, callable $fn): Generator {
    yield from transform(readRows($rows), $fn);
}

function loadAll(iterable $rows): array {
    $out = [];
    foreach ($rows as $row) {
        $out[] = $row;
    }
    return $out;
}

#[Attribute]
class Field {
    public function __construct(public string $name = "") {}
}

class Product {
    public function __construct(
        #[Field("id")] public int $id,
        #[Field("name")] public string $name,
    ) {}
}

class ReflectionSerializer {
    public function serialize(object $obj): array {
        $rc = new ReflectionClass($obj);
        $out = [];
        foreach ($rc->getProperties() as $prop) {
            $attrs = $prop->getAttributes(Field::class);
            if (empty($attrs)) continue;
            $field = $attrs[0]->newInstance();
            $key = $field->name !== "" ? $field->name : $prop->getName();
            $out[$key] = $prop->getValue($obj);
        }
        return $out;
    }
}

// Demo:
$p = new Product(7, "kale");
$s = new ReflectionSerializer();
echo json_encode($s->serialize($p), JSON_UNESCAPED_UNICODE) . "\n";

Test yourself

  1. Match reflection concepts

    ReflectionClass
    Reads class structure
    getAttributes
    Returns attribute list
    newInstance
    Creates attribute instance
    getValue
    Returns property value
  2. What does ReflectionProperty::getAttributes return?

    • Only the property name as a string
    • The property value directly
    • An array of ReflectionAttribute (may be empty)
    • A new ReflectionClass instance
    Show answer

    An array of ReflectionAttribute (may be empty) — Correct. getAttributes returns an array of ReflectionAttribute; the array is empty when no attribute is present.

  3. Complete the ReflectionClass call

    $rc = ____($obj);
    Show answer
    $rc = new ReflectionClass($obj);

All lessons in Expert