Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Carve Laravel Package

markup-carve/symfony-carve

Symfony bundle for Carve, a modern extensible markup language. Parse and render Carve documents in your apps with service container integration, Twig support, configurable options, and cached compilation for fast, flexible markup processing.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What changed in 0.1.5. This is a scoped feature release on a pre-1.0 package. Three things matter: (1) opt-in file-backed rendering via an absolute include_root plus CarveRenderer::renderFile() / renderFileWithReport(); (2) a report object that exposes resolved and attempted include targets; (3) dependency movement — carve-php bumped to ^0.1.9 and psr/log added as a runtime dependency for an optional warning logger.

Architecture fit. The valuable seam for us is CarveRenderer, which is plain PHP and framework-agnostic. The package's Twig filters are Symfony-oriented and are not a fit for a default Laravel stack (Blade, no Twig). So our architecture posture should be: consume the renderer as a library, ignore the Twig/Symfony integration surface. That is exactly the surface the release notes say is unchanged — render() and the filters still take anonymous strings and never touch the filesystem. In other words, the release adds capability without widening the part of the package we'd actually couple to.

Integration feasibility. Low friction. Composer-installable, small API, and psr/log is already present transitively in any Laravel app via Monolog, so the new runtime dependency is effectively free. The carve-php ^0.1.9 floor is the only real constraint — it forces a coordinated bump, and it is itself a pre-1.0 dependency, which compounds churn risk.

Where the new feature actually pays off. Before 0.1.5, file-backed rendering meant we would file_get_contents() ourselves and then call render(), which left include resolution entirely outside the library. Now the library owns resolution and returns a report of resolved and attempted targets. That report is the interesting artifact: it gives us a deterministic, content-derived signal to key a cache on, including the "attempted but missing" set. A cache invalidated by "the set of files this render actually depended on" is materially better than one keyed on a single root template mtime. The companion change — warnings to the logger omitting resolver details — is a small but real hardening win, since absolute filesystem paths stop leaking into log aggregation.

Technical risk. Three items dominate:

  • Path handling. include_root must be absolute and the resolver now has filesystem authority. Whether includes are constrained to the root (no ../ escape, no absolute-path injection from template content) is unverified from the notes and is the single most important thing to confirm before this touches user-supplied content.
  • Pre-1.0 instability. A 0.1.x package can break in any minor bump, and 0.1.5 already demonstrates that pattern by raising a dependency floor. We should pin exact versions and treat upgrades as deliberate changes, not composer update drift.
  • Report schema stability. We would build a cache key on the report's shape. Nothing here promises that shape is stable, so we need a defensive adapter rather than serializing the report directly into cache keys.

Key questions.

  1. Does the include resolver reject paths that escape include_root, and does it reject absolute paths originating from template content?
  2. On a missing include, does renderFile() throw, or does it degrade with a warning? Does renderFileWithReport() surface missing targets in the report without throwing?
  3. Is the resolved/attempted target list ordered deterministically, so it can be hashed into a stable cache key?
  4. Is CarveRenderer stateless and safe under Laravel Octane / long-lived workers (no memoized resolver state leaking between requests)?
  5. Does the package's Symfony bundle registration assume Symfony DI, and can the renderer be constructed and bound in a Laravel service provider without pulling Symfony components?
  6. What is the maintainer's stated BC policy ahead of 1.0, and how quickly did 0.1.4 → 0.1.5 move the dependency floor?

Integration Approach

Stack fit. Treat this as a rendering library, not a framework integration. Bind a single CarveRenderer instance as a singleton in a service provider, configured with a fixed absolute include_root pointing at a dedicated templates/partials directory — never base_path(), never the application root, never a user-writable path. Expose one narrow application-facing service (e.g. CarveContentRenderer) with two methods — render-string and render-file — so that no controller or job ever touches CarveRenderer directly. That adapter is where we absorb pre-1.0 churn.

Migration path. There is no prior assessment and no existing integration, so this is a greenfield adoption rather than a migration. The only "migration" is from whatever ad-hoc string templating exists today; the 0.1.5 notes confirm that the anonymous-string render() path is unchanged, so a string-rendering spike can be done in isolation with zero filesystem exposure before any file-backed work begins. Sequence the file-backed capability strictly after the string path is proven.

