kern046/changelog-parser
A PHP package for parsing changelog files into structured entries, helping you turn Markdown changelogs into usable data for display, filtering, or release notes. Makes version history easier to work with in Laravel and PHP applications.
Install it as a plain Composer dependency — it is a framework-agnostic PHP library, not a Laravel package, so there is no service provider to register:
composer require kern046/changelog-parser
Where to look first: this is a small library, so open vendor/kern046/changelog-parser/src/ before trusting any blog post. Read the main parser class and any *Formatter / *Format classes — that is where the actual contract lives. Expect a parse()-style entry point that takes either a file path or a raw CHANGELOG string and returns structured data (versions, release dates, change types, entries) or a serialized string depending on the formatter passed in. The README covers the happy path; the src covers the edge cases you will actually hit.
First use case: parse your project's own CHANGELOG.md and pull out the most recent released version so you can surface it in an /about, /health, or admin dashboard endpoint:
use Kern046\ChangelogParser\...; // confirm class name from src/
$raw = file_get_contents(base_path('CHANGELOG.md'));
$data = /* parser call */;
// e.g. grab version[0]['version'] or similar
Start with that single read-only use case before building anything on top of it. It confirms the expected file format, the shape of the return value, and how the library behaves with your real changelog — which is the fastest way to discover whether your file matches its assumptions.
Wrap it in a small application service, not scattered calls. Because there is no Laravel integration, define your own boundary so the rest of the app never imports the vendor namespace directly:
final class ChangelogReader
{
public function __construct(private string $path) {}
public function latest(): array { /* parse + normalise */ }
public function all(): array { /* ... */ }
}
Bind it in a service provider using base_path('CHANGELOG.md') and inject it where needed. This pays off immediately when the package's API shifts or when you swap it for a different parser.
Cache the parse, not the raw file. Parsing on every request is wasteful for a dashboard. Serialise the parsed result into the cache keyed on the file's mtime (and size as a cheap second signal), so edits to CHANGELOG.md invalidate automatically without a manual bust:
$key = 'changelog:' . md5_file($path) . ':' . filemtime($path);
return Cache::rememberForever($key, fn () => $this->parse($path));
Drive an Artisan command for release tooling. A php artisan changelog:latest --format=json command is the natural integration point — it lets your CI/CD pipeline, Docker image build, or deploy script extract the current version string (git describe alternative), validate that the top entry matches the tagged version, or emit release notes for a GitHub release body.
Use formatters as an output boundary. The package's "several formats" is really about serialisation. If it exposes a JSON formatter, use it for API responses and CLI output rather than re-serialising the array yourself; you get consistent field names for free. For HTML rendering, prefer consuming the array form and rendering with Blade — do not assume its markdown formatter produces your app's markup.
Combine with a validation step for CI. Parse the changelog in a test or CI job and assert the newest entry's version equals the version in composer.json, that every entry has a date, and that the file remains parseable. This is the highest-value day-to-day use of a changelog parser: keeping the changelog honest, not just reading it.
## [1.2.3] - 2024-01-01, ### Added, etc.). Non-conforming headings, missing dates, freeform prose, or ## Unreleased blocks may be skipped silently rather than erroring. Verify against your actual file — silent skipping is the most common surprise with this class of library.config/changelog.php or a facade to exist. Wrapping it (see above) is not optional hygiene, it is required plumbing.base_path(...)), not a relative one. Relative paths resolve against the process working directory, which differs between web requests, queue workers, and CLI — a classic source of "works in artisan tinker, fails in the web app" bugs.CHANGELOG.md should not 500 your dashboard. Log it, return an empty/unknown version, and move on.dd() (or var_export the array) rather than reading the markdown again — the shape of the output tells you exactly which headings the parser recognised and which it ignored.composer update stays safe.How can I help you explore Laravel packages today?