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

Toml Laravel Package

devium/toml

devium/toml is a PHP library for parsing and writing TOML configuration files. It offers a simple API for reading TOML into PHP data and serializing data back to TOML, making modern config formats easy to use in PHP and Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify the surface. composer require devium/toml (pin the exact version — see Gotchas). Read, in this order:

  1. The README's usage blocks — the codec has a small API (parse a string/file to an array, encode an array to TOML). Confirm the exact method names and whether parsing is static (Toml::parse(...)) or instance-based before writing adapters, as this varies by release.
  2. src/ for the top-level codec class — this is the only class you should ever call.
  3. tests/ and any fixtures/ — this is your fastest signal on which TOML constructs are actually supported and how errors are surfaced. Skim for datetimes, inline tables, arrays of tables, and multi-line strings.

First use case — read a foreign config into an array. The realistic day-one task is ingesting a pyproject.toml, Cargo.toml, or Hugo config:

use Devium\Toml\Toml; // verify namespace against the README

$array = Toml::parse(file_get_contents(base_path('pyproject.toml')));

Then do something useful with it immediately — hydrate a DTO, merge into a domain model, or expose it in an Artisan command. Do not wire it into config/ or your app's runtime config; Laravel's PHP config arrays and env() are the right home for that. Treat this as an ingestion adapter for external formats.

First sanity check. Before building on it, throw a real pyproject.toml and a hand-written file containing a local datetime, an array of tables, and an inline table at it, and print var_export($array). If those round-trip as PHP values you expect, you have the go signal for a read-only integration.

Implementation Patterns

Wrap it behind a one-class contract — always. Never call the library namespace from domain code. One interface, one adapter:

interface TomlCodec
{
    public function decode(string $toml): array;
    public function encode(array $data): string;
}

final class DeviumTomlCodec implements TomlCodec
{
    public function decode(string $toml): array
    {
        try {
            return Toml::parse($toml);
        } catch (\Throwable $e) {
            throw new InvalidTomlException($e->getMessage(), previous: $e);
        }
    }

    public function encode(array $data): string
    {
        return Toml::encode($data);
    }
}

Bind it in a service provider ($this->app->bind(TomlCodec::class, DeviumTomlCodec::class)) and inject TomlCodec everywhere. This makes a swap to yosymfony/toml, a vendored fork, or a hand-rolled shim a one-file change.

Parse → validate → hydrate, in that order. A parser gives you syntax, not schema. The practical pipeline is:

  1. TomlCodec::decode() → array.
  2. Validate against your own schema (Laravel's Validator, a form request, or a DTO constructor with typed properties). Reject unknown/unsupported keys explicitly.
  3. Hydrate a typed DTO or domain object.

Keep the parsed array out of your domain model — convert immediately so library quirks don't leak inward.

Use it for format conversion, not as a config source. The highest-value pattern is normalizing heterogeneous project configs into one internal shape: TOML → array → JSON/YAML → validate → store. That lets you accept "import my existing project" without teaching your app to speak TOML natively.

Artisan command for import/export. Keep the codec in a command class, not controllers:

class ImportProjectConfig extends Command
{
    protected $signature = 'project:import {path}';

    public function handle(TomlCodec $toml): int
    {
        $data = $toml->decode(File::get($this->argument('path')));
        // validate + persist
        return self::SUCCESS;
    }
}

This isolates TOML to a single, testable entry point.

Cache the parsed result. Parsing is cheap at config scale, but re-parsing on every request is wasteful. Parse once, Cache::remember() the resulting array keyed by file hash, and invalidate on file mtime change.

Feature-flag the write path and ship a tested fallback. Only enable encode() behind a flag. Run round-trip tests (decode → encode → decode and assert equality) in CI. If encode fails, restrict to decode-only and emit JSON/YAML on the write side. Keep yosymfony/toml or a vendored copy tested in CI so switching is a config change, not a refactor.

Test against real-world fixtures. Add pyproject.toml, Cargo.toml, and a hostile file (dotted keys, arrays of tables, inline tables, all four datetime types, multi-line literal strings) to your test suite. These become the regression guard when you pin or bump the package.

Gotchas and Tips

Confirm spec conformance before you commit. TOML 1.0 has sharp edges — offset vs. local datetimes, underscore numeric separators, multiline literal strings, inline tables, arrays of tables. A partial implementation can parse a simple file cleanly and throw on valid syntax in the next. Run the official toml-test corpus (valid + invalid) as your go/no-go gate; if you cannot, at least hand-test every construct you expect to meet.

Encoding is riskier than decoding. Type coercion on the write path is the highest-severity failure: datetimes can silently become strings, floats/integers can reformat, and key ordering can be normalized destructively. If you write TOML, assert exact round-trip equality in tests, and default to read-only unless you have a clear interop requirement to emit.

No comment or formatting preservation. Assume the parser models values, not documents — comments, whitespace, and key order are lost. If users edit files in your UI and you write them back, this is the wrong tool; you need a format-preserving parser. Never present "round-trip" in the UI unless you have verified it.

Normalize exceptions at the adapter boundary. Raw library exceptions must not leak into HTTP responses or user-facing errors. Catch in your adapter, wrap in a domain exception type, and prefer error messages that include line/column — check the library provides this; if it does not, you own producing a useful message (e.g., by echoing the offending line).

Guard against pathological input upstream. Cap input size and nesting depth before calling the parser. Deeply nested inline tables or huge arrays can exhaust memory/CPU, and this package has had no community fuzzing to harden it. A strlen($input) > 1_000_000 → reject at the loader is enough.

Verify PHP/dependency compatibility first. Check supported PHP versions against your composer.json matrix, whether the package pins dependencies that would block a Laravel major upgrade, and whether it emits E_DEPRECATED on your runtime. Run once with opcache enabled and declare(strict_types=1) in your adapter.

Pin the version and record the fork point. At 41 stars and zero dependents, treat upstream as likely dormant: pin the patch version, review upstream diffs periodically, and document the trigger for vendoring (upstream inactive >12 months, a CVE, or a conformance regression). A forked copy in packages/ is a reasonable end state for a library this small — the adapter you wrote above is what makes that a contained change.

Don't use it for hot paths or large files. This is a config-format codec: linear in file size but CPU-bound in PHP. It is not appropriate for per-request parsing of large documents or streaming. Parse at boot or cache; never in a loop over user uploads without size caps.

Keep it out of your own product's config surface. Adding TOML as a supported config format for your app is a self-inflicted support burden with no user demand. The package earns its keep only where an external ecosystem dictates the format — importing/exporting project config, CLI tooling, scaffolders. If the requirement is really "configuration," Laravel already ships PHP arrays, .env, and first-class JSON/YAML tooling.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console