├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .php_cs.dist ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin └── EMPTY ├── composer.json ├── examples ├── reading-test1.php └── reading-test2.php ├── lib ├── Element │ ├── Category.php │ ├── Entry.php │ ├── Feed.php │ ├── Link.php │ ├── Person.php │ └── Source.php ├── Service.php └── Version.php ├── phpstan.neon └── tests ├── Xml └── Atom │ └── ParseAtomTest.php └── phpunit.xml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: continuous-integration 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - release/* 7 | pull_request: 8 | jobs: 9 | unit-testing: 10 | name: PHPUnit (PHP ${{ matrix.php-versions }}) 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1'] 16 | coverage: ['pcov'] 17 | code-analysis: ['no'] 18 | include: 19 | - php-versions: '7.1' 20 | coverage: 'none' 21 | code-analysis: 'yes' 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v2 25 | 26 | - name: Setup PHP, with composer and extensions 27 | uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php 28 | with: 29 | php-version: ${{ matrix.php-versions }} 30 | extensions: mbstring, dom, fileinfo, mysql, redis, opcache 31 | coverage: ${{ matrix.coverage }} 32 | tools: composer 33 | 34 | - name: Get composer cache directory 35 | id: composer-cache 36 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 37 | 38 | - name: Cache composer dependencies 39 | uses: actions/cache@v2 40 | with: 41 | path: ${{ steps.composer-cache.outputs.dir }} 42 | # Use composer.json for key, if composer.lock is not committed. 43 | # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} 44 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 45 | restore-keys: ${{ runner.os }}-composer- 46 | 47 | - name: Install composer dependencies 48 | run: composer install --no-progress --prefer-dist --optimize-autoloader 49 | 50 | - name: Code Analysis (PHP CS-Fixer) 51 | if: matrix.code-analysis == 'yes' 52 | run: php vendor/bin/php-cs-fixer fix --dry-run --diff 53 | 54 | - name: Code Analysis (PHPStan) 55 | if: matrix.code-analysis == 'yes' 56 | run: composer phpstan 57 | 58 | - name: Test with phpunit 59 | run: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml 60 | 61 | - name: Code Coverage 62 | uses: codecov/codecov-action@v2 63 | if: matrix.coverage != 'none' 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Composer 2 | vendor/ 3 | composer.lock 4 | 5 | # Tests 6 | tests/cov/ 7 | tests/.phpunit.result.cache 8 | .php_cs.cache 9 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | getFinder() 5 | ->exclude('vendor') 6 | ->in(__DIR__); 7 | $config->setRules([ 8 | '@PSR1' => true, 9 | '@Symfony' => true 10 | ]); 11 | 12 | return $config; 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ChangeLog 2 | ========= 3 | 4 | 0.3.1 (2020-10-03) 5 | ------------------ 6 | 7 | * #15: Add support for PHP 8.0 (@phil-davis) 8 | 9 | 0.3.0 (2020-10-03) 10 | ------------------ 11 | 12 | * Add support for PHP 7.4 (@phil-davis) 13 | 14 | 0.2.0 (2017-01-03) 15 | ------------------ 16 | 17 | * Now a PHP 7 package! 18 | * Uses sabre/xml 2.0. 19 | * Strict types everywhere. 20 | 21 | 22 | 0.1.0 (2016-02-14) 23 | ------------------ 24 | 25 | * Depending on sabre/xml 1.4.0. 26 | * #3: Fixed a number of bugs during serialization. 27 | * Don't serialize empty elements anymore. 28 | * Added unittests using example documents from the rfc. 29 | * Better order of properties in feed/entry, so that the more important 30 | elements go on top. 31 | 32 | 33 | 0.0.2 (2015-12-30) 34 | ------------------ 35 | 36 | * Depending on sabre/xml 1.3.0. 37 | 38 | 39 | 0.0.1 (2015-12-23) 40 | ------------------ 41 | 42 | * First version! 43 | * Parses Atom feeds 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015-2017 fruux GmbH (https://fruux.com/) 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name Sabre nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sabre/xml-atom 2 | =============== 3 | 4 | This package contains a reader and writer for the [Atom XML format][5]. 5 | It also serves as a sample implementation of the [sabre/xml][6] package. 6 | 7 | 8 | Installation 9 | ------------ 10 | 11 | Make sure you have [composer][1] installed, and then run: 12 | 13 | composer require sabre/xml-atom 14 | 15 | 16 | Build status 17 | ------------ 18 | 19 | | branch | status | 20 | | ------ | ------ | 21 | | master | [![Build Status](https://travis-ci.org/sabre-io/xml-atom.png?branch=master)](https://travis-ci.org/sabre-io/xml-atom) | 22 | 23 | 24 | Questions? 25 | ---------- 26 | 27 | Head over to the [sabre/dav mailinglist][2], or you can also just open a ticket 28 | on [GitHub][3]. 29 | 30 | 31 | Made at fruux 32 | ------------- 33 | 34 | This library is being developed by [fruux][4]. Drop us a line for commercial 35 | services or enterprise support. 36 | 37 | [1]: http://getcomposer.org/ 38 | [2]: http://groups.google.com/group/sabredav-discuss 39 | [3]: https://github.com/fruux/sabre-skel/issues/ 40 | [4]: https://fruux.com/ 41 | [5]: https://tools.ietf.org/html/rfc4287 42 | [6]: https://github.com/fruux/sabre-xml/ 43 | -------------------------------------------------------------------------------- /bin/EMPTY: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sabre-io/xml-atom/0f6223b5e3191603f45c6a3d9529355c557e69e3/bin/EMPTY -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sabre/xml-atom", 3 | "description": "A reader and writer for the Atom format", 4 | "keywords": [ 5 | "sabre", 6 | "xml", 7 | "atom", 8 | "rfc4287" 9 | ], 10 | "homepage": "http://sabre.io/dav/", 11 | "license": "BSD-3-Clause", 12 | "require": { 13 | "php" : "^7.1 || ^8.0", 14 | "sabre/xml" : "^2.0" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Evert Pot", 19 | "email": "me@evertpot.com", 20 | "homepage": "http://evertpot.com/", 21 | "role": "Developer" 22 | } 23 | ], 24 | "support": { 25 | "forum": "https://groups.google.com/group/sabredav-discuss", 26 | "source": "https://github.com/fruux/sabre-skel" 27 | }, 28 | "autoload": { 29 | "psr-4" : { 30 | "Sabre\\Xml\\Atom\\" : "lib/" 31 | } 32 | }, 33 | "autoload-dev" : { 34 | "psr-4" : { 35 | "Sabre\\Xml\\Atom\\" : "tests/Xml/Atom" 36 | } 37 | }, 38 | "require-dev": { 39 | "friendsofphp/php-cs-fixer": "~2.17.1", 40 | "phpstan/phpstan": "^0.12", 41 | "phpunit/phpunit" : "^7.5 || ^8.5 || ^9.0" 42 | }, 43 | "scripts": { 44 | "phpstan": [ 45 | "phpstan analyse lib tests" 46 | ], 47 | "cs-fixer": [ 48 | "php-cs-fixer fix" 49 | ], 50 | "phpunit": [ 51 | "phpunit --configuration tests/phpunit.xml" 52 | ], 53 | "test": [ 54 | "composer phpstan", 55 | "composer cs-fixer", 56 | "composer phpunit" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/reading-test1.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | dive into mark 11 | 12 | A <em>lot</em> of effort 13 | went into making this effortless 14 | 15 | 2005-07-31T12:29:29Z 16 | tag:example.org,2003:3 17 | 19 | 21 | Copyright (c) 2003, Mark Pilgrim 22 | 23 | Example Toolkit 24 | 25 | 26 | Atom draft-07 snapshot 27 | 29 | 31 | tag:example.org,2003:3.2397 32 | 2005-07-31T12:29:29Z 33 | 2003-12-13T08:29:29-04:00 34 | 35 | Mark Pilgrim 36 | http://example.org/ 37 | f8dy@example.com 38 | 39 | 40 | Sam Ruby 41 | 42 | 43 | Joe Gregorio 44 | 45 | 47 |
48 |

[Update: The Atom draft is finished.]

49 |
50 |
51 |
52 |
53 | XML; 54 | 55 | $service = new Sabre\Xml\Atom\Service(); 56 | print_r($service->parse($input)); 57 | -------------------------------------------------------------------------------- /examples/reading-test2.php: -------------------------------------------------------------------------------- 1 | parse($input)); 11 | -------------------------------------------------------------------------------- /lib/Element/Category.php: -------------------------------------------------------------------------------- 1 | namespaceMap[self::ATOM_NS] = self::ATOM_DEFAULT_PREFIX; 27 | 28 | $atom = '{'.self::ATOM_NS.'}'; 29 | 30 | // The following elements are all simple xml elements, and we can use 31 | // the VO system for mapping from PHP object to XML element. 32 | $this->mapValueObject($atom.'feed', Element\Feed::class); 33 | $this->mapValueObject($atom.'entry', Element\Entry::class); 34 | $this->mapValueObject($atom.'source', Element\Source::class); 35 | $this->mapValueObject($atom.'author', Element\Person::class); 36 | $this->mapValueObject($atom.'contributer', Element\Person::class); 37 | 38 | // The following elements need custom (de-)serializers 39 | 40 | // This serializer takes an object and encodes all its properties as 41 | // XML attributes. 42 | $attributeWriter = function ($writer, $object) { 43 | $attributes = get_object_vars($object); 44 | 45 | // remove properties with a null value from the list. 46 | $attributes = array_filter( 47 | $attributes, 48 | function ($item) { 49 | return null !== $item; 50 | } 51 | ); 52 | $writer->writeAttributes( 53 | $attributes 54 | ); 55 | }; 56 | 57 | // This deserializer takes all attributes from an xml element and 58 | // turns the into properties of a class. 59 | $attributeReader = function ($reader, $class) { 60 | $attributes = $reader->parseAttributes(); 61 | $object = new $class(); 62 | foreach ($attributes as $key => $value) { 63 | if (property_exists($object, $key)) { 64 | $object->{$key} = $value; 65 | } 66 | } 67 | 68 | // Always advance the reader 69 | $reader->next(); 70 | 71 | return $object; 72 | }; 73 | 74 | // atom:category 75 | $this->classMap[Element\Category::class] = $attributeWriter; 76 | $this->elementMap[$atom.'category'] = function ($reader) use ($attributeReader) { 77 | return $attributeReader($reader, Element\Category::class); 78 | }; 79 | // atom:link 80 | $this->classMap[Element\Link::class] = $attributeWriter; 81 | $this->elementMap[$atom.'link'] = function ($reader) use ($attributeReader) { 82 | return $attributeReader($reader, Element\Link::class); 83 | }; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/Version.php: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | Example Feed 18 | 19 | 2003-12-13T18:30:02Z 20 | urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 21 | 22 | John Doe 23 | 24 | 25 | 26 | 27 | Atom-Powered Robots Run Amok 28 | 29 | urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 30 | 2003-12-13T18:30:02Z 31 | Some text. 32 | 33 | 34 | 35 | XML; 36 | 37 | $feed = new Element\Feed(); 38 | $feed->title = 'Example Feed'; 39 | 40 | $link = new Element\Link(); 41 | $link->href = 'http://example.org/'; 42 | $feed->link[] = $link; 43 | 44 | $feed->updated = '2003-12-13T18:30:02Z'; 45 | 46 | $author = new Element\Person(); 47 | $author->name = 'John Doe'; 48 | $feed->author[] = $author; 49 | 50 | $feed->id = 'urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6'; 51 | 52 | $entry = new Element\Entry(); 53 | $entry->title = 'Atom-Powered Robots Run Amok'; 54 | 55 | $link = new Element\Link(); 56 | $link->href = 'http://example.org/2003/12/13/atom03'; 57 | $entry->link[] = $link; 58 | 59 | $entry->id = 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'; 60 | $entry->updated = '2003-12-13T18:30:02Z'; 61 | $entry->summary = 'Some text.'; 62 | 63 | $feed->entry[] = $entry; 64 | 65 | $this->assertRoundTrip($xml, $feed); 66 | } 67 | 68 | public function testExtensive() 69 | { 70 | $xml = << 72 | 73 | dive into mark 74 | 75 | A <em>lot</em> of effort 76 | went into making this effortless 77 | 78 | 80 | 82 | 2005-07-31T12:29:29Z 83 | tag:example.org,2003:3 84 | Copyright (c) 2003, Mark Pilgrim 85 | 86 | Example Toolkit 87 | 88 | 89 | Atom draft-07 snapshot 90 | 92 | 94 | tag:example.org,2003:3.2397 95 | 2005-07-31T12:29:29Z 96 | 2003-12-13T08:29:29-04:00 97 | 98 | Mark Pilgrim 99 | http://example.org/ 100 | f8dy@example.com 101 | 102 | 103 | Sam Ruby 104 | 105 | 106 | Joe Gregorio 107 | 108 | [Update: The Atom draft is finished.] 109 | 110 | 111 | XML; 112 | 113 | $this->assertRoundTrip($xml); 114 | } 115 | 116 | /** 117 | * Assets whether we can parse an xml feed and serialize it again and end 118 | * up with a similar structure. 119 | * 120 | * If compareObject is specified, we'll also do a deep comparison of the 121 | * parsed atom php object. 122 | */ 123 | public function assertRoundTrip($xml, $compareObject = null) 124 | { 125 | $service = new Service(); 126 | 127 | // Changing the default namespace prefix to an empty one, as the tests 128 | // will be using that and it makes it easier to do comparisons. 129 | $service->namespaceMap[Service::ATOM_NS] = ''; 130 | 131 | $object = $service->parse($xml); 132 | 133 | if (!is_null($compareObject)) { 134 | $this->assertEquals($compareObject, $object); 135 | } 136 | 137 | $newXml = $service->writeValueObject($object); 138 | $this->assertXmlStringEqualsXmlString($newXml, $xml); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | . 13 | 14 | 15 | 16 | 17 | 18 | ../lib/ 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------