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

Yaml Laravel Package

symfony/yaml

The Symfony Yaml component parses YAML files into PHP arrays and dumps PHP data back to YAML, with support for inline syntax, custom flags, and clear parse exceptions. A lightweight, dependency-free way to handle configuration and fixtures.

View on GitHub
Deep Wiki
Context7

Getting Started

symfony/yaml is a standalone component with a static API — no service provider, no facade, no config file. Laravel does not ship it, so add it explicitly:

composer require symfony/yaml

Everything happens through two static methods:

use Symfony\Component\Yaml\Yaml;

$data = Yaml::parseFile(base_path('content/pages.yaml'));   // string|array|null
$yaml = Yaml::dump(['title' => 'Home', 'tags' => ['a', 'b']]);

Where to look first:

  1. https://symfony.com/doc/current/components/yaml.html — the canonical docs (the README is just an index).
  2. vendor/symfony/yaml/Yaml.php — the flag constants (PARSE_CONSTANT, PARSE_CUSTOM_TAGS, DUMP_MULTI_LINE_LITERAL_BLOCK, DUMP_EMPTY_ARRAY_AS_SEQUENCE, DUMP_NUMERIC_KEY_AS_STRING, DUMP_NULL_AS_EMPTY, …) are the real "features" of this package and are only visible in source.
  3. vendor/symfony/yaml/Exception/ParseException.php — getParsedLine() and getSnippet() are your debugging tools.

