hoa/view
Hoa\View is a lightweight PHP view layer for composing and rendering templates through view containers. It provides a simple abstraction for presenting data in Hoa-based applications, though the package is largely legacy and minimally maintained.
hoa/view is a contract, not a template engine. It ships an interface (surfaced as Hoa\View\View, with its render() method) plus the usual Hoa exception scaffolding — the whole package is a handful of files under Hoa/View/. Read Hoa/View/View.php first; that one file is the entire API you will code against.
Install (or, more realistically, inspect before installing):
composer require hoa/view
Confirm the interface signature in vendor/hoa/view/View.php — the last tagged release is from 2017, before return types were common, so expect an untyped render().
Implement it. The smallest useful class is a plain-PHP template view:
use Hoa\View\View;
final class PageView implements View
{
public function __construct(
private string $template,
private array $data = []
) {}
public function render()
{
extract($this->data, EXTR_SKIP);
ob_start();
include $this->template;
return ob_get_clean();
}
}
Wire it at the edge of your app (front controller or response factory) and let render() produce the body. That is the intended first use case: a stable, minimal "thing that can render itself" type that controllers can return without knowing whether the body comes from PHP, Twig, or a string.
Adapter over a real engine. The only durable pattern with this package is to treat Hoa\View\View as a port and put your actual engine behind it. A TwigView, BladeView, or PlatesView implementing render() lets you swap templating later without touching call sites — which matters here because the interface is the only thing the ecosystem agrees on.
Constructor-injected data, pure render(). Build the view with its data, then render once. Keeping render() free of side effects (no writes, no header calls) makes it trivially testable with a string assertion and makes it safe to render twice in a caching layer.
Composite/aggregate views. If you need a layout-plus-partials, compose inside your implementation rather than nesting engines: accept an array of View instances in the layout's constructor and concatenate their render() output in render(). This keeps the dependency graph one-directional and avoids the "which partial escaped this variable" problem.
Controller returns a view, not a string. Have controllers return View; have the response layer call render() once and set Content-Type there. This is the pattern that makes the package worth having in a Hoa-based stack: your dispatcher code stops caring about output format.
Stream-friendly output. Hoa's ecosystem is stream-centric. If your installed version's render() returns a stream-like value rather than a string, keep it open through the response write and close it in a finally — closing early is the most common cause of truncated output in Hoa apps.
hoa/consistency, hoa/exception) are equally frozen. There is no PHP 8.1+ support guarantee. Adopt only if you are already inside a Hoa stack; otherwise Twig, Blade, or Plates is the correct choice and this package gives you nothing they don't.htmlspecialchars($v, ENT_QUOTES, 'UTF-8') — or wrap an engine that does. Assuming "the view library handles XSS" is the fastest way to ship a hole.Hoa\Exception\Exception subclasses, so catch (\Exception $e) around render() will not do what you expect in every path. Catch the Hoa exception explicitly and rethrow as your domain error at the boundary.render() that returns '' is almost always an ob_start() without a matching ob_get_clean() — usually from a template that itself starts buffering, or from an exception thrown between the two calls. Assert non-empty output in tests; don't debug it in a browser.Hoa\Core autoloader; the Composer-era releases use PSR-4. If classes vanish after a composer update, check vendor/hoa/view/composer.json for the autoload block before assuming a bug in your code.Hoa\View\View and keep your class final, readonly-friendly, and free of static state. That way, when you eventually migrate off the package, the change is a one-line implements swap.How can I help you explore Laravel packages today?