├── .github └── workflows │ └── main.yml ├── CHANGELOG.md ├── README.md ├── composer.json └── src ├── Parser.php └── Retrievers ├── AbstractRetriever.php ├── ChangesRetriever.php ├── DescriptionRetriever.php ├── LineRetriever.php └── ReleasesRetriever.php /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: 2 | CI 3 | on: [push] 4 | jobs: 5 | php: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v1 9 | - name: Installing PHP 10 | uses: shivammathur/setup-php@master 11 | with: 12 | php-version: 7.4 13 | - name: Installing dependencies 14 | run: composer install --prefer-dist --ignore-platform-reqs 15 | - name: Tests 16 | run: composer test 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | A change log for the change log parser 3 | 4 | ## Unreleased 5 | 6 | ## 0.11.0 - 2019-12-23 7 | 8 | ### Changed 9 | 10 | - Allow `symfony/dom-crawler:5` and `symfony/css-selector:5` 11 | - Drop support for everything below PHP 7.4 12 | 13 | ## 0.10.1 - 2019-10-05 14 | 15 | ### Changed 16 | 17 | - [internal] Replaced Travis with GitHub actions 18 | - [internal] Updated to PHPSpec 6 19 | 20 | ### Security 21 | 22 | - Upgraded to `league/commonmark` to fix CVE-2019-10010 23 | 24 | ## 0.10.0 - 2018-02-25 25 | 26 | ### Changed 27 | 28 | - Drop support for PHP5, PHP 7.1.3 is now required 29 | - Upgrade dependencies to their latest versions 30 | 31 | ## 0.9.0 - 2016-01-01 32 | 33 | ### Changed 34 | 35 | - Updated symfony dependencies to 3.0 36 | 37 | ## 0.8.0 - 2015-12-25 38 | 39 | ### Changed 40 | 41 | - Updated symfony requirements to 2.8 42 | 43 | ## 0.7.1 - 2015-12-13 44 | 45 | ### Fixed 46 | 47 | - Fixed issue where the description returned the first release if no description was given 48 | - Better support for multi line descriptions. 49 | - Fixed issue where the parser crashed if a release had no date (for example and `unreleased` section) 50 | 51 | ## 0.7.0 - 2015-11-25 52 | 53 | * Updated the following dependencies to their latest versions: `league/commonmark:0.12.0`, `symfony/dom-crawler:2.7.7`, `symfony/css-selector:2.7.7` 54 | * PSR-4 autoloading 55 | 56 | ## 0.6.2 - 2015-04-15 57 | 58 | ### Fixed 59 | 60 | * Fix issue with empty lines being parsed 61 | 62 | ## 0.6.1 - 2015-01-09 63 | 64 | ### Fixed 65 | 66 | * Fixed an issue where a section would sometimes be empty 67 | 68 | ## 0.6.0 - 2014-12-28 69 | 70 | ### Added 71 | 72 | * added `.gitattributes` file to only include necessary content during a composer install 73 | 74 | ## 0.5.0 - 2014-12-25 75 | 76 | ### Changed 77 | 78 | * getChanges and getReleases now return arrays instead of JSON 79 | 80 | ## 0.4.0 - 2014-12-24 81 | 82 | _No notable changes_ 83 | 84 | ## 0.3.0 - 2014-12-23 85 | 86 | ### Changed 87 | 88 | * Changed the underlying Markdown parser to [league/commonmark](https://github.com/thephpleague/commonmark) 89 | 90 | ## 0.2.0 - 2014-11-22 91 | 92 | ### Added 93 | 94 | * `getChanges` method to retrieve a single release 95 | 96 | ## 0.1.0 - 2014-11-22 97 | 98 | ### Added 99 | 100 | * `getReleases` method that retrieves the releases described in a change log 101 | * `toJson` method that creates a json representation of a change log. 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | > Parse changelogs like a pro 3 | 4 | This package makes it easy to parse change logs in the [keepachangelog.com](http://keepachangelog.com/) format 5 | 6 | ## Installation 7 | 8 | `composer require bramdevries/changelog` 9 | 10 | ## Usage 11 | 12 | ### Parsing an entire change log 13 | 14 | The following change log: 15 | 16 | ```md 17 | 18 | # Change Log 19 | A change log for the change log parser 20 | 21 | ## 0.2.0 - 2014-11-22 22 | 23 | ### Added 24 | 25 | * `getChanges` method to retrieve a single release 26 | 27 | ## 0.1.0 - 2014-11-22 28 | 29 | ### Added 30 | 31 | * `getReleases` method that retrieves the releases described in a change log 32 | * `toJson` method that creates a json representation of a change log. 33 | 34 | ``` 35 | 36 | ```php 37 | $parser = new Changelog\Parser(file_get_contents('CHANGELOG.md'); 38 | echo $parser->toJson(); 39 | ``` 40 | 41 | Will return 42 | 43 | ```json 44 | { 45 | "description": "A change log for the change log parser", 46 | "releases": [ 47 | { 48 | "name": "0.0.1", 49 | "date": "2014-11-22", 50 | "changes": { 51 | "added": [ 52 | "getReleases method that retrieves the releases described in a change log", 53 | "toJson method that creates a json representation of a change log." 54 | ] 55 | } 56 | } 57 | ] 58 | } 59 | ``` 60 | 61 | ### Parsing a single release's changelog 62 | 63 | eg: If you want to parse a pull request in this format 64 | 65 | ```md 66 | ### Added 67 | - Addition 1 68 | - Addition 2 69 | 70 | ### Changed 71 | - Change 1 72 | - Change 2 73 | 74 | ### Removed 75 | - Removal 1 76 | - Removal 2 77 | ``` 78 | 79 | ```php 80 | // Assuming $content contains the above markdown 81 | $parser = new Changelog\Parser($content); 82 | echo $parser->getChanges(); 83 | ``` 84 | 85 | returns 86 | 87 | ```json 88 | { 89 | "added": [ 90 | "Addition 1", 91 | "Addition 2" 92 | ], 93 | "changed": [ 94 | "Change 1", 95 | "Change 2" 96 | ], 97 | "removed": [ 98 | "Removal 1", 99 | "Removal 2" 100 | ] 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bramdevries/changelog", 3 | "license": "MIT", 4 | "description": "A http://keepachangelog.com/ parser for the common developer", 5 | "authors": [ 6 | { 7 | "name": "Bram Devries", 8 | "email": "bram@madewithlove.be" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=7.4", 13 | "symfony/dom-crawler": "~5", 14 | "symfony/css-selector": "~5", 15 | "league/commonmark": "^1.0" 16 | }, 17 | "require-dev": { 18 | "phpspec/phpspec": "~6" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Changelog\\": "src" 23 | } 24 | }, 25 | "scripts": { 26 | "test": [ 27 | "phpspec run --format=pretty" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Parser.php: -------------------------------------------------------------------------------- 1 | setContent($content); 29 | } 30 | 31 | /** 32 | * Retrieve all the releases from the change log 33 | * 34 | * @return array 35 | */ 36 | public function getReleases() 37 | { 38 | return (new ReleasesRetriever($this->content->filter('h2')))->retrieve(); 39 | } 40 | 41 | /** 42 | * @return array 43 | */ 44 | public function getChanges() 45 | { 46 | return (new ChangesRetriever($this->content->filter('h3')))->retrieve(); 47 | } 48 | 49 | /** 50 | * Set the content to parse 51 | * 52 | * @param $value 53 | */ 54 | public function setContent($value) 55 | { 56 | $converter = new CommonMarkConverter; 57 | $this->content = new Crawler($converter->convertToHtml($value)); 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getDescription() 64 | { 65 | return (new DescriptionRetriever($this->content->filter('h1 ~ p')))->retrieve(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Retrievers/AbstractRetriever.php: -------------------------------------------------------------------------------- 1 | nodes = []; 20 | $nodes->each(function ($node) { 21 | $this->nodes[] = $node; 22 | }); 23 | } 24 | 25 | /** 26 | * @return array 27 | */ 28 | public function retrieve() 29 | { 30 | return array_map([$this, 'parse'], $this->nodes); 31 | } 32 | 33 | /** 34 | * @param Crawler $node 35 | */ 36 | abstract protected function parse(Crawler $node); 37 | } -------------------------------------------------------------------------------- /src/Retrievers/ChangesRetriever.php: -------------------------------------------------------------------------------- 1 | html()); 34 | 35 | if (!$key) { 36 | return []; 37 | } 38 | 39 | $lines = (new LineRetriever($node->nextAll()->first()->children('li')))->retrieve(); 40 | 41 | return [ 42 | 'section' => $key, 43 | 'changes' => $lines 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Retrievers/DescriptionRetriever.php: -------------------------------------------------------------------------------- 1 | text(); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Retrievers/LineRetriever.php: -------------------------------------------------------------------------------- 1 | html()); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Retrievers/ReleasesRetriever.php: -------------------------------------------------------------------------------- 1 | html()); 17 | 18 | // Get the changes 19 | $nodes = $node->nextAll()->filterXPath('h3[preceding-sibling::h2[1][.="'.$node->html().'"]]'); 20 | 21 | $changes = (new ChangesRetriever($nodes))->retrieve(); 22 | 23 | return [ 24 | 'name' => $title[0], 25 | 'date' => isset($title[1]) ? $title[1] : null, 26 | 'changes' => $changes, 27 | ]; 28 | } 29 | } --------------------------------------------------------------------------------