First real use case: reading data that something else owns the format for — a manifest, an openapi.yaml, a docker-compose file, a CI workflow, a third-party import file, or seed/fixture data you want humans to edit. Do not migrate your config/*.php files to YAML; config:cache and the rest of Laravel assume PHP.

Sanity check in tinker:

>>> Yaml::parse("a: 1\nb: [x, y]\n")
=> ["a" => 1, "b" => ["x", "y"]]

Implementation Patterns

Load YAML into config() via a service provider

The cleanest integration if the YAML is application-owned and static:

// app/Providers/YamlConfigServiceProvider.php
public function register(): void
{
    $path = config_path('features.yaml');

    if (is_file($path)) {
        // values set here are baked into bootstrap/cache/config.php by config:cache
        $this->app['config']->set('features', Yaml::parseFile($path) ?? []);
    }
}

Once set, config('features.new_checkout.enabled') works like any other config key, and php artisan config:cache freezes it — which is usually exactly what you want.

Cache the parse for runtime/YAML owned by an external process

Parsing is regex-heavy pure PHP; re-parsing per request is wasted CPU. Key the cache on filemtime():

class YamlRepository
{
    public function get(string $path, array $default = []): array
    {
        $abs = base_path($path);
        $key = 'yaml:'.md5($abs).':'.@filemtime($abs);

        return Cache::rememberForever($key, fn () => Yaml::parseFile($abs) ?? $default);
    }
}

Bind it as a singleton and inject it; this is your extension point since the component itself has none.

Validate the parsed array with Laravel's validator

YAML is untyped and hand-edited — never trust its shape. Treat the parse result as request input:

$parsed = Yaml::parseFile($path);

$data = Validator::make($parsed ?? [], [
    'name'      => ['required', 'string', 'max:120'],
    'steps'     => ['required', 'array', 'min:1'],
    'steps.*.id'=> ['required', 'string', 'distinct'],
])->validate();

Turn ParseException into something Laravel-shaped

use Symfony\Component\Yaml\Exception\ParseException;

try {
    $data = Yaml::parseFile($path);
} catch (ParseException $e) {
    throw ValidationException::withMessages([
        'file' => sprintf('%s (line %d)', $e->getMessage(), $e->getParsedLine()),
    ]);
    // $e->getSnippet() gives the surrounding lines for logs
}

Read through Laravel's filesystem when the file isn't local

parseFile() is local-only. For an S3 disk, Storage::disk('s3')->get($path) then Yaml::parse().

Dump with intent

Defaults ($inline = 2, $indent = 4) surprise people who expect 2-space YAML:

$yaml = Yaml::dump(
    $data,
    10,   // expand deeply — fewer inline `{ }` blobs
    2,    // 2-space indent, matches most external tooling
    Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK
        | Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE
        | Yaml::DUMP_NUMERIC_KEY_AS_STRING
);

Read → mutate → write round trips

Typical for "update this manifest" tasks:

$doc = Yaml::parseFile($path) ?? [];
$doc['image'] = "app:{$tag}";

File::put($path, Yaml::dump($doc, 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));

For git-friendly diffs, normalise key order first with a recursive ksort() — the dumper uses insertion order, so unsorted arrays produce noisy commits.

Artisan command + YAML fixtures in tests

// php artisan import:yaml database/data/products.yaml
$rows = Yaml::parseFile($this->argument('file')) ?? [];

For tests, YAML data providers are genuinely nice for long fixture arrays:

public static function pricingCases(): array
{
    return collect(Yaml::parseFile(__DIR__.'/fixtures/pricing.yaml'))
        ->map(fn ($case) => [$case['input'], $case['expected']])
        ->all();
}

Gotchas and Tips

It's static, so it's untestable to mock. Yaml::parseFile() is a static call — wrap it in your own YamlRepository/YamlLoader interface if you need to stub it or swap libraries later. There is no DI extension point inside the component.

Reserved indicators must be quoted. A value starting with %, @, `, !, &, *, -, ?, or : throws ParseException. This bites constantly with emails/mentions (@handle), and with Symfony parameter syntax (%env(...)%). Quote them:

handle: '@acme'      # ❌ ParseException
handle: "'@acme'"    # ✅ (or just '@acme' in double quotes: "@acme")

Booleans are true/false only. Symfony follows YAML 1.2: yes, no, on, off, y, n parse as strings. null, Null, NULL, ~, and an empty value parse as null. If external tools emit yes, convert explicitly.

Numeric-looking strings round-trip badly. version: 1.10 becomes 1.1; zip: 01234 becomes 1234. Quote version numbers and anything with leading zeros. Also: integers outside PHP_INT_MIN..MAX are now kept as strings (fixed in 6.4.26+) — don't (int) cast large IDs blindly.

Dates are not DateTime and not timestamps. The old PARSE_DATETIME / PARSE_OBJECT / PARSE_OBJECT_FOR_MAP flags are gone in Symfony 7. Cast and validate dates yourself, and quote them so the dumper doesn't reformat them.

Empty arrays and numeric keys are ambiguous. Yaml::dump([]) emits { }, which is an empty map but parses back to [] — fine until an external tool cares. Use DUMP_EMPTY_ARRAY_AS_SEQUENCE when you need []. PHP arrays with sequential int keys dump as sequences; if you truly need a numeric-keyed map, use DUMP_NUMERIC_KEY_AS_STRING or a JSON string payload.

No comments, no anchors, no formatting fidelity. Dumping expands aliases, drops comments, and normalises style. If you need to edit a user's YAML preserving their comments, round-tripping with this component will destroy them — keep those files as opaque templates or use a comment-preserving library.

Single document only. Don't rely on ----separated multi-document streams; split the file yourself before parsing.

Tabs are illegal for indentation. Recent releases fixed tabs inside block scalars and comments after empty ones, but indentation must be spaces. This is the #1 cause of ParseException on files pasted from editors.

Debugging a parse error: the message names the line; add $e->getSnippet() to logs to see the surrounding text. A quick bisect is parsing progressively larger prefixes of the file.

Security — treat YAML as untrusted code-adjacent input.

  • Never enable PARSE_CUSTOM_TAGS or accept !php/object on user-supplied files — that path calls unserialize() (there's a PHPStan rule for it upstream, which tells you how often it goes wrong). Avoid !php/const / PARSE_CONSTANT too.
  • Alias/billion-laughs and deep-recursion DoS were fixed in 5.4.52+, 6.4.40+, 7.4.12+ (CVE-2026-45304/45305/45133). Pin ^7.4.12 or newer, or ^8.1 on PHP 8.4.
  • Bound upload size (max: file rule) and, if you accept arbitrary YAML, cap nesting depth by validating the resulting array shape.

Version constraints matter. Composer resolves 6.4 / 7.x / 8.x depending on your PHP version, and the parse behaviour of edge cases (block scalars, + prefixed ints, NaN, 0X prefixes) changed across patch releases. Pin a major in composer.json if generated YAML is compared byte-for-byte in CI, and upgrade deliberately for the security patches.

Don't assert exact dump strings in tests. assertSame($expectedYaml, Yaml::dump($data)) is brittle across patch releases. Prefer round-trip assertions — but remember round-tripping is lossy for objects, DateTime, numeric-keyed maps, and 1.0 vs 1, so assert the semantic subset you care about.

Reach for JSON on hot paths. If a file is machine-generated and machine-consumed, json_decode() is meaningfully faster than the YAML parser. YAML earns its cost when humans edit the file.

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.
vento/laravel-airbrake
wfeller/laravel-batch
maco-studios/mage-os-installer
busarm/armie
siberfx/laravel-logger
kfteam1991/dcat-admin
dashed/dashed-laposta
dashed/dashed-ecommerce-montaportal
dashed/dashed-ecommerce-etsy
neohq/string-blade-compiler
log1x/acf-composer
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager