marko/view
marko/view is the view layer for the Marko PHP framework, offering template loading, layout and partial composition, and a clean API for passing data from controllers to rendered HTML output while keeping presentation logic simple and framework-native.
marko/view is a contracts-only package: it ships interfaces and value objects, not a working template engine. You almost never install it directly — it arrives as a transitive dependency of a concrete renderer (e.g. a Blade/Twig/Plates adapter for Marko) or of marko/framework itself. Your first move is therefore to read the interfaces before reading any implementation: open the package's src/ directory and note the shape of the view factory, the view instance, and any renderer/engine contract. That shape is the contract every Marko view implementation must satisfy, so it is the fastest way to understand how rendering is wired into the container.
Your first real use case is almost always one of two things: (1) type-hinting against the contract in your own services so your code is engine-agnostic, or (2) wrapping a third-party engine you already use (Twig, Blade via jenssegers/blade, etc.) behind the provided interfaces when no Marko adapter exists. Start by binding the concrete implementation in a service provider, then resolve and render one template end-to-end before touching anything else. If the package exposes a ViewFactory-style contract, get that resolving from the container first — everything else builds on it.
Because the repo is a read-only split ([READ-ONLY]), file issues and PRs against marko-php/marko, not here. Watch the main repo's release tags to know when the contract surface changes.
Type-hint the interface, resolve the implementation. The whole point of this package is that your application code should depend on ViewFactoryInterface (or whatever the equivalent is named) and never on a concrete engine. Constructor-inject the factory into controllers, mailers, or other services; let Marko's container resolve the concrete binding you configured.
Adapter/wrapper pattern for third-party engines. When you need to plug in Twig, Blade, Latte, or a hand-rolled engine, implement the package's engine/renderer contract and delegate to the real engine internally. Keep the adapter thin: translate Marko's render call into the engine's render(), map the returned string back, and let exceptions bubble or wrap them in whatever view exception the package defines. This keeps the rest of your app ignorant of the engine choice and lets you swap engines later without touching call sites.
Factory + instance split. Most Marko view contracts mirror a two-tier design: a factory that knows where templates live and how to locate them, and a per-render view instance that holds data and knows how to render itself. Follow that split in your own code — resolve the factory once (often a singleton), create view instances per render, and avoid caching view instances across requests since they carry bound data.
Testing with the contract. Because this package has essentially no runtime logic, mocking it is trivial. In unit tests, bind a fake implementation of the factory interface that returns a stub view whose render() returns a canned string. This is the cleanest way to assert that a controller rendered the right template with the right data without spinning up a real engine.
Composition over inheritance when extending. If you need shared behaviour across several adapters (e.g. escaping, path resolution, caching), wrap decorators around the contract rather than subclassing a concrete engine. The contract-first package is designed for exactly this — decorating is safe, subclassing couples you to an engine.
marko/view standalone and expecting view('foo') to work. It will not — you need a concrete engine plus a service-provider binding. Check composer.lock to confirm which adapter you actually pulled in.marko/view minor that predates an interface addition with an adapter that implements the newer interface will fail at container resolution — usually with a "cannot instantiate interface" or a fatal "class must implement method" error. Pin the adapter and this package together in composer.json if you support multiple versions.marko-php/marko repo, watch that repo's changelog rather than this one.config/app.php or equivalent, (b) the binding key matches what the factory interface resolves to, and (c) you haven't accidentally bind()-ed a scoped singleton and lost it between requests.\Exception. They carry the template name and are the fastest path to diagnosing a stray path or a typo'd view.__toString unless the engine contract guarantees it. Passing a DTO and converting at the adapter boundary is more portable if you ever change engines.marko-php/marko rather than patch locally — local patches will be silently overwritten on the next composer update.How can I help you explore Laravel packages today?