PHPackages                             mistralys/changelog-parser - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. mistralys/changelog-parser

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

mistralys/changelog-parser
==========================

PHP library to parse Markdown-formatted change log files.

1.1.0(3mo ago)12.6k↓50%3MITPHPPHP &gt;=8.4

Since Apr 20Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/Mistralys/changelog-parser)[ Packagist](https://packagist.org/packages/mistralys/changelog-parser)[ RSS](/packages/mistralys-changelog-parser/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (5)Dependencies (6)Versions (6)Used By (3)

Changelog Parser
================

[](#changelog-parser)

PHP library to parse Markdown-formatted change log files.

Requirements
------------

[](#requirements)

- PHP &gt;= 8.4
- JSON extension
- [Composer](https://getcomposer.org)

Installation
------------

[](#installation)

Add the package to your `composer.json` file with the following command:

```
composer require mistralys/changelog-parser
```

Also see the [Packagist page](https://packagist.org/packages/mistralys/changelog-parser).

Supported changelog formats
---------------------------

[](#supported-changelog-formats)

The parser expects all versions to be listed as headers with the same header level, and individual changes to be added as a list. Both of these examples will work:

```
# v1.2.3
- Made some changes

# v1.2.2
- Lotsa code changes
- Added some documentation
```

```
### v1.2.3
- Made some changes

### v1.2.2
- Lotsa code changes
- Added some documentation
```

### Version heading formats

[](#version-heading-formats)

All the following headings are valid formats that the parser will recognize:

```
# v1

# v1.2

# v1.2.3

# 1

# 1.2

# 1.2.3

# 1.2.3-ALPHA

# 1.2.3-BranchName-SNAPSHOT

# 5.0 - Optional version label

# 5.0 | Optional version label

# 5.0 ~ Optional version label
```

For more information on the spectrum of version strings that can be used, have a look at the supported formats in the [mistralys/version-parser](https://github.com/Mistralys/version-parser#supported-version-strings)package, which is used to read them.

### Nesting the changelog in a document

[](#nesting-the-changelog-in-a-document)

The way the parser analyzes the Markdown document means that the changelog can be nested anywhere. The heading level will be inferred from the first version heading it encounters.

In this example, the changelog is not a separate document, but is nested in a subsection.

```
# Application name

## Usage

Learn how to use the application with this documentation.

## Change log

### v1.2.3
- Made some changes

### v1.2.2
- Lotsa code changes
- Added some documentation

## Credits

Many people contributed to the application.
```

> The changelog parser will stick to the first changelog it finds in the document, meaning that only the first of multiple, separate version lists will be used, even if they have the same heading level.

### Subheaders within versions

[](#subheaders-within-versions)

The parser will recognize subheaders within a version entry, and add collect these as plain text to be accessed again later. This makes it possible to further document things like breaking changes for example.

```
# v2.0.0 - Complete rework (breaking)
- Code entirely refurbished
- Documentation rewritten

## Breaking changes
- Renamed all methods
- Renamed all files
```

Only the items below the version will be considered changes in the version. The "Breaking changes" subheader and any additional content is captured as text, which can be accessed via `getFreeformText()`:

```
use Mistralys\ChangelogParser\ChangelogParser;

$version = ChangelogParser::parseMarkdownFile('changelog.md')->getVersionByNumber('2.0.0');

echo $version->getFreeformText();
```

This will output:

```
## Breaking changes
- Renamed all methods
- Renamed all files
```

### Categorized changes

[](#categorized-changes)

#### Introduction

[](#introduction)

The parser also supports categorized changes, which are a way to mark changes by type. Entries can be prepended with symbols that define what kind of change they are, with two criticality levels: Optional and mandatory.

The idea is to mark changes that require users to take action with a new version, declare which ones are optional, and categorize them by the components that were modified.

#### Supported symbols

[](#supported-symbols)

The parser handles the following symbols:

- `{ }` - Global change, optional
- `( )` - Global change, mandatory
- `{C}` - Component change, optional
- `(C)` - Component change, mandatory

For component-related changes, the name of the component is expected to come directly after the symbol, and the description of the change separated by a colon.

#### Example

[](#example)

```
# v1.1.0 Tweaks
- ( ) The supported image format was changed to JPG only.
- (C) Image Reader: Added support for JPG images.
- {C} Image Reader: Added PNG image conversion.
```

This version contains three changes, two of which are mandatory, requiring special attention when updating. The third is optional.

> Note: Optional changes do not necessarily need to be marked with a symbol, but it has the advantage of using the component name to group changes by component.

#### Backticks for symbols

[](#backticks-for-symbols)

To make the changelogs nicer to read, the parser supports using backticks for the symbols. With a monospaced font, they are better aligned and easier to read.

```
# v1.1.0 Tweaks
- `( )` The supported image format was changed to JPG only.
- `(C)` Image Reader: Added support for JPG images.
- `{C}` Image Reader: Added PNG image conversion.
```

Usage examples
--------------

[](#usage-examples)

### Fetch all versions

[](#fetch-all-versions)

```
use Mistralys\ChangelogParser\ChangelogParser;

$versions = ChangelogParser::parseMarkdownFile('changelog.md')->getVersions();

foreach($versions as $version)
{
    echo $version->getNumber().PHP_EOL;
}
```

### Fetch the latest version

[](#fetch-the-latest-version)

```
use Mistralys\ChangelogParser\ChangelogParser;

$parser = ChangelogParser::parseMarkdownFile('changelog.md');

$latest = $parser->getLatestVersion();
```

### Get a version by number

[](#get-a-version-by-number)

```
use Mistralys\ChangelogParser\ChangelogParser;

$parser = ChangelogParser::parseMarkdownFile('changelog.md');

$version = $parser->getVersionByNumber('5.2.0');
```

This will throw an exception if the version is not found. To check if a version number exists beforehand

### Check if a version exists

[](#check-if-a-version-exists)

```
use Mistralys\ChangelogParser\ChangelogParser;

$parser = ChangelogParser::parseMarkdownFile('changelog.md');

if($parser->versionExists('5.2.0'))
{
    $version = $parser->getVersionByNumber('5.2.0');
}
```

Note that this requires the exact version number to be known (major, minor and patch version numbers). For a more flexible way to find versions, the version info is best used instead.

For example, to find all versions matching `v4.2.x`:

```
use Mistralys\ChangelogParser\ChangelogParser;

$versions = ChangelogParser::parseMarkdownFile('changelog.md')->getVersions();

foreach($versions as $version)
{
    $info = $version->getVersionInfo();

    if($info->getMajorVersion() === 4 && $info->getMinorVersion() === 2)
    {
        // Matches v4.2
    }
}
```

### Go through individual changes in a version

[](#go-through-individual-changes-in-a-version)

```
use Mistralys\ChangelogParser\ChangelogParser;

$version = ChangelogParser::parseMarkdownFile('changelog.md')->requireLatestVersion();

$changes = $version->getChanges();

echo "Changes in version ".$version->getNumber().":".PHP_EOL;

foreach($changes as $change)
{
    echo '- '.$change->getText().PHP_EOL;
}
```

> Note the use of the `requireLatestVersion()` method: This will throw an exception instead of `NULL` if no versions are found in the change log. Handy to avoid checking for a null value.

Persisting and caching
----------------------

[](#persisting-and-caching)

To easily store or transmit changelog information, the parser offers the possibility to serialize the data to JSON. This can be decoded again later instead of parsing the source file each time.

```
use Mistralys\ChangelogParser\ChangelogParser;

if(!file_exists('changelog.json'))
{
    $changelog = ChangelogParser::parseMarkdownFile('changelog.md');
    $changelog->toJSONFile('changelog.json');
}
else
{
    $changelog = ChangelogParser::parseJSONFile('changelog.json');
}
```

This example will automatically create a JSON cache file, which performs better than parsing the source Markdown file each time, especially for large files.

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance82

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~257 days

Total

5

Last Release

96d ago

PHP version history (2 changes)1.0.0PHP &gt;=7.4

1.1.0PHP &gt;=8.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/8895528?v=4)[Mistralys](/maintainers/Mistralys)[@Mistralys](https://github.com/Mistralys)

---

Top Contributors

[![Mistralys](https://avatars.githubusercontent.com/u/8895528?v=4)](https://github.com/Mistralys "Mistralys (34 commits)")

---

Tags

changelogchangelog-parsermarkdownmarkdown-changelogphp

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/mistralys-changelog-parser/health.svg)

```
[![Health](https://phpackages.com/badges/mistralys-changelog-parser/health.svg)](https://phpackages.com/packages/mistralys-changelog-parser)
```

###  Alternatives

[masterminds/html5

An HTML5 parser and serializer.

1.8k242.8M229](/packages/masterminds-html5)[sabberworm/php-css-parser

Parser for CSS Files written in PHP

1.8k191.2M65](/packages/sabberworm-php-css-parser)[jms/metadata

Class/method/property metadata management in PHP

1.8k152.8M88](/packages/jms-metadata)[jms/serializer-bundle

Allows you to easily serialize, and deserialize data of any complexity

1.8k89.3M627](/packages/jms-serializer-bundle)[hassankhan/config

Lightweight configuration file loader that supports PHP, INI, XML, JSON, and YAML files

97513.5M170](/packages/hassankhan-config)[meyfa/php-svg

Read, edit, write, and render SVG files with PHP

54613.9M42](/packages/meyfa-php-svg)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