Compatibility. psr/log is satisfied by Monolog in any standard Laravel app — verify with composer why psr/log rather than assuming, since it is declared runtime-optional and the version constraint is unstated in the notes. The carve-php ^0.1.9 floor must be checked against whatever is currently locked; if another package pins a lower carve-php, we have a conflict that forces a resolution decision at adoption time. Laravel version compatibility is a non-issue for the renderer core but is untested for the Symfony/Twig side, which we are not using.

Sequencing.

  1. Spike (no filesystem). Install pinned, bind the renderer, validate string rendering against a handful of real templates. Confirms carve-php floor compatibility.
  2. Confirm path containment. Read the resolver source and write adversarial tests (../, absolute paths, symlinks, null bytes) before enabling include_root anywhere. This gates step 3.
  3. File-backed pilot on internal, non-user-supplied content. Use renderFileWithReport() from day one, even though we do not yet cache, so we accumulate real reports and learn the include graph shape.
  4. Caching layer. Build the cache key in our own adapter from the report's sorted resolved-target list plus a schema version we control. Treat the report as an input, not as a serialized artifact.
  5. Logger wiring. Route warnings through Laravel's logger at warning; confirm empirically that no absolute paths appear in output.
  6. Broaden only after the above holds. Expansion to user-influenced content requires a dedicated review of the include-escape question.

Operational Impact

Maintenance. The package is small, single-maintainer, and pre-1.0, with a dependency that is also pre-1.0. Budget for deliberate, tested upgrades rather than passive drift: exact version pins in composer.json, a composer.lock that is committed and reviewed, and a short upgrade checklist that includes re-running the adversarial include tests. The 0.1.4 → 0.1.5 change is a good calibration point — it moved a dependency floor in a patch release, which is the kind of thing that breaks a build without warning.

Support. We own the integration surface. There is no vendor SLA and no realistic expectation of one at this stage. The adapter pattern is the support strategy: if the upstream API shifts, the blast radius is one class. Escalation for upstream defects is a GitHub issue, so plan for the possibility of carrying a local patch — which argues for keeping our usage free of the Twig/Symfony surface, since that is the part most likely to diverge from our needs.

Scaling. Rendering is CPU-bound string processing with per-render filesystem reads once includes are enabled, no built-in cache, and no built-in include memoization apparent from the notes. Two consequences: (a) the app-level cache keyed on the report is not optional at volume — it is the scaling mechanism; (b) hot paths should render from strings or from a warm cache rather than repeatedly resolving a deep include tree. Watch for a per-render resolver that stats the same partials on every request. Statelessness under Octane is an open question (see above) and must be settled before we deploy on a long-lived worker runtime.

Failure modes.

  • Missing include — behavior is unspecified here; either an exception mid-render (user-visible 500) or a silent omission (wrong content, no alert). Both are bad in different ways and both are avoidable by treating "attempted targets present in report" as an error condition in our adapter and alerting on it, regardless of what the library does.
  • Cache staleness — if the key omits a newly added include, we serve stale content indefinitely. Mitigate by keying on the full resolved set and including a schema version; the attempted-target set also lets us distinguish "missing file" from "cached miss" rather than conflating them.
  • Path escape — the highest-severity failure, and the one the release notes do not address. Contained by a fixed root plus a directory dedicated to templates with no symlinks out.
  • Log noise / log leakage — partially mitigated by the 0.1.5 change; verify rather than trust.
  • Silent dependency drift — a composer update pulling a new carve-php minor that changes include semantics. Mitigated by pins.

Ramp-up. Low. The public surface is a handful of methods, and the Twig filters — the only conceptually fiddly part — are out of scope for us. A mid-level Laravel engineer should be productive within a day using render() and renderFile(), and roughly another day on the report-driven cache key. The real ramp-up cost is not the API; it is the path-containment review and the upgrade discipline, both of which need an owner before adoption rather than after.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast