CP is 'current page' = the final product
having a single “current page” object that accumulates the final product is a clean mental model. Just keep it focused so it doesn’t turn into a “god object.”
Keep CP small and intentional
What CP should do
• Hold the final artifact (e.g., a render tree or structured arrays).
• Provide append/update methods (e.g., addPart(place, id, result)).
• Expose a finalize/render step.
What CP should NOT do
• Parse input, validate, or apply business logic.
• Contain per-part transient state.
• Know about controllers’ internals.
IDEA
In ParentApplier (or your controllers), avoid touching arrays directly:
public function place_result(): void {
if (!$this->id) return;
$this->CP->add($this->place, $this->id, $this->result);
}
final class Page {
private array $parts = []; // [$place][$id] = $result
public function add(string $place, string $id, $result): void {
$this->parts[$place][$id] = $result;
}
public function getParts(): array { return $this->parts; }
public function render(callable $renderer): string {
return $renderer($this->parts);
}
}
Benefits of this approach
• Intuitive: “Everything adds to the current page.”
• Testable: Assert Page::getParts() without rendering.
• Extensible: You can support multi-page later with a Site object that holds many Pages.
• Safer: CP owns the output; controllers can’t accidentally corrupt structure.
Pitfalls to avoid
• Don’t let CP store parser flags (lineBlockKey, etc.).
• Don’t write directly to CP->parts[...] from everywhere; always go through add().
• Don’t overuse globals—pass Page $cp explicitly where needed.
When to split further
If you later need more structure:
• Page (final data)
• Renderer (HTML serializer)
• AssetBundle (collected CSS/JS)
• Diagnostics (warnings/errors per part)
using CP as “current page” = good idea. Rename it to something output-focused (e.g., Page), give it a tiny, clear API (add, render), and keep all parsing/controller logic outside. That will make the system both intuitive and maintainable.