.gitignore 0000666 00000000041 13165210352 0006531 0 ustar 00 artifacts/ vendor/ .php_cs.cache .scrutinizer.yml 0000666 00000000233 13165210352 0007726 0 ustar 00 imports: - php tools: external_code_coverage: true filter: excluded_paths: - "examples/" - "bin/" - "doc/" - "tests/" .travis.yml 0000666 00000001525 13165210352 0006662 0 ustar 00 language: php php: - 7.1 - 7.2 - nightly env: global: - PHPUNIT_BIN='vendor/bin/phpunit' - PHPUNIT_CONFIG='phpunit.xml.dist' - PHPUNIT_FLAGS='--stop-on-failure --verbose' matrix: include: - php: '7.1' env: - PHPUNIT_FLAGS="--stop-on-failure --verbose --coverage-text --coverage-clover=coverage.xml" allow_failures: - php: nightly before_script: - composer config --global repo.packagist composer https://packagist.org - composer remove --dev friendsofphp/php-cs-fixer - composer install script : $PHPUNIT_BIN -c $PHPUNIT_CONFIG $PHPUNIT_FLAGS after_script: - | if [ -f "coverage.xml" ]; then echo 'sending clover to Scrutinizer' wget https://scrutinizer-ci.com/ocular.phar php ocular.phar code-coverage:upload --format=php-clover coverage.xml echo 'done' fi CODE_OF_CONDUCT.md 0000666 00000006222 13165210352 0007347 0 ustar 00 # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at alex.debril@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ LICENSE 0000666 00000002065 13165210352 0005556 0 ustar 00 The MIT License (MIT) Copyright (c) 2014 alexdebril Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Makefile 0000666 00000000377 13165210352 0006215 0 ustar 00 all: clean coverage test: vendor/bin/phpunit test-live-feeds: vendor/bin/phpunit -c phpunit-feeds.xml coverage: vendor/bin/phpunit --coverage-html=artifacts/coverage view-coverage: open artifacts/coverage/index.html clean: rm -rf artifacts/* README.md 0000666 00000014505 13165210352 0006032 0 ustar 00 # feed-io [![SensioLabsInsight](https://insight.sensiolabs.com/projects/9cabcb4b-695e-43fa-8b83-a1f9ecefea88/mini.png)](https://insight.sensiolabs.com/projects/9cabcb4b-695e-43fa-8b83-a1f9ecefea88) [![Latest Stable Version](https://poser.pugx.org/debril/feed-io/v/stable.png)](https://packagist.org/packages/debril/feed-io) [![Build Status](https://secure.travis-ci.org/alexdebril/feed-io.png?branch=master)](http://travis-ci.org/alexdebril/feed-io) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/alexdebril/feed-io/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/alexdebril/feed-io/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/alexdebril/feed-io/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/alexdebril/feed-io/?branch=master) [![Dependency Status](https://www.versioneye.com/php/debril:feed-io/2.3.1/badge?style=flat-square)](https://www.versioneye.com/php/debril:feed-io/2.3.1) [feed-io](https://github.com/alexdebril/feed-io) is a PHP library built to consume and serve news feeds. It features: - JSONFeed / Atom / RSS read and write support - a Command line interface to read feeds - HTTP Headers support when reading feeds in order to save network traffic - Detection of the format (RSS / Atom) when reading feeds - Enclosure support to handle external medias like audio content - PSR compliant logging - Content filtering to fetch only the newest items - Malformed feeds auto correction - DateTime detection and conversion - A generic HTTP ClientInterface - Guzzle Client integration Keep informed about new releases and incoming features : http://debril.org/category/feed-io # Installation Use Composer to add feed-io into your project's requirements : ```sh composer require debril/feed-io ``` # Requirements feed-io requires : - php 7.1+ - psr/log 1.0 - guzzlehttp/guzzle 6.2+ it suggests : - monolog/monolog 1.10+ Monolog is not the only library suitable to handle feed-io's logs, you can use any PSR/Log compliant library instead. ## Still on PHP 5 ? No problem, you can still install feed-io 3.0. This version will be supported until the end of PHP 5.6 security fixes (31 december 2018). ## Why skipping PHP 7.0 ? feed-io 4 requires PHP 7.1+ because return types cannot be nullable in PHP 7.0. # Fetching the repository Do this if you want to contribute (and you're welcome to do so): ```sh git clone https://github.com/alexdebril/feed-io.git cd feed-io/ composer install ``` # Unit Testing You can run the unit test suites using the following command in the library's source directory: ```sh ./vendor/bin/phpunit ``` Usage ===== ## CLI Let's suppose you installed feed-io using Composer, you can use its command line client to read feeds from your terminal : ```shell ./vendor/bin/feedio read http://php.net/feed.atom ``` You can specify the number of items you want to read using the --count option. The instruction below will display the latest item : ```shell ./vendor/bin/feedio read -c 1 http://php.net/feed.atom ``` ## reading feed-io is designed to read feeds across the internet and to publish your own. Its main class is [FeedIo](https://github.com/alexdebril/feed-io/blob/master/src/FeedIo/FeedIo.php) : ```php // create a simple FeedIo instance $feedIo = \FeedIo\Factory::create()->getFeedIo(); // read a feed $result = $feedIo->read($url); // or read a feed since a certain date $result = $feedIo->readSince($url, new \DateTime('-7 days')); // get title $feedTitle = $result->getFeed()->getTitle(); // iterate through items foreach( $result->getFeed() as $item ) { echo $item->getTitle(); } ``` ## formatting an object into a XML stream ```php // build the feed $feed = new FeedIo\Feed; $feed->setTitle('...'); // convert it into Atom $atomString = $feedIo->toAtom($feed); // or ... $atomString = $feedIo->format($feed, 'atom'); ``` ## building a feed including medias ```php // build the feed $feed = new FeedIo\Feed; $feed->setTitle('...'); $item = $feed->newItem(); // build the media $media = new \FeedIo\Feed\Item\Media $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3'); $media->setType('audio/mpeg'); // add it to the item $item->addMedia($media); $feed->add($item); ``` ## activate logging feed-io natively supports PSR-3 logging, you can activate it by choosing a 'builder' in the factory : ```php $feedIo = \FeedIo\Factory::create(['builder' => 'monolog'])->getFeedIo(); ``` feed-io only provides a builder to create Monolog\Logger instances. You can write your own, as long as the Builder implements BuilderInterface. ## Building a FeedIo instance without the factory To create a new FeedIo instance you only need to inject two dependencies : - an HTTP Client implementing FeedIo\Adapter\ClientInterface. It can be wrapper for an external library like FeedIo\Adapter\Guzzle\Client - a PSR-3 logger implementing Psr\Log\LoggerInterface ```php // first dependency : the HTTP client // here we use Guzzle as a dependency for the client $guzzle = new GuzzleHttp\Client(); // Guzzle is wrapped in this adapter which is a FeedIo\Adapter\ClientInterface implementation $client = new FeedIo\Adapter\Guzzle\Client($guzzle); // second dependency : a PSR-3 logger $logger = new Psr\Log\NullLogger(); // now create FeedIo's instance $feedIo = new FeedIo\FeedIo($client, $logger); ``` Another example with Monolog configured to write on the standard output : ```php use FeedIo\FeedIo; use FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client as GuzzleClient; use Monolog\Logger; use Monolog\Handler\StreamHandler; $client = new Client(new GuzzleClient()); $logger = new Logger('default', [new StreamHandler('php://stdout')]); $feedIo = new FeedIo($client, $logger); ``` ## Dealing with missing timezones Sometimes you have to consume feeds in which the timezone is missing from the dates. In some use-cases, you may need to specify the feed's timezone to get an accurate value, so feed-io offers a workaround for that : ```php $feedIo->getDateTimeBuilder()->setFeedTimezone(new \DateTimeZone($feedTimezone)); $result = $feedIo->read($feedUrl); $feedIo->getDateTimeBuilder()->resetFeedTimezone(); ``` Don't forget to reset `feedTimezone` after fetching the result, or you'll end up with all feeds located in the same timezone. ## Online documentation and API reference The whole documentation and API reference is available at https://feed-io.net/documentation.html UPGRADE-3.0.md 0000666 00000002406 13165210352 0006457 0 ustar 00 # UPGRADE FROM 2.x to 3.0 ## FeedIo::format() now returns a string In version 2.x FeedIo::format() returned a DomDocument. Now that feed-io supports JSON Feed, this is no longer relevant so now FeedIo::format() returns a string Before : ```php // Feed creation $feed = new \FeedIo\Feed(); // set all feed properties and add items ... $feed->setTitle('your title'); // ... $feedIo = \FeedIo\Factory::create()->getFeedIo(); $domDocument = $feedIo->format($feed, 'atom'); echo $domDocument->saveXML(); ``` Now you get the string : ```php // Feed creation $feed = new \FeedIo\Feed(); $feedIo = \FeedIo\Factory::create()->getFeedIo(); echo $feedIo->format($feed, 'atom'); ``` ## Result::getDocument() returns a \FeedIo\Reader\Document instance Instead of a DomDocument. Before : ```php $feedIo = \FeedIo\Factory::create()->getFeedIo(); $result = $feedIo->read('http://php.net/feed.atom'); $dom = $result->getDocument(); ``` After : ```php $feedIo = \FeedIo\Factory::create()->getFeedIo(); $result = $feedIo->read('http://php.net/feed.atom'); $dom = $result->getDocument()->getDOMDocument(); ``` This is because Result::getDocument()'s return value is a wrapper for both XML and JSON streams. ### That's it There are no other modifications to upgrade into 3.0. UPGRADE-4.0.md 0000666 00000002531 13165210352 0006457 0 ustar 00 # UPGRADE FROM 3.x to 4.0 The major change in version 4.0 is the full migration to PHP 7.1. It has an impact on interface implementations. ## Types are explicit From now on, all types are explicits in method signatures. It has an impact on classes that implements : - \FeedIo\FeedInterface - \FeedIo\Feed\ItemInterface - \FeedIo\Feed\NodeInterface - \FeedIo\Feed\ElementsAwareInterface - \FeedIo\Feed\Item\AuthorInterface - \FeedIo\Feed\Item\MediaInterface - \FeedIo\Feed\Node\CategoryInterface - \FeedIo\Feed\Node\ElementInterface For instance, `FeedIo\FeedInterface::setUrl($url)` becomes : ```php /** * @param string $url * @return FeedInterface */ public function setUrl(string $url) : FeedInterface; ``` As a consequence, you need to adapt any class that implements `FeedIo\FeedInterface` according to the new signature : ```php /** * @param string $url * @return FeedInterface */ public function setUrl($url) { $this->url = $url; return $this; } ``` becomes : ```php /** * @param string $url * @return FeedInterface */ public function setUrl(string $url) : FeedInterface { $this->url = $url; return $this; } ``` You should refer to the new interfaces declaration to get the full list of concerned functions. bin/feedio 0000666 00000002204 13165210352 0006472 0 ustar 00 #!/usr/bin/env php setName('feed-io : the CLI feed reader'); $application->add(new \FeedIo\Command\ReadCommand()); $application->run(); } else { main($argc, $argv); exit; } /** * This function is invoked if symfony/console is not installed */ function main($argc, $argv) { if ( $argc < 2 ) { echo 'feed-io version 2.4' . PHP_EOL; echo 'Usage : ' . PHP_EOL; echo "\t feed-io [url]" . PHP_EOL; exit; } $feedIo = \FeedIo\Factory::create()->getFeedIo(); $feed = array_reverse(iterator_to_array($feedIo->read($argv[$argc-1])->getFeed())); foreach( $feed as $i => $item ) { echo "\033[32m{$item->getLastModified()->format(\DateTime::ATOM)} : \033[34m{$item->getTitle()}\033[0m"; echo strip_tags(nl2br($item->getDescription())) . PHP_EOL; } } composer.json 0000666 00000002234 13165210352 0007271 0 ustar 00 { "name": "debril/feed-io", "description": "PHP library built to consume and serve JSONFeed / RSS / Atom feeds", "keywords": ["rss", "atom","jsonfeed", "feed", "news", "CLI", "client"], "homepage": "https://feed-io.net", "type": "library", "license": "MIT", "authors": [ { "name": "Alexandre Debril", "email": "alex.debril@gmail.com" } ], "bin" : [ "bin/feedio" ], "require": { "php": ">=7.1", "guzzlehttp/guzzle": "~6.2", "psr/log": "~1.0" }, "require-dev": { "phpunit/phpunit": "~6.2.0", "monolog/monolog": "1.*", "friendsofphp/php-cs-fixer": "^2.4" }, "suggest": { "monolog/monolog": "Allows to handle logs", "symfony/console": "Allows to use the command line interface" }, "autoload": { "psr-4": {"FeedIo\\": "src/FeedIo"} }, "scripts": { "install-hook": "chmod +x pre-commit.sh && cp pre-commit.sh .git/hooks/pre-commit", "pre-autoload-dump": "composer install-hook" }, "config": { "preferred-install": { "*": "dist" } } } composer.lock 0000666 00000303321 13165210352 0007251 0 ustar 00 { "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], "content-hash": "73bcf4c29cae702c2d7e4b9af4630966", "packages": [ { "name": "guzzlehttp/guzzle", "version": "6.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", "shasum": "" }, "require": { "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.4", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.0 || ^5.0", "psr/log": "^1.0" }, "suggest": { "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { "dev-master": "6.2-dev" } }, "autoload": { "files": [ "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" } ], "description": "Guzzle is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", "rest", "web service" ], "time": "2017-06-22T18:50:49+00:00" }, { "name": "guzzlehttp/promises", "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", "shasum": "" }, "require": { "php": ">=5.5.0" }, "require-dev": { "phpunit/phpunit": "^4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Promise\\": "src/" }, "files": [ "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], "time": "2016-12-20T10:07:11+00:00" }, { "name": "guzzlehttp/psr7", "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "shasum": "" }, "require": { "php": ">=5.4.0", "psr/http-message": "~1.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" }, "files": [ "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "Tobias Schultze", "homepage": "https://github.com/Tobion" } ], "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", "request", "response", "stream", "uri", "url" ], "time": "2017-03-20T17:10:46+00:00" }, { "name": "psr/http-message", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", "psr", "psr-7", "request", "response" ], "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], "time": "2016-10-10T12:19:37+00:00" } ], "packages-dev": [ { "name": "composer/semver", "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.5 || ^5.0.5", "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nils Adermann", "email": "naderman@naderman.de", "homepage": "http://www.naderman.de" }, { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http://seld.be" }, { "name": "Rob Bast", "email": "rob.bast@gmail.com", "homepage": "http://robbast.nl" } ], "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ "semantic", "semver", "validation", "versioning" ], "time": "2016-08-30T16:08:34+00:00" }, { "name": "doctrine/annotations", "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/annotations/zipball/5beebb01b025c94e93686b7a0ed3edae81fe3e7f", "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f", "shasum": "" }, "require": { "doctrine/lexer": "1.*", "php": "^7.1" }, "require-dev": { "doctrine/cache": "1.*", "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.5.x-dev" } }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], "description": "Docblock Annotations Parser", "homepage": "http://www.doctrine-project.org", "keywords": [ "annotations", "docblock", "parser" ], "time": "2017-07-22T10:58:02+00:00" }, { "name": "doctrine/instantiator", "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.2.x-dev" } }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", "homepage": "http://ocramius.github.com/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://github.com/doctrine/instantiator", "keywords": [ "constructor", "instantiate" ], "time": "2017-07-22T11:58:36+00:00" }, { "name": "doctrine/lexer", "version": "v1.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", "shasum": "" }, "require": { "php": ">=5.3.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", "homepage": "http://www.doctrine-project.org", "keywords": [ "lexer", "parser" ], "time": "2014-09-09T13:34:57+00:00" }, { "name": "friendsofphp/php-cs-fixer", "version": "v2.7.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", "reference": "ab2e189d94698178988f9732bc75bb4ce8d16f77" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ab2e189d94698178988f9732bc75bb4ce8d16f77", "reference": "ab2e189d94698178988f9732bc75bb4ce8d16f77", "shasum": "" }, "require": { "composer/semver": "^1.4", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", "gecko-packages/gecko-php-unit": "^2.0", "php": "^5.6 || >=7.0 <7.3", "php-cs-fixer/diff": "^1.0", "symfony/console": "^3.2", "symfony/event-dispatcher": "^3.0", "symfony/filesystem": "^3.0", "symfony/finder": "^3.0", "symfony/options-resolver": "^3.0", "symfony/polyfill-php70": "^1.0", "symfony/polyfill-php72": "^1.4", "symfony/process": "^3.0", "symfony/stopwatch": "^3.0" }, "conflict": { "hhvm": "*" }, "require-dev": { "johnkary/phpunit-speedtrap": "^1.1", "justinrainbow/json-schema": "^5.0", "php-cs-fixer/accessible-object": "^1.0", "phpunit/phpunit": "^4.8.35 || ^5.4.3", "satooshi/php-coveralls": "^1.0", "symfony/phpunit-bridge": "^3.2.2" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." }, "bin": [ "php-cs-fixer" ], "type": "application", "autoload": { "psr-4": { "PhpCsFixer\\": "src/" }, "classmap": [ "tests/Test/Assert/AssertTokensTrait.php", "tests/Test/AbstractFixerTestCase.php", "tests/Test/AbstractIntegrationTestCase.php", "tests/Test/IntegrationCase.php", "tests/Test/IntegrationCaseFactory.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Dariusz Rumiński", "email": "dariusz.ruminski@gmail.com" }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" } ], "description": "A tool to automatically fix PHP code style", "time": "2017-10-02T12:16:05+00:00" }, { "name": "gecko-packages/gecko-php-unit", "version": "v2.2", "source": { "type": "git", "url": "https://github.com/GeckoPackages/GeckoPHPUnit.git", "reference": "ab525fac9a9ffea219687f261b02008b18ebf2d1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/ab525fac9a9ffea219687f261b02008b18ebf2d1", "reference": "ab525fac9a9ffea219687f261b02008b18ebf2d1", "shasum": "" }, "require": { "php": "^5.3.6 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.4.3" }, "suggest": { "ext-dom": "When testing with xml.", "ext-libxml": "When testing with xml.", "phpunit/phpunit": "This is an extension for it so make sure you have it some way." }, "type": "library", "autoload": { "psr-4": { "GeckoPackages\\PHPUnit\\": "src/PHPUnit" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Additional PHPUnit asserts and constraints.", "homepage": "https://github.com/GeckoPackages", "keywords": [ "extension", "filesystem", "phpunit" ], "time": "2017-08-23T07:39:54+00:00" }, { "name": "monolog/monolog", "version": "1.23.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", "shasum": "" }, "require": { "php": ">=5.3.0", "psr/log": "~1.0" }, "provide": { "psr/log-implementation": "1.0.0" }, "require-dev": { "aws/aws-sdk-php": "^2.4.9 || ^3.0", "doctrine/couchdb": "~1.0@dev", "graylog2/gelf-php": "~1.0", "jakub-onderka/php-parallel-lint": "0.9", "php-amqplib/php-amqplib": "~2.4", "php-console/php-console": "^3.1.3", "phpunit/phpunit": "~4.5", "phpunit/phpunit-mock-objects": "2.3.0", "ruflin/elastica": ">=0.90 <3.0", "sentry/sentry": "^0.13", "swiftmailer/swiftmailer": "^5.3|^6.0" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "doctrine/couchdb": "Allow sending log messages to a CouchDB server", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", "ext-mongo": "Allow sending log messages to a MongoDB server", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server", "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http://seld.be" } ], "description": "Sends your logs to files, sockets, inboxes, databases and various web services", "homepage": "http://github.com/Seldaek/monolog", "keywords": [ "log", "logging", "psr-3" ], "time": "2017-06-19T01:22:40+00:00" }, { "name": "myclabs/deep-copy", "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", "shasum": "" }, "require": { "php": ">=5.4.0" }, "require-dev": { "doctrine/collections": "1.*", "phpunit/phpunit": "~4.1" }, "type": "library", "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", "homepage": "https://github.com/myclabs/DeepCopy", "keywords": [ "clone", "copy", "duplicate", "object", "object graph" ], "time": "2017-04-12T18:52:22+00:00" }, { "name": "paragonie/random_compat", "version": "v2.0.11", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", "shasum": "" }, "require": { "php": ">=5.2.0" }, "require-dev": { "phpunit/phpunit": "4.*|5.*" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", "autoload": { "files": [ "lib/random.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Paragon Initiative Enterprises", "email": "security@paragonie.com", "homepage": "https://paragonie.com" } ], "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", "pseudorandom", "random" ], "time": "2017-09-27T21:40:39+00:00" }, { "name": "phar-io/manifest", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "phar-io/version": "^1.0.1", "php": "^5.6 || ^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "time": "2017-03-05T18:14:27+00:00" }, { "name": "phar-io/version", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", "shasum": "" }, "require": { "php": "^5.6 || ^7.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Library for handling version information and constraints", "time": "2017-03-05T17:38:23+00:00" }, { "name": "php-cs-fixer/diff", "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/diff.git", "reference": "d068edadcb8f7bc2ea3d3769cdbaf609026ec4f4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/d068edadcb8f7bc2ea3d3769cdbaf609026ec4f4", "reference": "d068edadcb8f7bc2ea3d3769cdbaf609026ec4f4", "shasum": "" }, "require": { "php": "^5.6 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.4.3" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "sebastian/diff v2 backport support for PHP5.6", "homepage": "https://github.com/PHP-CS-Fixer", "keywords": [ "diff" ], "time": "2017-09-23T16:02:08+00:00" }, { "name": "phpdocumentor/reflection-common", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { "php": ">=5.5" }, "require-dev": { "phpunit/phpunit": "^4.6" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ "src" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jaap van Otterdijk", "email": "opensource@ijaap.nl" } ], "description": "Common reflection classes used by phpdocumentor to reflect the code structure", "homepage": "http://www.phpdoc.org", "keywords": [ "FQSEN", "phpDocumentor", "phpdoc", "reflection", "static analysis" ], "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", "version": "4.1.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", "shasum": "" }, "require": { "php": "^7.0", "phpdocumentor/reflection-common": "^1.0@dev", "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { "mockery/mockery": "^0.9.4", "phpunit/phpunit": "^4.4" }, "type": "library", "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ "src/" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Mike van Riel", "email": "me@mikevanriel.com" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "time": "2017-08-30T18:51:59+00:00" }, { "name": "phpdocumentor/type-resolver", "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { "mockery/mockery": "^0.9.4", "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ "src/" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Mike van Riel", "email": "me@mikevanriel.com" } ], "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", "version": "v1.7.2", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", "sebastian/comparator": "^1.1|^2.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.7.x-dev" } }, "autoload": { "psr-0": { "Prophecy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", "homepage": "http://everzet.com" }, { "name": "Marcello Duarte", "email": "marcello.duarte@gmail.com" } ], "description": "Highly opinionated mocking framework for PHP 5.3+", "homepage": "https://github.com/phpspec/prophecy", "keywords": [ "Double", "Dummy", "fake", "mock", "spy", "stub" ], "time": "2017-09-04T11:05:03+00:00" }, { "name": "phpunit/php-code-coverage", "version": "5.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "8ed1902a57849e117b5651fc1a5c48110946c06b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/8ed1902a57849e117b5651fc1a5c48110946c06b", "reference": "8ed1902a57849e117b5651fc1a5c48110946c06b", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", "php": "^7.0", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", "phpunit/php-token-stream": "^1.4.11 || ^2.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^3.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { "ext-xdebug": "^2.5", "phpunit/phpunit": "^6.0" }, "suggest": { "ext-xdebug": "^2.5.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.2.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "time": "2017-08-03T12:40:43+00:00" }, { "name": "phpunit/php-file-iterator", "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "time": "2016-10-03T07:40:28+00:00" }, { "name": "phpunit/php-text-template", "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.2.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Wrapper around PHP's tokenizer extension.", "homepage": "https://github.com/sebastianbergmann/php-token-stream/", "keywords": [ "tokenizer" ], "time": "2017-08-20T05:47:52+00:00" }, { "name": "phpunit/phpunit", "version": "6.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "ff3a76a58ac293657808aefd58c8aaf05945f4d9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ff3a76a58ac293657808aefd58c8aaf05945f4d9", "reference": "ff3a76a58ac293657808aefd58c8aaf05945f4d9", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "myclabs/deep-copy": "^1.3", "phar-io/manifest": "^1.0.1", "phar-io/version": "^1.0", "php": "^7.0", "phpspec/prophecy": "^1.7", "phpunit/php-code-coverage": "^5.2", "phpunit/php-file-iterator": "^1.4", "phpunit/php-text-template": "^1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^4.0", "sebastian/comparator": "^2.0", "sebastian/diff": "^1.4.3", "sebastian/environment": "^3.0.2", "sebastian/exporter": "^3.1", "sebastian/global-state": "^1.1 || ^2.0", "sebastian/object-enumerator": "^3.0.2", "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0" }, "conflict": { "phpdocumentor/reflection-docblock": "3.0.2", "phpunit/dbunit": "<3.0" }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", "phpunit/php-invoker": "^1.1" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "6.2.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], "time": "2017-08-03T13:59:28+00:00" }, { "name": "phpunit/phpunit-mock-objects", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", "reference": "2f789b59ab89669015ad984afa350c4ec577ade0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/2f789b59ab89669015ad984afa350c4ec577ade0", "reference": "2f789b59ab89669015ad984afa350c4ec577ade0", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", "php": "^7.0", "phpunit/php-text-template": "^1.2.1", "sebastian/exporter": "^3.0" }, "conflict": { "phpunit/phpunit": "<6.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "suggest": { "ext-soap": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Mock Object library for PHPUnit", "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", "keywords": [ "mock", "xunit" ], "time": "2017-08-03T14:08:16+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "20f84f468cb67efee293246e6a09619b891f55f0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/20f84f468cb67efee293246e6a09619b891f55f0", "reference": "20f84f468cb67efee293246e6a09619b891f55f0", "shasum": "" }, "require": { "php": "^7.0", "sebastian/diff": "^1.2", "sebastian/exporter": "^3.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "http://www.github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "time": "2017-03-03T06:26:08+00:00" }, { "name": "sebastian/diff", "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff" ], "time": "2017-05-22T07:24:03+00:00" }, { "name": "sebastian/environment", "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], "time": "2017-07-01T08:51:00+00:00" }, { "name": "sebastian/exporter", "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { "php": "^7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "time": "2017-04-03T13:19:02+00:00" }, { "name": "sebastian/global-state", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "time": "2017-04-27T15:39:26+00:00" }, { "name": "sebastian/object-enumerator", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { "php": "^7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "time": "2017-08-03T12:35:26+00:00" }, { "name": "sebastian/object-reflector", "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", "time": "2017-03-29T09:07:27+00:00" }, { "name": "sebastian/recursion-context", "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "time": "2017-03-03T06:23:57+00:00" }, { "name": "sebastian/resource-operations", "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", "shasum": "" }, "require": { "php": ">=5.6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "time": "2015-07-28T20:34:47+00:00" }, { "name": "sebastian/version", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, "require": { "php": ">=5.6" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/console", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", "reference": "a1e1b01293a090cb9ae2ddd221a3251a4a7e4abf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/console/zipball/a1e1b01293a090cb9ae2ddd221a3251a4a7e4abf", "reference": "a1e1b01293a090cb9ae2ddd221a3251a4a7e4abf", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", "symfony/debug": "~2.8|~3.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~3.3", "symfony/dependency-injection": "~3.3", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/filesystem": "~2.8|~3.0", "symfony/process": "~2.8|~3.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/filesystem": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Console Component", "homepage": "https://symfony.com", "time": "2017-09-06T16:40:18+00:00" }, { "name": "symfony/debug", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", "reference": "8beb24eec70b345c313640962df933499373a944" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/debug/zipball/8beb24eec70b345c313640962df933499373a944", "reference": "8beb24eec70b345c313640962df933499373a944", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { "symfony/http-kernel": "~2.8|~3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", "time": "2017-09-01T13:23:39+00:00" }, { "name": "symfony/event-dispatcher", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", "reference": "54ca9520a00386f83bca145819ad3b619aaa2485" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/54ca9520a00386f83bca145819ad3b619aaa2485", "reference": "54ca9520a00386f83bca145819ad3b619aaa2485", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "conflict": { "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~2.8|~3.0", "symfony/dependency-injection": "~3.3", "symfony/expression-language": "~2.8|~3.0", "symfony/stopwatch": "~2.8|~3.0" }, "suggest": { "symfony/dependency-injection": "", "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", "time": "2017-07-29T21:54:42+00:00" }, { "name": "symfony/filesystem", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", "reference": "b32a0e5f928d0fa3d1dd03c78d020777e50c10cb" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/b32a0e5f928d0fa3d1dd03c78d020777e50c10cb", "reference": "b32a0e5f928d0fa3d1dd03c78d020777e50c10cb", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", "time": "2017-07-29T21:54:42+00:00" }, { "name": "symfony/finder", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/finder/zipball/b2260dbc80f3c4198f903215f91a1ac7fe9fe09e", "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", "time": "2017-07-29T21:54:42+00:00" }, { "name": "symfony/options-resolver", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", "reference": "ee4e22978fe885b54ee5da8c7964f0a5301abfb6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ee4e22978fe885b54ee5da8c7964f0a5301abfb6", "reference": "ee4e22978fe885b54ee5da8c7964f0a5301abfb6", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony OptionsResolver Component", "homepage": "https://symfony.com", "keywords": [ "config", "configuration", "options" ], "time": "2017-07-29T21:54:42+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803", "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", "mbstring", "polyfill", "portable", "shim" ], "time": "2017-06-14T15:44:48+00:00" }, { "name": "symfony/polyfill-php70", "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", "reference": "b6482e68974486984f59449ecea1fbbb22ff840f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/b6482e68974486984f59449ecea1fbbb22ff840f", "reference": "b6482e68974486984f59449ecea1fbbb22ff840f", "shasum": "" }, "require": { "paragonie/random_compat": "~1.0|~2.0", "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Php70\\": "" }, "files": [ "bootstrap.php" ], "classmap": [ "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "time": "2017-06-14T15:44:48+00:00" }, { "name": "symfony/polyfill-php72", "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", "reference": "8abc9097f5001d310f0edba727469c988acc6ea7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/8abc9097f5001d310f0edba727469c988acc6ea7", "reference": "8abc9097f5001d310f0edba727469c988acc6ea7", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Php72\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "time": "2017-07-11T13:25:55+00:00" }, { "name": "symfony/process", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/process.git", "reference": "b7666e9b438027a1ea0e1ee813ec5042d5d7f6f0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/b7666e9b438027a1ea0e1ee813ec5042d5d7f6f0", "reference": "b7666e9b438027a1ea0e1ee813ec5042d5d7f6f0", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Process Component", "homepage": "https://symfony.com", "time": "2017-07-29T21:54:42+00:00" }, { "name": "symfony/stopwatch", "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", "reference": "9a5610a8d6a50985a7be485c0ba745c22607beeb" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/9a5610a8d6a50985a7be485c0ba745c22607beeb", "reference": "9a5610a8d6a50985a7be485c0ba745c22607beeb", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", "time": "2017-07-29T21:54:42+00:00" }, { "name": "theseer/tokenizer", "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": "^7.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "time": "2017-04-07T12:08:54+00:00" }, { "name": "webmozart/assert", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.6", "sebastian/version": "^1.0.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.3-dev" } }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ "assert", "check", "validate" ], "time": "2016-11-23T20:04:58+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.1" }, "platform-dev": [] } doc/specifications-support.md 0000666 00000004224 13165210352 0012354 0 ustar 00 # synopsis This document explains which attributes are supported by feed-io and how to access them ## top level document : feed (atom) / channel (rss) interface : FeedInterface | atom | rss | getter | setter | | ---- | --- | ------ | ------ | | title | title | getTitle | setTitle | | link | link | getLink | setLink | | updated | pubDate / lastBuildDate | getLastModified | setLastModified | | id | N/A | getPublicId | setPublicId | | N/A | description | getDescription | setDescription | | category | category | getCategories | addCategory | | author | author | getAuthor | setAuthor | | contributor | N/A | not supported | not supported | | icon / logo | image | not supported | not supported | | rights | copyright | not supported | not supported | | subtitle | N/A | not supported | not supported | | lang | language | not supported | not supported | | base | N/A | not supported | not supported | | generator | generator | not supported | not supported | | N/A | managingEditor | not supported | not supported | | N/A | webMaster | not supported | not supported | | N/A | docs | not supported | not supported | | N/A | cloud | not supported | not supported | | N/A | ttl | not supported | not supported | | N/A | rating | not supported | not supported | | N/A | textInput | not supported | not supported | | N/A | skipdays | not supported | not supported | | N/A | skipHours | not supported | not supported | ## entry (atom) / item (rss) Interface : ItemInterface | atom | rss | getter | setter | | ---- | --- | ------ | ------ | | title | title | getTitle | setTitle | | link | link | getLink | setLink | | link | enclosure | getMedias | addMedia | | updated / published | pubDate | getLastModified | setLastModified | | id | guid | getPublicId | setPublicId | | content | description | getDescription | setDescription | | summary | N/A | not supported | not supported | | source | source | not supported | not supported | | category | category | getCategories | addCategory | | author | N/A | getAuthor | setAuthor | | contributor | N/A | not supported | not supported | | N/A | comments | not supported | not supported | | rights | N/A | not supported | not supported | examples/bootstrap.php 0000666 00000000511 13165210352 0011107 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ $loader = require __DIR__."/../vendor/autoload.php"; date_default_timezone_set('UTC'); examples/factory.php 0000666 00000001556 13165210352 0010553 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $feedIo = \FeedIo\Factory::create()->getFeedIo(); $result = $feedIo->read('http://php.net/feed.atom'); echo "feed title : {$result->getFeed()->getTitle()} \n "; foreach ($result->getFeed() as $item) { echo "item title : {$item->getTitle()} \n "; // let's turn php.net into a PodCast $media = new \FeedIo\Feed\Item\Media(); $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3'); $media->setType('audio/mpeg'); // add it to the item $item->addMedia($media); } $domDocument = $feedIo->toAtom($result->getFeed()); echo $domDocument->saveXML(); examples/feed-creation.php 0000666 00000001734 13165210352 0011607 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; use \FeedIo\Factory; use \FeedIo\Feed; $feed = new Feed(); $feed->setLink('https://feed-io.net'); $feed->setTitle('feed-io example feed'); // The item instance SHOULD be instanciated by the feed $item = $feed->newItem(); $item->setTitle('a title'); $item->setLastModified(new \DateTime()); $item->setLink('https://feed-io.net/item/1'); $item->setDescription("Hope you like the code you are reading"); $feed->add($item); $feedIo = Factory::create()->getFeedIo(); echo 'ATOM' . PHP_EOL; echo $feedIo->format($feed, 'atom'); echo PHP_EOL; echo 'RSS' . PHP_EOL; echo $feedIo->format($feed, 'rss'); echo PHP_EOL; echo 'JSON Feed' . PHP_EOL; echo $feedIo->format($feed, 'json'); echo PHP_EOL; examples/feedio.php 0000666 00000001716 13165210352 0010335 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $client = new \FeedIo\Adapter\Guzzle\Client(new GuzzleHttp\Client()); $logger = new \Psr\Log\NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); $result = $feedIo->read('http://php.net/feed.atom'); echo "feed title : {$result->getFeed()->getTitle()} \n "; foreach ($result->getFeed() as $item) { echo "item title : {$item->getTitle()} \n "; // let's turn php.net into a PodCast $media = new \FeedIo\Feed\Item\Media(); $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3'); $media->setType('audio/mpeg'); // add it to the item $item->addMedia($media); } $domDocument = $feedIo->toAtom($result->getFeed()); echo $domDocument; examples/force-timezone.php 0000666 00000002675 13165210352 0012035 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $feedIo = \FeedIo\Factory::create()->getFeedIo(); $feedIo->getDateTimeBuilder()->setFeedTimezone(new \DateTimeZone('-0500')); $result = $feedIo->read('http://news.php.net/group.php?group=php.announce&format=rss'); echo "First item's pubDate raw value in the feed" .PHP_EOL; $domItems = $result->getDocument()->getDOMDocument()->getElementsByTagName('item'); /** @var \DOMElement $firstDomItem */ $firstDomItem = $domItems->item(0); $pubDate = $firstDomItem->getElementsByTagName('pubDate')->item(0); var_dump($pubDate->nodeValue); $pubDateTime = new \DateTime($pubDate->nodeValue); echo "here is its timestamp : {$pubDateTime->getTimestamp()}" . PHP_EOL; $feed = $result->getFeed(); /** @var \FeedIo\Feed\ItemInterface $item */ $feed->rewind(); $item = $feed->current(); echo "var_dump the first item's pubDate after parsing. It's the same date converted in your local configuration's timezone" . PHP_EOL; var_dump($item->getLastModified()); echo "here is its timestamp : {$item->getLastModified()->getTimestamp()}" . PHP_EOL; if($pubDateTime->getTimestamp() === $item->getLastModified()->getTimestamp()) { echo "HOURAY, both timestamps match !" . PHP_EOL; } examples/guzzle-client.php 0000666 00000000731 13165210352 0011672 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $client = new \FeedIo\Adapter\Guzzle\Client(new GuzzleHttp\Client()); $response = $client->getResponse('http://php.net/feed.atom', new \DateTime()); echo $response->getBody(); examples/jsonfeed.php 0000666 00000001541 13165210352 0010673 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $client = new \FeedIo\Adapter\Guzzle\Client(new GuzzleHttp\Client()); $logger = new \Psr\Log\NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); $items = [ getItem('item 1', 'Lorem Ipsum'), getItem('item 2', '
Foo Bar
'), ]; $feed = new \FeedIo\Feed(); $feed->setTitle('feed title'); foreach($items as $item) { $feed->add($item); } echo $feedIo->format($feed, 'json'); function getItem($title, $description) { $item = new \FeedIo\Feed\Item(); $item->setTitle($title); $item->setDescription($description); return $item; } examples/media-reading.php 0000666 00000001212 13165210352 0011557 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $client = new \FeedIo\Adapter\FileSystem\Client(); $logger = new \Psr\Log\NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); $result = $feedIo->read(dirname(__FILE__).'/../tests/samples/enclosure-atom.xml'); $feed = $result->getFeed(); foreach($feed as $item) { foreach($item->getMedias() as $media) { var_dump($media); } } examples/media.php 0000666 00000001500 13165210352 0010150 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $feed = new \FeedIo\Feed(); // ask for a new Item $item = $feed->newItem(); // build the media $media = new \FeedIo\Feed\Item\Media(); $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3'); $media->setType('audio/mpeg'); // add it to the item $item->addMedia($media); $item->setLink('http://yourdomain.tld/item/1'); // add the item to the feed $feed->add($item); $client = new \FeedIo\Adapter\NullClient(); $logger = new \Psr\Log\NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); echo $feedIo->toAtom($feed); examples/missing-timezone.php 0000666 00000002413 13165210352 0012376 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; echo 'see https://github.com/alexdebril/feed-io/issues/134 for full explanations' . PHP_EOL; $dateTimeBuilder = new \FeedIo\Rule\DateTimeBuilder(); $pubDate = 'Wed, 02 Aug 2017 07:21:29'; echo 'the publish date\'s timezone is America/chicago but $dateTimeBuilder ignores it. The date below is wrong' . PHP_EOL; $dateTime = $dateTimeBuilder->convertToDateTime($pubDate); var_dump($dateTime); echo "timestamp : {$dateTime->getTimestamp()}" . PHP_EOL; echo 'after setting the feed\'s timezone in the builder\'s attributes, the date is right' . PHP_EOL; $dateTimeBuilder->setFeedTimezone(new \DateTimeZone('America/Chicago')); $dateTime = $dateTimeBuilder->convertToDateTime($pubDate); var_dump($dateTime); echo "timestamp : {$dateTime->getTimestamp()}" . PHP_EOL; echo 'if I really want the date in its original timezone, I can set it' . PHP_EOL; $dateTime->setTimezone(new \DateTimeZone('America/Chicago')); var_dump($dateTime); echo "timestamp : {$dateTime->getTimestamp()}" . PHP_EOL; examples/modifiedSince.php 0000666 00000001374 13165210352 0011644 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $client = new \FeedIo\Adapter\Guzzle\Client(new GuzzleHttp\Client()); $logger = new \Psr\Log\NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); $result = $feedIo->readSince('http://php.net/feed.atom', new \DateTime('-1 month')); echo "feed title : {$result->getFeed()->getTitle()} \n "; $newItems = $result->getFeed(); foreach($newItems as $value) { echo $value->getTitle() . ' : ' . $value->getLastModified()->format(\DateTime::ATOM) . PHP_EOL; } examples/parse-rss.php 0000666 00000002552 13165210352 0011020 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; // new DateTimeBuilder : it will be used by the parser to convert formatted dates into DateTime instances $dateTimeBuilder = new \FeedIo\Rule\DateTimeBuilder(); // new Standard\\Rss : it will provide all standard specific rules to the parser $standard = new \FeedIo\Standard\Rss($dateTimeBuilder); // new Parser: it will turn a RSS stream into a Feed instance, using the rules provided by the Standard // the Logger must implement the PSR3 logging standard $parser = new \FeedIo\Parser($standard, new \Psr\Log\NullLogger()); // the file is sample-rss.xml $file = dirname(__FILE__)."/../tests/samples/rss/sample-rss.xml"; // we load it using the Dom library $document = new DOMDocument(); $document->load($file, LIBXML_NOBLANKS | LIBXML_COMPACT); // Now let's parse it // The second argument must implement FeedInterface $feed = $parser->parse($document, new \FeedIo\Feed()); // $feed is now ready echo "feed's title : {$feed->getTitle()} \n"; // FeedInterface extends \Iterator, we can iterate through it foreach ($feed as $item) { echo "item's title : {$item->getTitle()} \n"; } examples/rich-feed.php 0000666 00000001612 13165210352 0010723 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require __DIR__.DIRECTORY_SEPARATOR.'bootstrap.php'; $feedIo = \FeedIo\Factory::create()->getFeedIo(); $result = $feedIo->read('https://debril.org/feed/'); echo "feed title : {$result->getFeed()->getTitle()} \n "; foreach ($result->getFeed() as $item) { echo "item title : {$item->getTitle()} \n "; foreach ($item->getAllElements() as $element) { echo "element name : " . $element->getName() . PHP_EOL; foreach( $element->getAllElements() as $subElement) { echo "sub element name : " . $subElement->getName() . PHP_EOL; echo "sub element value : " . $subElement->getValue() . PHP_EOL; } } } phpunit-feeds.xml 0000666 00000000615 13165210352 0010045 0 ustar 00
* // will display http://example.org/video.mpeg
* echo $media->getUrl();
*
*/
interface MediaInterface
{
/**
* @return string
*/
public function getType() : ? string;
/**
* @param string $type
* @return MediaInterface
*/
public function setType(string $type = null) : MediaInterface;
/**
* @return string
*/
public function getUrl() : ? string;
/**
* @param string $url
* @return MediaInterface
*/
public function setUrl(string $url = null) : MediaInterface;
/**
* @return string
*/
public function getLength() : ? string;
/**
* @param string $length
* @return MediaInterface
*/
public function setLength(string $length = null) : MediaInterface;
}
src/FeedIo/Feed/ItemInterface.php 0000666 00000004257 13165210352 0012573 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed;
use FeedIo\Feed\Item\Author;
use FeedIo\Feed\Item\MediaInterface;
use FeedIo\Feed\Item\AuthorInterface;
/**
* Describes an Item instance
*
* an item holds three types of properties :
* - basic values inherited from the NodeInterface like title, description, URL
* - MediaInterface instances for medias like videos, images and podcasts
* - ElementInterface instances for nodes not related to a known property of the ItemInterface instance
*
* ElementInterface instances are accessed using two methods :
*
* - ItemInterface::getElementIterator($name). Use it to read an array of elements or if you need to get an ElementInterface instance
* - ItemInterface::getValue($name). use it to get the element's v lue
*
*/
interface ItemInterface extends NodeInterface
{
/**
* adds $media to the object's attributes
*
* @param MediaInterface $media
* @return ItemInterface
*/
public function addMedia(MediaInterface $media) : ItemInterface;
/**
* returns the current object's medias
*
* @return iterable
*/
public function getMedias() : iterable;
/**
* returns true if at least one MediaInterface exists in the object's attributes
*
* @return boolean
*/
public function hasMedia() : bool;
/**
* returns a new MediaInterface
*
* @return MediaInterface
*/
public function newMedia() : MediaInterface;
/**
* returns the author attribute
*
* @return AuthorInterface
*/
public function getAuthor() : ? AuthorInterface;
/**
* sets $author to the object's attributes
*
* @param AuthorInterface $author
* @return ItemInterface
*/
public function setAuthor(AuthorInterface $author = null) : ItemInterface;
/**
* returns a new AuthorInterface
*
* @return AuthorInterface
*/
public function newAuthor() : AuthorInterface;
}
src/FeedIo/Feed/Node.php 0000666 00000011362 13165210352 0010734 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed;
use FeedIo\Feed\Node\Category;
use FeedIo\Feed\Node\CategoryInterface;
class Node implements NodeInterface, ElementsAwareInterface
{
use ElementsAwareTrait;
/**
* @var \ArrayIterator
*/
protected $categories;
/**
* @var string
*/
protected $title;
/**
* @var string
*/
protected $publicId;
/**
* @var string
*/
protected $description;
/**
* @var \DateTime
*/
protected $lastModified;
/**
* @var string
*/
protected $link;
public function __construct()
{
$this->initElements();
$this->categories = new \ArrayIterator();
}
/**
* @param string $name element name
* @param string $value element value
* @return NodeInterface
*/
public function set(string $name, string $value = null) : NodeInterface
{
$element = $this->newElement();
$element->setName($name);
$element->setValue($value);
$this->addElement($element);
return $this;
}
/**
* returns node's categories
*
* @return iterable
*/
public function getCategories() : iterable
{
return $this->categories;
}
/**
* @return \Generator
*/
public function getCategoriesGenerator() : \Generator
{
foreach ($this->categories as $category) {
yield $category->getlabel();
}
}
/**
* adds a category to the node
*
* @param \FeedIo\Feed\Node\CategoryInterface $category
* @return NodeInterface
*/
public function addCategory(CategoryInterface $category) : NodeInterface
{
$this->categories->append($category);
return $this;
}
/**
* returns a new CategoryInterface
*
* @return \FeedIo\Feed\Node\CategoryInterface
*/
public function newCategory() : CategoryInterface
{
return new Category();
}
/**
* @return string
*/
public function getTitle() : ? string
{
return $this->title;
}
/**
* @param string $title
* @return NodeInterface
*/
public function setTitle(string $title = null) : NodeInterface
{
$this->title = $title;
return $this;
}
/**
* @return string
*/
public function getPublicId() : ? string
{
return $this->publicId;
}
/**
* @param string $publicId
* @return NodeInterface
*/
public function setPublicId(string $publicId = null) : NodeInterface
{
$this->publicId = $publicId;
return $this;
}
/**
* @return string
*/
public function getDescription() : ? string
{
return $this->description;
}
/**
* @param string $description
* @return NodeInterface
*/
public function setDescription(string $description = null) : NodeInterface
{
$this->description = $description;
return $this;
}
/**
* @return \DateTime
*/
public function getLastModified() : ? \DateTime
{
return $this->lastModified;
}
/**
* @param \DateTime $lastModified
* @return NodeInterface
*/
public function setLastModified(\DateTime $lastModified = null) : NodeInterface
{
$this->lastModified = $lastModified;
return $this;
}
/**
* @return string
*/
public function getLink() : ? string
{
return $this->link;
}
/**
* @param string $link
* @return NodeInterface
*/
public function setLink(string $link = null) : NodeInterface
{
$this->link = $link;
return $this;
}
/**
* @param string $name element name
* @return null|string
*/
public function getValue(string $name) : ? string
{
foreach ($this->getElementIterator($name) as $element) {
return $element->getValue();
}
return null;
}
/**
* @return array
*/
public function toArray() : array
{
$properties = get_object_vars($this);
foreach ($properties as $name => $property) {
if ($property instanceof \DateTime) {
$properties[$name] = $property->format(\DateTime::ATOM);
}
}
$properties['elements'] = iterator_to_array($this->getElementsGenerator());
$properties['categories'] = iterator_to_array($this->getCategoriesGenerator());
return $properties;
}
}
src/FeedIo/Feed/Node/Category.php 0000666 00000003011 13165210352 0012501 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed\Node;
class Category implements CategoryInterface
{
/**
* @var string
*/
protected $term;
/**
* @var string
*/
protected $scheme;
/**
* @var string
*/
protected $label;
/**
* @return null|string
*/
public function getTerm() : ? string
{
return $this->term;
}
/**
* @param string $term
* @return CategoryInterface
*/
public function setTerm(string $term = null) : CategoryInterface
{
$this->term = $term;
return $this;
}
/**
* @return null|string
*/
public function getScheme() : ? string
{
return $this->scheme;
}
/**
* @param string $scheme
* @return CategoryInterface
*/
public function setScheme(string $scheme = null) : CategoryInterface
{
$this->scheme = $scheme;
return $this;
}
/**
* @return null|string
*/
public function getLabel() : ? string
{
return $this->label;
}
/**
* @param string $label
* @return CategoryInterface
*/
public function setLabel(string $label = null) : CategoryInterface
{
$this->label = $label;
return $this;
}
}
src/FeedIo/Feed/Node/CategoryInterface.php 0000666 00000002073 13165210352 0014331 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed\Node;
/**
* Describe a Category instance
*
*/
interface CategoryInterface
{
/**
* @return null|string
*/
public function getTerm() : ? string;
/**
* @param string $term
* @return CategoryInterface
*/
public function setTerm(string $term = null) : CategoryInterface;
/**
* @return null|string
*/
public function getScheme() : ? string;
/**
* @param string $scheme
* @return CategoryInterface
*/
public function setScheme(string $scheme = null) : CategoryInterface;
/**
* @return null|string
*/
public function getLabel() : ? string;
/**
* @param string $label
* @return CategoryInterface
*/
public function setLabel(string $label = null) : CategoryInterface;
}
src/FeedIo/Feed/Node/Element.php 0000666 00000004030 13165210352 0012317 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed\Node;
use FeedIo\Feed\ElementsAwareInterface;
use FeedIo\Feed\ElementsAwareTrait;
class Element implements ElementInterface, ElementsAwareInterface
{
use ElementsAwareTrait;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $value;
/**
* @var array
*/
protected $attributes = array();
public function __construct()
{
$this->initElements();
}
/**
* @return string
*/
public function getName() : string
{
return $this->name;
}
/**
* @param string $name
* @return ElementInterface
*/
public function setName(string $name) : ElementInterface
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getValue() : ? string
{
return $this->value;
}
/**
* @param string $value
* @return ElementInterface
*/
public function setValue(string $value = null) : ElementInterface
{
$this->value = $value;
return $this;
}
/**
* @param string $name
* @return null|string
*/
public function getAttribute(string $name) : ? string
{
if (array_key_exists($name, $this->attributes)) {
return $this->attributes[$name];
}
return null;
}
/**
* @return iterable
*/
public function getAttributes() : iterable
{
return $this->attributes;
}
/**
* @param string $name
* @param string $value
* @return ElementInterface
*/
public function setAttribute(string $name, string $value = null) : ElementInterface
{
$this->attributes[$name] = $value;
return $this;
}
}
src/FeedIo/Feed/Node/ElementInterface.php 0000666 00000003372 13165210352 0014150 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed\Node;
use PharIo\Manifest\ElementCollection;
/**
* Describe an Element instance
*
* $name matches the node's tag name
* $value matches the node's content
* each attribute matches an attribute of the node
*
* for example, to represent this XML node
*
*
* $item->setName('media');
* $item->setValue('http://example.org/some-sound.mp3');
* $item->setAttribute('lenght', 45668);
* $item->setAttribute('type', 'audio/mpeg');
*
*
*/
interface ElementInterface
{
/**
* @return string
*/
public function getName() : string;
/**
* @param string $name
* @return ElementInterface
*/
public function setName(string $name) : ElementInterface;
/**
* @return string
*/
public function getValue() : ? string;
/**
* @param string $value
* @return ElementInterface
*/
public function setValue(string $value = null) : ElementInterface;
/**
* @param string $name
* @return string
*/
public function getAttribute(string $name) : ? string;
/**
* @return iterable
*/
public function getAttributes() : iterable;
/**
* @param string $name
* @param string $value
* @return ElementInterface
*/
public function setAttribute(string $name, string $value = null) : ElementInterface;
}
src/FeedIo/Feed/Node/ElementIterator.php 0000666 00000002415 13165210352 0014036 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed\Node;
/**
* Iterator to filter elements by name
* @see \FilterIterator
*/
class ElementIterator extends \FilterIterator
{
/**
* @var string $name Element name to accept
*/
protected $name;
/**
* @param \Iterator $iterator Set of elements to filter
* @param string $name Element name to accept
*/
public function __construct(\Iterator $iterator, string $name)
{
parent::__construct($iterator);
$this->name = $name;
}
/**
* override PHP's count implementation.
* @return int
*/
public function count() : int
{
$count = 0;
foreach ($this as $node) {
$count++;
}
return $count;
}
/**
* @return boolean True if the current element's name matches the expected one
*/
public function accept() : bool
{
$element = $this->getInnerIterator()->current();
return (0 == strcasecmp($this->name, $element->getName()));
}
}
src/FeedIo/Feed/NodeInterface.php 0000666 00000005737 13165210352 0012566 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Feed;
use FeedIo\Feed\Node\CategoryInterface;
/**
* Describes a node instance
*
* A node exposes attributes which are common to feeds and items
*/
interface NodeInterface
{
/**
* Returns node's title
*
* @return string
*/
public function getTitle() : ? string;
/**
* Sets nodes's title
*
* @param string $title
* @return NodeInterface
*/
public function setTitle(string $title = null) : NodeInterface;
/**
* Returns node's public id
*
* @return string
*/
public function getPublicId() : ? string;
/**
* sets node's public id
*
* @param string $id
* @return NodeInterface
*/
public function setPublicId(string $id = null) : NodeInterface;
/**
* Returns node's description
*
* @return string
*/
public function getDescription() : ? string;
/**
* Sets node's description
*
* @param string $description
* @return NodeInterface
*/
public function setDescription(string $description = null) : NodeInterface;
/**
* Returns the node's last modified date
*
* @return \DateTime
*/
public function getLastModified() : ? \DateTime;
/**
* Sets the node's last modified date
*
* @param \DateTime $lastModified
* @return NodeInterface
*/
public function setLastModified(\DateTime $lastModified = null) : NodeInterface;
/**
* Returns the node's link
*
* @return string
*/
public function getLink() : ? string;
/**
* Sets the nodes's link
*
* @param string $link
* @return NodeInterface
*/
public function setLink(string $link = null) : NodeInterface;
/**
* returns node's categories
*
* @return iterable
*/
public function getCategories() : iterable;
/**
* adds a category to the node
*
* @param \FeedIo\Feed\Node\CategoryInterface $category
* @return NodeInterface
*/
public function addCategory(CategoryInterface $category) : NodeInterface;
/**
* returns a new CategoryInterface
*
* @return \FeedIo\Feed\Node\CategoryInterface
*/
public function newCategory() : CategoryInterface;
/**
* returns an element's value
*
* @param string $name element name
* @return string
*/
public function getValue(string $name) : ? string;
/**
* creates a new ElementInterface called $name and sets its value to $value
*
* @param string $name element name
* @param string $value element value
* @return NodeInterface
*/
public function set(string $name, string $value = null) : NodeInterface;
}
src/FeedIo/FeedInterface.php 0000666 00000002160 13165210352 0011664 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\Feed\NodeInterface;
use FeedIo\Feed\ItemInterface;
use FeedIo\FeedInterface;
/**
* Interface FeedInterface
* Represents the top node of a news feed
* @package FeedIo
*/
interface FeedInterface extends \Iterator, \Countable, NodeInterface
{
/**
* This method MUST return the feed's full URL
* @return string
*/
public function getUrl() : ? string;
/**
* @param string $url
* @return FeedInterface
*/
public function setUrl(string $url = null) : FeedInterface;
/**
* Atom : feed.entry
* // $client is a \FeedIo\Adapter\ClientInterface instance, $logger a \Psr\Log\LoggerInterface
* $feedIo = new FeedIo($client, $logger);
*
* // read a feed. Output is a Result instance
* $result = $feedIo->read('http://somefeed.org/feed.rss');
*
* // use the feed
* $feed = $result->getFeed();
* echo $feed->getTitle();
*
* // and its items
* foreach ( $feed as $item ) {
* echo $item->getTitle();
* echo $item->getDescription();
* }
*
*
*
*
* // build the feed to publish
* $feed = new \FeedIo\Feed;
* $feed->setTitle('title');
* // ...
*
* // add items to it
* $item = new \FeedIo\Feed\Item
* $item->setTitle('my great post');
*
* // want to publish a media ? no problem
* $media = new \FeedIo\Feed\Item\Media
* $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3');
* $media->setType('audio/mpeg');
*
* // add it to the item
* $item->addMedia($media);
*
* // add the item to the feed (almost there)
* $feed->add($item);
*
* // format it in atom
* $feedIo->toAtom($feed);
*
*
*/
class FeedIo
{
/**
* @var \FeedIo\Reader
*/
protected $reader;
/**
* @var \FeedIo\Rule\DateTimeBuilder
*/
protected $dateTimeBuilder;
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var array
*/
protected $standards;
/**
* @var \FeedIo\Reader\FixerSet
*/
protected $fixerSet;
/**
* @param \FeedIo\Adapter\ClientInterface $client
* @param \Psr\Log\LoggerInterface $logger
*/
public function __construct(ClientInterface $client, LoggerInterface $logger)
{
$this->logger = $logger;
$this->dateTimeBuilder = new DateTimeBuilder($logger);
$this->setReader(new Reader($client, $logger));
$this->loadCommonStandards();
$this->loadFixerSet();
}
/**
* Loads main standards (RSS, RDF, Atom) in current object's attributes
*
* @return FeedIo
*/
protected function loadCommonStandards() : FeedIo
{
$standards = $this->getCommonStandards();
foreach ($standards as $name => $standard) {
$this->addStandard($name, $standard);
}
return $this;
}
/**
* adds a filter to the reader
*
* @param \FeedIo\FilterInterface $filter
* @return FeedIo
*/
public function addFilter(FilterInterface $filter) : FeedIo
{
$this->getReader()->addFilter($filter);
return $this;
}
/**
* Returns main standards
*
* @return array
*/
public function getCommonStandards() : array
{
$loader = new Loader();
return $loader->getCommonStandards($this->getDateTimeBuilder());
}
/**
* @param string $name
* @param \FeedIo\StandardAbstract $standard
* @return FeedIo
*/
public function addStandard(string $name, StandardAbstract $standard) : FeedIo
{
$name = strtolower($name);
$this->standards[$name] = $standard;
$parser = $this->newParser($standard->getSyntaxFormat(), $standard);
$this->reader->addParser($parser);
return $this;
}
/**
* @param string $format
* @param StandardAbstract $standard
* @return ParserAbstract
*/
public function newParser(string $format, StandardAbstract $standard) : ParserAbstract
{
$reflection = new \ReflectionClass("FeedIo\\Parser\\{$format}Parser");
if (! $reflection->isSubclassOf('FeedIo\ParserAbstract')) {
throw new \InvalidArgumentException();
}
return $reflection->newInstanceArgs([$standard, $this->logger]);
}
/**
* @return \FeedIo\Reader\FixerSet
*/
public function getFixerSet() : FixerSet
{
return $this->fixerSet;
}
/**
* @return FeedIo
*/
protected function loadFixerSet() : FeedIo
{
$this->fixerSet = new FixerSet();
$fixers = $this->getBaseFixers();
foreach ($fixers as $fixer) {
$this->addFixer($fixer);
}
return $this;
}
/**
* @param FixerAbstract $fixer
* @return FeedIo
*/
public function addFixer(FixerAbstract $fixer) : FeedIo
{
$fixer->setLogger($this->logger);
$this->fixerSet->add($fixer);
return $this;
}
/**
* @return array
*/
public function getBaseFixers() : array
{
return array(
new Reader\Fixer\LastModified(),
new Reader\Fixer\PublicId(),
);
}
/**
* @param array $formats
* @return FeedIo
*/
public function addDateFormats(array $formats) : FeedIo
{
foreach ($formats as $format) {
$this->getDateTimeBuilder()->addDateFormat($format);
}
return $this;
}
/**
* @return \FeedIo\Rule\DateTimeBuilder
*/
public function getDateTimeBuilder() : DateTimeBuilder
{
return $this->dateTimeBuilder;
}
/**
* @return \FeedIo\Reader
*/
public function getReader() : Reader
{
return $this->reader;
}
/**
* @param \FeedIo\Reader
* @return FeedIo
*/
public function setReader(Reader $reader) : FeedIo
{
$this->reader = $reader;
return $this;
}
/**
* @param string $url
* @param FeedInterface $feed
* @param \DateTime $modifiedSince
* @return \FeedIo\Reader\Result
*/
public function read(string $url, FeedInterface $feed = null, \DateTime $modifiedSince = null) : Result
{
if (is_null($feed)) {
$feed = new Feed();
}
if ($modifiedSince instanceof \DateTime) {
$this->addFilter(new ModifiedSince($modifiedSince));
}
$this->logAction($feed, "read access : $url into a feed instance");
$result = $this->reader->read($url, $feed, $modifiedSince);
$this->fixerSet->correct($result->getFeed());
return $result;
}
/**
* @param string $url
* @param \DateTime $modifiedSince
* @return \FeedIo\Reader\Result
*/
public function readSince(string $url, \DateTime $modifiedSince) : Result
{
return $this->read($url, new Feed(), $modifiedSince);
}
/**
* @return FeedIo
*/
public function resetFilters() : FeedIo
{
$this->getReader()->resetFilters();
return $this;
}
/**
* @param FeedInterface $feed
* @param string $standard Standard's name
* @return string
*/
public function format(FeedInterface $feed, string $standard) : string
{
$this->logAction($feed, "formatting a feed in $standard format");
$formatter = $this->getStandard($standard)->getFormatter();
return $formatter->toString($feed);
}
/**
* @param \FeedIo\FeedInterface $feed
* @return string
*/
public function toRss(FeedInterface $feed) : string
{
return $this->format($feed, 'rss');
}
/**
* @param \FeedIo\FeedInterface $feed
* @return string
*/
public function toAtom(FeedInterface $feed) : string
{
return $this->format($feed, 'atom');
}
/**
* @param \FeedIo\FeedInterface $feed
* @return string
*/
public function toJson(FeedInterface $feed) : string
{
return $this->format($feed, 'json');
}
/**
* @param string $name
* @return \FeedIo\StandardAbstract
* @throws \OutOfBoundsException
*/
public function getStandard(string $name) : StandardAbstract
{
$name = strtolower($name);
if (array_key_exists($name, $this->standards)) {
return $this->standards[$name];
}
throw new \OutOfBoundsException("no standard found for $name");
}
/**
* @param \FeedIo\FeedInterface $feed
* @param string $message
* @return FeedIo
*/
protected function logAction(FeedInterface $feed, string $message) : FeedIo
{
$class = get_class($feed);
$this->logger->debug("$message (feed class : $class)");
return $this;
}
}
src/FeedIo/FeedIoException.php 0000666 00000000520 13165210352 0012210 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
class FeedIoException extends \RuntimeException
{
}
src/FeedIo/Filter/ModifiedSince.php 0000666 00000001636 13165210352 0013136 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Filter;
use FeedIo\Feed\ItemInterface;
use FeedIo\FilterInterface;
class ModifiedSince implements FilterInterface
{
/**
* @var \DateTime
*/
protected $date;
/**
* ModifiedSince constructor.
* @param \DateTime $date
*/
public function __construct(\DateTime $date)
{
$this->date = $date;
}
/**
* @param ItemInterface $item
* @return bool
*/
public function isValid(ItemInterface $item) : bool
{
if ($item->getLastModified() instanceof \DateTime) {
return $item->getLastModified() > $this->date;
}
return false;
}
}
src/FeedIo/FilterInterface.php 0000666 00000000733 13165210352 0012252 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\Feed\ItemInterface;
interface FilterInterface
{
/**
* @param ItemInterface $item
* @return bool
*/
public function isValid(ItemInterface $item) : bool;
}
src/FeedIo/Formatter/JsonFormatter.php 0000666 00000006734 13165210352 0013753 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Formatter;
use FeedIo\Feed;
use FeedIo\FeedInterface;
use FeedIo\FormatterInterface;
class JsonFormatter implements FormatterInterface
{
/**
* @param FeedInterface $feed
* @return string
*/
public function toString(FeedInterface $feed) : string
{
return json_encode($this->toArray($feed));
}
/**
* @param FeedInterface $feed
* @return array
*/
public function toArray(FeedInterface $feed) : array
{
return array_filter([
'version' => 'https://jsonfeed.org/version/1',
'title' => $feed->getTitle(),
'description' => $feed->getDescription(),
'home_page_url' => $feed->getLink(),
'feed_url' => $feed->getUrl(),
'id' => $feed->getPublicId(),
'items' => iterator_to_array($this->itemsToArray($feed)),
]);
}
/**
* @param FeedInterface $feed
* @return iterable
*/
public function itemsToArray(FeedInterface $feed) : iterable
{
foreach ($feed as $item) {
yield $this->itemToArray($item);
}
}
/**
* @param Feed\ItemInterface $item
* @return array
*/
public function itemToArray(Feed\ItemInterface $item) : array
{
$array = $this->itemToBaseArray($item);
$this->handleAuthor($item, $array);
$this->handleMedia($item, $array);
$this->handleDate($item, $array);
return array_filter($array);
}
/**
* @param Feed\ItemInterface $item
* @return array
*/
public function itemToBaseArray(Feed\ItemInterface $item) : array
{
$offset = $this->isHtml($item->getDescription()) ? 'content_html':'content_txt';
return [
'id' => $item->getPublicId(),
'title' => $item->getTitle(),
$offset => $item->getDescription(),
'url' => $item->getLink(),
];
}
/**
* @param $string
* @return bool
*/
public function isHtml(string $string) : bool
{
return $string !== strip_tags($string);
}
/**
* @param Feed\ItemInterface $item
* @param array $array
* @return array
*/
public function handleAuthor(Feed\ItemInterface $item, array &$array) : array
{
if (! is_null($item->getAuthor())) {
$array['author'] = array_filter([
'name' => $item->getAuthor()->getName(),
'url' => $item->getAuthor()->getUri(),
]);
}
return $array;
}
/**
* @param Feed\ItemInterface $item
* @param array $array
* @return array
*/
public function handleMedia(Feed\ItemInterface $item, array &$array) : array
{
if ($item->hasMedia()) {
$array['image'] = $item->getMedias()->current()->getUrl();
}
return $array;
}
/**
* @param Feed\ItemInterface $item
* @param array $array
* @return array
*/
public function handleDate(Feed\ItemInterface $item, array &$array) : array
{
if (! is_null($item->getLastModified())) {
$array['date_published'] = $item->getLastModified()->format(\DateTime::RFC3339);
}
return $array;
}
}
src/FeedIo/Formatter/XmlFormatter.php 0000666 00000007567 13165210352 0013607 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Formatter;
use FeedIo\Feed\NodeInterface;
use FeedIo\FeedInterface;
use FeedIo\Rule\OptionalField;
use FeedIo\RuleSet;
use FeedIo\Standard\XmlAbstract;
use FeedIo\FormatterInterface;
/**
* Turns a FeedInterface instance into a XML document.
*
* Depends on :
* - FeedIo\StandardAbstract
*
*/
class XmlFormatter implements FormatterInterface
{
/**
* @var XmlAbstract
*/
protected $standard;
/**
* @param XmlAbstract $standard
*/
public function __construct(XmlAbstract $standard)
{
$this->standard = $standard;
}
/**
* @param \DOMDocument $document
* @param FeedInterface $feed
* @return XmlFormatter
*/
public function setHeaders(\DOMDocument $document, FeedInterface $feed) : XmlFormatter
{
$rules = $this->standard->getFeedRuleSet();
$mainElement = $this->standard->getMainElement($document);
$this->buildElements($rules, $document, $mainElement, $feed);
return $this;
}
/**
* @param \DOMDocument $document
* @param NodeInterface $node
* @return XmlFormatter
*/
public function addItem(\DOMDocument $document, NodeInterface $node) : XmlFormatter
{
$domItem = $document->createElement($this->standard->getItemNodeName());
$rules = $this->standard->getItemRuleSet();
$this->buildElements($rules, $document, $domItem, $node);
$this->standard->getMainElement($document)->appendChild($domItem);
return $this;
}
/**
* @param RuleSet $ruleSet
* @param \DOMDocument $document
* @param \DOMElement $rootElement
* @param NodeInterface $node
*/
public function buildElements(RuleSet $ruleSet, \DOMDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$rules = $this->getAllRules($ruleSet, $node);
foreach ($rules as $rule) {
$rule->apply($document, $rootElement, $node);
}
}
/**
* @param RuleSet $ruleSet
* @param NodeInterface $node
* @return iterable
*/
public function getAllRules(RuleSet $ruleSet, NodeInterface $node) : iterable
{
$rules = $ruleSet->getRules();
$optionalFields = $node->listElements();
foreach ($optionalFields as $optionalField) {
$rules[] = new OptionalField($optionalField);
}
return $rules;
}
/**
* @return \DOMDocument
*/
public function getEmptyDocument() : \DOMDocument
{
return new \DOMDocument('1.0', 'utf-8');
}
/**
* @return \DOMDocument
*/
public function getDocument() : \DOMDocument
{
$document = $this->getEmptyDocument();
return $this->standard->format($document);
}
/**
* @param FeedInterface $feed
* @return string
*/
public function toString(FeedInterface $feed) : string
{
$document = $this->toDom($feed);
return $document->saveXML();
}
/**
* @param FeedInterface $feed
* @return \DomDocument
*/
public function toDom(FeedInterface $feed) : \DOMDocument
{
$document = $this->getDocument();
$this->setHeaders($document, $feed);
$this->setItems($document, $feed);
return $document;
}
/**
* @param \DOMDocument $document
* @param FeedInterface $feed
* @return XmlFormatter
*/
public function setItems(\DOMDocument $document, FeedInterface $feed) : XmlFormatter
{
foreach ($feed as $item) {
$this->addItem($document, $item);
}
return $this;
}
}
src/FeedIo/FormatterInterface.php 0000666 00000000702 13165210352 0012764 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
interface FormatterInterface
{
/**
* @param FeedInterface $feed
* @return string
*/
public function toString(FeedInterface $feed) : string;
}
src/FeedIo/Parser/JsonParser.php 0000666 00000005322 13165210352 0012525 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Parser;
use FeedIo\Feed\Item;
use FeedIo\FeedInterface;
use FeedIo\ParserAbstract;
use FeedIo\Reader\Document;
class JsonParser extends ParserAbstract
{
/**
* @param Document $document
* @param FeedInterface $feed
* @return FeedInterface
*/
public function parseContent(Document $document, FeedInterface $feed) : FeedInterface
{
$data = $document->getJsonAsArray();
$feed->setTitle($this->readOffset($data, 'title'));
$feed->setDescription($this->readOffset($data, 'description'));
$feed->setLink($this->readOffset($data, 'feed_url'));
$feed->setUrl($this->readOffset($data, 'home_page_url'));
if (array_key_exists('items', $data)) {
$this->parseItems($data['items'], $feed);
}
return $feed;
}
/**
* @param Document $document
* @param iterable $mandatoryFields
* @throws MissingFieldsException
* @return bool
*/
public function checkBodyStructure(Document $document, iterable $mandatoryFields) : bool
{
$data = $document->getJsonAsArray();
foreach ($mandatoryFields as $mandatoryField) {
if (! array_key_exists($mandatoryField, $data)) {
throw new MissingFieldsException("Missing {$mandatoryField} in the JSON Feed");
}
}
return true;
}
/**
* @param iterable $items
* @param FeedInterface $feed
* @return JsonParser
*/
public function parseItems(iterable $items, FeedInterface $feed) : JsonParser
{
foreach ($items as $dataItem) {
$item = new Item();
$item->setPublicId($this->readOffset($dataItem, 'id'));
$item->setTitle($this->readOffset($dataItem, 'title'));
$item->setLastModified(new \DateTime($this->readOffset($dataItem, 'date_published')));
$contentHtml = $this->readOffset($dataItem, 'content_html');
$item->setDescription($this->readOffset($dataItem, 'content_text', $contentHtml));
$feed->add($item);
}
return $this;
}
/**
* @param array $data
* @param string $offsetName
* @param string|null $default
* @return null|string
*/
public function readOffset(array $data, string $offsetName, string $default = null) : ? string
{
if (array_key_exists($offsetName, $data)) {
return $data[$offsetName];
}
return $default;
}
}
src/FeedIo/Parser/MissingFieldsException.php 0000666 00000000571 13165210352 0015057 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Parser;
use FeedIo\FeedIoException;
class MissingFieldsException extends FeedIoException
{
}
src/FeedIo/Parser/UnsupportedFormatException.php 0000666 00000000575 13165210352 0016024 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Parser;
use FeedIo\FeedIoException;
class UnsupportedFormatException extends FeedIoException
{
}
src/FeedIo/Parser/XmlParser.php 0000666 00000006416 13165210352 0012361 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Parser;
use FeedIo\Parser;
use FeedIo\RuleSet;
use FeedIo\FeedInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\ParserAbstract;
use FeedIo\Reader\Document;
use FeedIo\Parser\MissingFieldsException;
use FeedIo\Parser\UnsupportedFormatException;
/**
* Parses a DOM document if its format matches the parser's standard
*
* Depends on :
* - FeedIo\StandardAbstract
* - Psr\Log\LoggerInterface
*
*/
class XmlParser extends ParserAbstract
{
/**
* @param $tagName
* @return bool
*/
public function isItem(string $tagName) : bool
{
return (strtolower($this->standard->getItemNodeName()) === strtolower($tagName));
}
/**
* @param Document $document
* @param FeedInterface $feed
* @return \FeedIo\FeedInterface
* @throws Parser\MissingFieldsException
* @throws Parser\UnsupportedFormatException
*/
public function parseContent(Document $document, FeedInterface $feed) : FeedInterface
{
$element = $this->standard->getMainElement($document->getDOMDocument());
$this->parseNode($feed, $element, $this->standard->getFeedRuleSet());
return $feed;
}
/**
* @param Document $document
* @param iterable $mandatoryFields
* @throws MissingFieldsException
* @return bool
*/
public function checkBodyStructure(Document $document, iterable $mandatoryFields) : bool
{
$errors = array();
$element = $document->getDOMDocument()->documentElement;
foreach ($mandatoryFields as $field) {
$list = $element->getElementsByTagName($field);
if (0 === $list->length) {
$errors[] = $field;
}
}
if (!empty($errors)) {
$message = "missing mandatory field(s) : ".implode(',', $errors);
$this->logger->warning($message);
throw new MissingFieldsException($message);
}
return true;
}
/**
* @param NodeInterface $item
* @param \DOMElement $element
* @param RuleSet $ruleSet
* @return NodeInterface
*/
public function parseNode(NodeInterface $item, \DOMElement $element, RuleSet $ruleSet) : NodeInterface
{
foreach ($element->childNodes as $node) {
if ($node instanceof \DOMElement) {
$this->handleNode($item, $node, $ruleSet);
}
}
return $item;
}
/**
* @param NodeInterface $item
* @param \DOMElement $node
* @param RuleSet $ruleSet
*/
protected function handleNode(NodeInterface $item, \DOMElement $node, RuleSet $ruleSet) : void
{
if ($this->isItem($node->tagName) && $item instanceof FeedInterface) {
$newItem = $this->parseNode($item->newItem(), $node, $this->standard->getItemRuleSet());
$this->addValidItem($item, $newItem);
} else {
$rule = $ruleSet->get($node->tagName);
$rule->setProperty($item, $node);
}
}
}
src/FeedIo/ParserAbstract.php 0000666 00000006735 13165210352 0012134 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\Parser\UnsupportedFormatException;
use FeedIo\Reader\Document;
use FeedIo\Feed\ItemInterface;
use FeedIo\Feed\NodeInterface;
use Psr\Log\LoggerInterface;
/**
* Parses a document if its format matches the parser's standard
*
* Depends on :
* - FeedIo\StandardAbstract
* - Psr\Log\LoggerInterface
*
*/
abstract class ParserAbstract
{
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var array[FilterInterface]
*/
protected $filters = array();
/**
* @var StandardAbstract
*/
protected $standard;
/**
* @param StandardAbstract $standard
* @param LoggerInterface $logger
*/
public function __construct(StandardAbstract $standard, LoggerInterface $logger)
{
$this->standard = $standard;
$this->logger = $logger;
}
/**
* Tries to parse the document
*
* @param Document $document
* @param FeedInterface $feed
* @return \FeedIo\FeedInterface
* @throws \FeedIo\Parser\UnsupportedFormatException
*/
public function parse(Document $document, FeedInterface $feed) : FeedInterface
{
if (!$this->standard->canHandle($document)) {
throw new UnsupportedFormatException('this is not a supported format');
}
$this->checkBodyStructure($document, $this->standard->getMandatoryFields());
$this->parseContent($document, $feed);
return $feed;
}
/**
* This method is called by parse() if and only if the checkBodyStructure was successful
*
* @param Document $document
* @param FeedInterface $feed
* @return \FeedIo\FeedInterface
*/
abstract public function parseContent(Document $document, FeedInterface $feed) : FeedInterface;
/**
* @param Document $document
* @param iterable $mandatoryFields
* @throws MissingFieldsException
* @return bool
*/
abstract public function checkBodyStructure(Document $document, iterable $mandatoryFields) : bool;
/**
* @return StandardAbstract
*/
public function getStandard() : StandardAbstract
{
return $this->standard;
}
/**
* @param FeedInterface $feed
* @param NodeInterface $item
* @return ParserAbstract
*/
public function addValidItem(FeedInterface $feed, NodeInterface $item) : ParserAbstract
{
if ($item instanceof ItemInterface && $this->isValid($item)) {
$feed->add($item);
}
return $this;
}
/**
* @param ItemInterface $item
* @return bool
*/
public function isValid(ItemInterface $item) : bool
{
foreach ($this->filters as $filter) {
if (!$filter->isValid($item)) {
return false;
}
}
return true;
}
/**
* @param FilterInterface $filter
* @return ParserAbstract
*/
public function addFilter(FilterInterface $filter) : ParserAbstract
{
$this->filters[] = $filter;
return $this;
}
/**
* Reset filters
* @return ParserAbstract
*/
public function resetFilters() : ParserAbstract
{
$this->filters = [];
return $this;
}
}
src/FeedIo/Reader.php 0000666 00000011551 13165210352 0010406 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\ParserAbstract;
use FeedIo\Adapter\ClientInterface;
use FeedIo\Adapter\ResponseInterface;
use FeedIo\Reader\Document;
use FeedIo\Reader\ReadErrorException;
use FeedIo\Reader\Result;
use FeedIo\Reader\NoAccurateParserException;
use Psr\Log\LoggerInterface;
/**
* Consumes feeds and return corresponding Result instances
*
* Depends on :
* - FeedIo\Adapter\ClientInterface
* - Psr\Log\LoggerInterface
*
* A Reader instance MUST have at least one parser added with the addParser() method to read feeds
* It will throw a NoAccurateParserException if it cannot find a suitable parser for the feed.
*/
class Reader
{
/**
* @var \FeedIo\Adapter\ClientInterface;
*/
protected $client;
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var array
*/
protected $parsers = array();
/**
* @param ClientInterface $client
* @param LoggerInterface $logger
*/
public function __construct(ClientInterface $client, LoggerInterface $logger)
{
$this->client = $client;
$this->logger = $logger;
}
/**
* @param ParserAbstract $parser
* @return Reader
*/
public function addParser(ParserAbstract $parser) : Reader
{
$this->logger->debug("new parser added : ".get_class($parser->getStandard()));
$this->parsers[] = $parser;
return $this;
}
/**
* adds a filter to every parsers
*
* @param \FeedIo\FilterInterface $filter
* @return Reader
*/
public function addFilter(FilterInterface $filter) : Reader
{
foreach ($this->parsers as $parser) {
$parser->addFilter($filter);
}
return $this;
}
/**
* Reset filters on every parsers
* @return Reader
*/
public function resetFilters() : Reader
{
foreach ($this->parsers as $parser) {
$parser->resetFilters();
}
return $this;
}
/**
* @param string $url
* @param FeedInterface $feed
* @param \DateTime $modifiedSince
* @return \FeedIo\Reader\Result
* @throws ReadErrorException
*/
public function read(string $url, FeedInterface $feed, \DateTime $modifiedSince = null) : Result
{
$this->logger->debug("start reading {$url}");
if (is_null($modifiedSince)) {
$this->logger->notice("no 'modifiedSince' parameter given, setting it to 01/01/1970");
$modifiedSince = new \DateTime('@0');
}
try {
$this->logger->info("hitting {$url}");
$response = $this->client->getResponse($url, $modifiedSince);
$document = $this->handleResponse($response, $feed);
return new Result($document, $feed, $modifiedSince, $response, $url);
} catch (\Exception $e) {
$this->logger->warning("{$url} read error : {$e->getMessage()}");
throw new ReadErrorException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* @param ResponseInterface $response
* @param FeedInterface $feed
* @return Document
*/
public function handleResponse(ResponseInterface $response, FeedInterface $feed) : Document
{
$this->logger->debug("response ok, now turning it into a document");
$document = new Document($response->getBody());
if ($response->isModified()) {
$this->logger->info("the stream is modified, parsing it");
$this->parseDocument($document, $feed);
}
return $document;
}
/**
* @param Document $document
* @param FeedInterface $feed
* @return FeedInterface
* @throws Parser\UnsupportedFormatException
* @throws Reader\NoAccurateParserException
*/
public function parseDocument(Document $document, FeedInterface $feed) : FeedInterface
{
$parser = $this->getAccurateParser($document);
$this->logger->debug("accurate parser : ".get_class($parser));
return $parser->parse($document, $feed);
}
/**
* @param Document $document
* @return ParserAbstract
* @throws Reader\NoAccurateParserException
*/
public function getAccurateParser(Document $document) : ParserAbstract
{
foreach ($this->parsers as $parser) {
if ($parser->getStandard()->canHandle($document)) {
return $parser;
}
}
$message = 'No parser can handle this stream';
$this->logger->error($message);
throw new NoAccurateParserException($message);
}
}
src/FeedIo/Reader/Document.php 0000666 00000004735 13165210352 0012172 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
class Document
{
/**
* @var string
*/
protected $content;
/**
* @var \DOMDocument
*/
protected $domDocument;
/**
* @var array
*/
protected $jsonArray;
/**
* Document constructor.
* @param string $content
*/
public function __construct(string $content)
{
$this->content = trim($content);
}
/**
* @param $character
* @return bool
*/
public function startWith(string $character) : bool
{
return substr($this->content, 0, 1) === $character;
}
/**
* @return bool
*/
public function isJson() : bool
{
return $this->startWith('{');
}
/**
* @return bool
*/
public function isXml() : bool
{
return $this->startWith('<');
}
/**
* @return \DOMDocument
*/
public function getDOMDocument() : \DOMDocument
{
if (is_null($this->domDocument)) {
$this->domDocument = $this->loadDomDocument();
}
return $this->domDocument;
}
/**
* @return array
*/
public function getJsonAsArray() : array
{
if (is_null($this->jsonArray)) {
$this->jsonArray = $this->loadJsonAsArray();
}
return $this->jsonArray;
}
/**
* @return \DOMDocument
*/
protected function loadDomDocument() : \DOMDocument
{
if (! $this->isXml()) {
throw new \LogicException('this document is not a XML stream');
}
set_error_handler(
/**
* @param string $errno
*/
function ($errno, $errstr) {
throw new \InvalidArgumentException("malformed xml string. parsing error : $errstr ($errno)");
}
);
$domDocument = new \DOMDocument();
$domDocument->loadXML($this->content);
restore_error_handler();
return $domDocument;
}
/**
* @return array
*/
protected function loadJsonAsArray() : array
{
if (! $this->isJson()) {
throw new \LogicException('this document is not a JSON stream');
}
return json_decode($this->content, true);
}
}
src/FeedIo/Reader/Fixer/LastModified.php 0000666 00000002323 13165210352 0014024 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader\Fixer;
use FeedIo\FeedInterface;
use FeedIo\Reader\FixerAbstract;
class LastModified extends FixerAbstract
{
/**
* @param FeedInterface $feed
* @return FixerAbstract
*/
public function correct(FeedInterface $feed) : FixerAbstract
{
if (is_null($feed->getLastModified())) {
$this->logger->notice("correct last modified date for feed {$feed->getTitle()}");
$feed->setLastModified(
$this->searchLastModified($feed)
);
}
return $this;
}
/**
* @param FeedInterface $feed
* @return \DateTime
*/
public function searchLastModified(FeedInterface $feed) : \DateTime
{
$latest = new \DateTime('@0');
foreach ($feed as $item) {
if ($item->getLastModified() > $latest) {
$latest = $item->getLastModified();
}
}
return $latest;
}
}
src/FeedIo/Reader/Fixer/PublicId.php 0000666 00000002237 13165210352 0013157 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader\Fixer;
use FeedIo\FeedInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\Reader\FixerAbstract;
class PublicId extends FixerAbstract
{
/**
* @param FeedInterface $feed
* @return $this
*/
public function correct(FeedInterface $feed) : FixerAbstract
{
$this->fixNode($feed);
$this->fixItems($feed);
return $this;
}
/**
* @param NodeInterface $node
*/
protected function fixNode(NodeInterface $node) : void
{
if (is_null($node->getPublicId())) {
$this->logger->notice("correct public id for node {$node->getTitle()}");
$node->setPublicId($node->getLink());
}
}
/**
* @param FeedInterface $feed
*/
protected function fixItems(FeedInterface $feed) : void
{
foreach ($feed as $item) {
$this->fixNode($item);
}
}
}
src/FeedIo/Reader/FixerAbstract.php 0000666 00000001474 13165210352 0013152 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
use FeedIo\FeedInterface;
use Psr\Log\LoggerInterface;
abstract class FixerAbstract
{
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @param \Psr\Log\LoggerInterface
* @return $this
*/
public function setLogger(LoggerInterface $logger) : FixerAbstract
{
$this->logger = $logger;
return $this;
}
/**
* @param FeedInterface $feed
* @return FixerAbstract
*/
abstract public function correct(FeedInterface $feed) : FixerAbstract;
}
src/FeedIo/Reader/FixerSet.php 0000666 00000001511 13165210352 0012132 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
use FeedIo\FeedInterface;
class FixerSet
{
protected $fixers = array();
/**
* @param \FeedIo\Reader\FixerAbstract
* @return FixerSet
*/
public function add(FixerAbstract $fixer) : FixerSet
{
$this->fixers[] = $fixer;
return $this;
}
/**
* @param FeedInterface $feed
* @return FixerSet
*/
public function correct(FeedInterface $feed) : FixerSet
{
foreach ($this->fixers as $fixer) {
$fixer->correct($feed);
}
return $this;
}
}
src/FeedIo/Reader/NoAccurateParserException.php 0000666 00000000574 13165210352 0015471 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
use FeedIo\FeedIoException;
class NoAccurateParserException extends FeedIoException
{
}
src/FeedIo/Reader/ReadErrorException.php 0000666 00000000565 13165210352 0014155 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
use FeedIo\FeedIoException;
class ReadErrorException extends FeedIoException
{
}
src/FeedIo/Reader/Result.php 0000666 00000004650 13165210352 0011666 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
use FeedIo\Adapter\ResponseInterface;
use FeedIo\FeedInterface;
/**
* Result of the read() operation
*
* a Result instance holds the following :
*
* - the Feed instance
* - Date and time of the request
* - value of the 'modifiedSince' header sent throught the request
* - the raw response
* - the DOM document
* - URL of the feed
*/
class Result
{
/**
* @var \DateTime
*/
protected $modifiedSince;
/**
* @var \DateTime
*/
protected $date;
/**
* @var \FeedIo\FeedInterface
*/
protected $feed;
/**
* @var \FeedIo\Adapter\ResponseInterface
*/
protected $response;
/**
* @var Document
*/
protected $document;
/**
* @var string
*/
protected $url;
/**
* @param Document $document
* @param FeedInterface $feed
* @param \DateTime $modifiedSince
* @param ResponseInterface $response
* @param $url
*/
public function __construct(
Document $document,
FeedInterface $feed,
\DateTime $modifiedSince,
ResponseInterface $response,
string $url
) {
$this->date = new \DateTime();
$this->document = $document;
$this->feed = $feed;
$this->modifiedSince = $modifiedSince;
$this->response = $response;
$this->url = $url;
}
/**
* @return \DateTime
*/
public function getDate() : \DateTime
{
return $this->date;
}
/**
* @return Document
*/
public function getDocument() : Document
{
return $this->document;
}
/**
* @return FeedInterface
*/
public function getFeed() : FeedInterface
{
return $this->feed;
}
/**
* @return \DateTime|null
*/
public function getModifiedSince() : ? \DateTime
{
return $this->modifiedSince;
}
/**
* @return ResponseInterface
*/
public function getResponse() : ResponseInterface
{
return $this->response;
}
/**
* @return string
*/
public function getUrl() : string
{
return $this->url;
}
}
src/FeedIo/Rule/Atom/Author.php 0000666 00000003045 13165210352 0012254 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule\Atom;
use FeedIo\Feed\ItemInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\Rule\Author as BaseAuthor;
class Author extends BaseAuthor
{
const NODE_NAME = 'author';
/**
* @param NodeInterface $node
* @param \DOMElement $element
* @return mixed
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
if ($node instanceof ItemInterface) {
$author = $node->newAuthor();
$author->setName($this->getChildValue($element, 'name'));
$author->setUri($this->getChildValue($element, 'uri'));
$author->setEmail($this->getChildValue($element, 'email'));
$node->setAuthor($author);
}
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$element = $document->createElement(static::NODE_NAME);
$element->appendChild($document->createElement('name', $node->getAuthor()->getName()));
$element->appendChild($document->createElement('uri', $node->getAuthor()->getUri()));
$element->appendChild($document->createElement('email', $node->getAuthor()->getEmail()));
$rootElement->appendChild($element);
}
}
src/FeedIo/Rule/Atom/Category.php 0000666 00000002632 13165210352 0012570 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule\Atom;
use FeedIo\Feed\Node\CategoryInterface;
use FeedIo\Feed\NodeInterface;
class Category extends \FeedIo\Rule\Category
{
/**
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
$category = $node->newCategory();
$category->setScheme($this->getAttributeValue($element, 'scheme'))
->setLabel($this->getAttributeValue($element, 'label'))
->setTerm($this->getAttributeValue($element, 'term'));
$node->addCategory($category);
}
/**
* @param \DomDocument $document
* @param CategoryInterface $category
* @return \DomElement
*/
public function createCategoryElement(\DomDocument $document, CategoryInterface $category) : \DOMElement
{
$element = $document->createElement($this->getNodeName());
$element->setAttribute('scheme', $category->getScheme() ?? '');
$element->setAttribute('term', $category->getTerm() ?? '');
$element->setAttribute('label', $category->getLabel() ?? '');
return $element;
}
}
src/FeedIo/Rule/Atom/Link.php 0000666 00000002032 13165210352 0011702 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule\Atom;
use FeedIo\Feed\NodeInterface;
use FeedIo\Rule\Link as BaseLink;
class Link extends BaseLink
{
const NODE_NAME = 'link';
/**
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
if ($element->hasAttribute('href')) {
$node->setLink($element->getAttribute('href'));
}
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$element = $document->createElement(static::NODE_NAME);
$element->setAttribute('href', $node->getLink());
$rootElement->appendChild($element);
}
}
src/FeedIo/Rule/Atom/LinkNode.php 0000666 00000003522 13165210352 0012515 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule\Atom;
use FeedIo\Feed\ItemInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
use FeedIo\RuleSet;
class LinkNode extends RuleAbstract
{
const NODE_NAME = 'link';
/**
* @var \FeedIo\RuleSet
*/
protected $ruleSet;
/**
* @param string $nodeName
*/
public function __construct(string $nodeName = null)
{
parent::__construct($nodeName);
$mediaRule = new Media();
$mediaRule->setUrlAttributeName('href');
$this->ruleSet = new RuleSet(new Link('related'));
$this->ruleSet->add($mediaRule, ['media', 'enclosure']);
}
/**
* @param NodeInterface $node
* @param \DOMElement $element
* @return mixed
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
if ($element->hasAttribute('rel')) {
$this->ruleSet->get($element->getAttribute('rel'))->setProperty($node, $element);
} else {
$this->ruleSet->getDefault()->setProperty($node, $element);
}
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return true;
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
if ($node instanceof ItemInterface && $node->hasMedia()) {
$this->ruleSet->get('media')->apply($document, $rootElement, $node);
}
$this->ruleSet->getDefault()->apply($document, $rootElement, $node);
}
}
src/FeedIo/Rule/Atom/Media.php 0000666 00000001324 13165210352 0012027 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule\Atom;
use FeedIo\Feed\Item\MediaInterface;
use FeedIo\Rule\Media as BaseMedia;
class Media extends BaseMedia
{
const NODE_NAME = 'link';
/**
* @inheritDoc
*/
public function createMediaElement(\DomDocument $document, MediaInterface $media) : \DOMElement
{
$element = parent::createMediaElement($document, $media);
$element->setAttribute('rel', 'enclosure');
return $element;
}
}
src/FeedIo/Rule/Author.php 0000666 00000003123 13165210352 0011351 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\ItemInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
class Author extends RuleAbstract
{
const NODE_NAME = 'author';
/**
* Sets the accurate $item property according to the DomElement content
*
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
if ($node instanceof ItemInterface) {
$author = $node->newAuthor();
$author->setName($element->nodeValue);
$node->setAuthor($author);
}
}
/**
* Tells if the node contains the expected value
*
* @param NodeInterface $node
* @return bool
*/
protected function hasValue(NodeInterface $node) : bool
{
return $node instanceof ItemInterface && !! $node->getAuthor();
}
/**
* Creates and adds the element(s) to the docuement's rootElement
*
* @param \DomDocument $document
* @param \DOMElement $rootElement
* @param NodeInterface $node
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$rootElement->appendChild($document->createElement($this->getNodeName(), $node->getAuthor()->getName()));
}
}
src/FeedIo/Rule/Category.php 0000666 00000003630 13165210352 0011667 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\Node\CategoryInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
class Category extends RuleAbstract
{
const NODE_NAME = 'category';
/**
* @param NodeInterface $node
* @param \DOMElement $element
* @return mixed
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
$category = $node->newCategory();
$category->setScheme($this->getAttributeValue($element, 'domain'))
->setLabel($element->nodeValue)
->setTerm($element->nodeValue);
$node->addCategory($category);
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getCategories();
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
foreach ($node->getCategories() as $category) {
$rootElement->appendChild($this->createCategoryElement($document, $category));
}
}
/**
* @param \DomDocument $document
* @param CategoryInterface $category
* @return \DomElement
*/
public function createCategoryElement(\DomDocument $document, CategoryInterface $category) : \DOMElement
{
$element = $document->createElement(
$this->getNodeName(),
is_null($category->getTerm()) ? $category->getLabel():$category->getTerm()
);
if (!! $category->getScheme()) {
$element->setAttribute('domain', $category->getScheme());
}
return $element;
}
}
src/FeedIo/Rule/DateTimeBuilder.php 0000666 00000012601 13165210352 0013113 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
class DateTimeBuilder
{
/**
* Supported date formats
* @var array
*/
protected $dateFormats = [
\DateTime::RFC2822,
\DateTime::ATOM,
\DateTime::RFC3339,
\DateTime::RSS,
\DateTime::W3C,
'Y-m-d\TH:i:s.uP',
'Y-m-d\TH:i:s',
'Y-m-d',
'd/m/Y',
'D, d M Y H:i O',
'D, d M Y H:i:s O',
'D M d Y H:i:s e',
'*, m#d#Y - H:i',
];
/**
* @var \DateTimeZone
*/
protected $feedTimezone;
/**
* @var \DateTimeZone
*/
protected $serverTimezone;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var string
*/
protected $lastGuessedFormat = \DateTime::RFC2822;
/**
* @param \Psr\Log\LoggerInterface $logger
*/
public function __construct(LoggerInterface $logger = null)
{
if (is_null($logger)) {
$logger = new NullLogger;
}
$this->logger = $logger;
$this->setTimezone(new \DateTimeZone(date_default_timezone_get()));
}
/**
* @param $dateFormat
* @return DateTimeBuilder
*/
public function addDateFormat(string $dateFormat) : DateTimeBuilder
{
$this->dateFormats[] = $dateFormat;
return $this;
}
/**
* @param array $dateFormats
* @return $this
*/
public function setDateFormats(array $dateFormats) : DateTimeBuilder
{
$this->dateFormats = $dateFormats;
return $this;
}
/**
* @return string
*/
public function getLastGuessedFormat() : string
{
return $this->lastGuessedFormat;
}
/**
* Tries to guess the date's format from the list
* @param string $date
* @return string|null date Format
*/
public function guessDateFormat(string $date) : ? string
{
foreach ($this->dateFormats as $format) {
$test = \DateTime::createFromFormat($format, $date);
if ($test instanceof \DateTime) {
$this->lastGuessedFormat = $format;
return $format;
}
}
return null;
}
/**
* Creates a DateTime instance for the given string. Default format is RFC2822
* @param string $string
* @return \DateTime
*/
public function convertToDateTime(string $string) : \DateTime
{
$string = trim($string);
foreach ([$this->getLastGuessedFormat(), $this->guessDateFormat($string) ] as $format) {
$date = $this->newDate((string) $format, $string);
if ($date instanceof \DateTime) {
$date->setTimezone($this->getTimezone());
return $date;
}
}
return $this->stringToDateTime($string);
}
/**
* Creates a DateTime instance for the given string if the format was not catch from the list
* @param string $string
* @return \DateTime
* @throws InvalidArgumentException
*/
public function stringToDateTime(string $string) : \DateTime
{
$this->logger->notice("unsupported date format, use strtotime() to build the DateTime instance : {$string}");
if (false === strtotime($string)) {
throw new \InvalidArgumentException('Impossible to convert date : '.$string);
}
$date = new \DateTime($string, $this->getFeedTimezone());
$date->setTimezone($this->getTimezone());
return $date;
}
/**
* @return \DateTimeZone
*/
public function getFeedTimezone() : ? \DateTimeZone
{
return $this->feedTimezone;
}
/**
* Specifies the feed's timezone. Do this it the timezone is missing
*
* @param \DateTimeZone $timezone
*/
public function setFeedTimezone(\DateTimeZone $timezone) : void
{
$this->feedTimezone = $timezone;
}
/**
* Resets feedTimezone to null.
*/
public function resetFeedTimezone() : void
{
$this->feedTimezone = null;
}
/**
* @return \DateTimeZone
*/
public function getServerTimezone() : ? \DateTimeZone
{
return $this->serverTimezone;
}
/**
* @param \DateTimeZone $timezone
*/
public function setServerTimezone(\DateTimeZone $timezone) : void
{
$this->serverTimezone = $timezone;
}
/**
* @return \DateTimeZone
*/
public function getTimezone() : ? \DateTimeZone
{
return $this->getServerTimezone();
}
/**
* @param \DateTimeZone $timezone
*/
public function setTimezone(\DateTimeZone $timezone) : void
{
$this->setServerTimezone($timezone);
}
/**
* @param $format
* @param $string
* @return \DateTime
*/
protected function newDate(string $format, string $string)
{
if (!! $this->getFeedTimezone()) {
return \DateTime::createFromFormat($format, $string, $this->getFeedTimezone());
}
return \DateTime::createFromFormat($format, $string);
}
}
src/FeedIo/Rule/Description.php 0000666 00000002500 13165210352 0012370 0 ustar 00 firstChild && $element->firstChild->nodeType == XML_CDATA_SECTION_NODE) {
$string = $element->firstChild->textContent;
} else {
foreach ($element->childNodes as $childNode) {
$string .= $element->ownerDocument->saveXML($childNode);
}
}
$node->setDescription(htmlspecialchars_decode($string));
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getDescription();
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$description = htmlspecialchars($node->getDescription());
$rootElement->appendChild($document->createElement($this->getNodeName(), $description));
}
}
src/FeedIo/Rule/Link.php 0000666 00000002026 13165210352 0011005 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
class Link extends RuleAbstract
{
const NODE_NAME = 'link';
/**
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
$node->setLink($element->nodeValue);
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getLink();
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$rootElement->appendChild($document->createElement($this->getNodeName(), $node->getLink()));
}
}
src/FeedIo/Rule/Media.php 0000666 00000004577 13165210352 0011144 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\Item;
use FeedIo\Feed\Item\MediaInterface;
use FeedIo\Feed\ItemInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
class Media extends RuleAbstract
{
const NODE_NAME = 'enclosure';
protected $urlAttributeName = 'url';
/**
* @return string
*/
public function getUrlAttributeName() : string
{
return $this->urlAttributeName;
}
/**
* @param string $name
*/
public function setUrlAttributeName(string $name) : void
{
$this->urlAttributeName = $name;
}
/**
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
if ($node instanceof ItemInterface) {
$media = $node->newMedia();
$media->setType($this->getAttributeValue($element, 'type'))
->setUrl($this->getAttributeValue($element, $this->getUrlAttributeName()))
->setLength($this->getAttributeValue($element, 'length'));
$node->addMedia($media);
}
}
/**
* @param \DomDocument $document
* @param MediaInterface $media
* @return \DomElement
*/
public function createMediaElement(\DomDocument $document, MediaInterface $media) : \DOMElement
{
$element = $document->createElement($this->getNodeName());
$element->setAttribute($this->getUrlAttributeName(), $media->getUrl());
$element->setAttribute('type', $media->getType() ?? '');
$element->setAttribute('length', $media->getLength() ?? '');
return $element;
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return $node instanceof ItemInterface && !! $node->getMedias();
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
foreach ($node->getMedias() as $media) {
$rootElement->appendChild($this->createMediaElement($document, $media));
}
}
}
src/FeedIo/Rule/ModifiedSince.php 0000666 00000002354 13165210352 0012616 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\NodeInterface;
use FeedIo\DateRuleAbstract;
class ModifiedSince extends DateRuleAbstract
{
const NODE_NAME = 'pubDate';
/**
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
$node->setLastModified($this->getDateTimeBuilder()->convertToDateTime($element->nodeValue));
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return true;
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$date = is_null($node->getLastModified()) ? new \DateTime():$node->getLastModified();
$rootElement->appendChild($document->createElement(
$this->getNodeName(),
$date->format($this->getDefaultFormat())
));
}
}
src/FeedIo/Rule/OptionalField.php 0000666 00000010076 13165210352 0012645 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\ElementsAwareInterface;
use FeedIo\Feed\NodeInterface;
use FeedIo\Feed\Node\ElementInterface;
use FeedIo\RuleAbstract;
class OptionalField extends RuleAbstract
{
const NODE_NAME = 'default';
/**
* @param NodeInterface $node
* @param \DOMElement $domElement
*/
public function setProperty(NodeInterface $node, \DOMElement $domElement) : void
{
$element = $this->createElementFromDomNode($node, $domElement);
$node->addElement($element);
}
/**
* @param NodeInterface $node
* @param ElementInterface $element
* @param \DOMNode $domNode
*/
private function addSubElements(NodeInterface $node, ElementInterface $element, \DOMNode $domNode) : void
{
if (!$domNode->hasChildNodes() || !$this->hasSubElements($domNode)) {
// no elements to add
return;
}
$this->addElementsFromNodeList($node, $element, $domNode->childNodes);
}
/**
* @param NodeInterface $node
* @param ElementInterface $element
* @param \DOMNodeList $childNodeList
*/
private function addElementsFromNodeList(NodeInterface $node, ElementInterface $element, \DOMNodeList $childNodeList) : void
{
foreach ($childNodeList as $childNode) {
if ($childNode instanceof \DOMText) {
continue;
}
$element->addElement($this->createElementFromDomNode($node, $childNode));
}
}
/**
* @param \DOMNode $domNode
* @return bool
*/
private function hasSubElements(\DOMNode $domNode) : bool
{
foreach ($domNode->childNodes as $childDomNode) {
if (!$childDomNode instanceof \DOMText) {
return true;
}
}
return false;
}
/**
* @param NodeInterface $node
* @param \DOMNode $domNode
* @return ElementInterface
*/
private function createElementFromDomNode(NodeInterface $node, \DOMNode $domNode) : ElementInterface
{
$element = $node->newElement();
$element->setName($domNode->nodeName);
$element->setValue($domNode->nodeValue);
foreach ($domNode->attributes as $attribute) {
$element->setAttribute($attribute->name, $attribute->value);
}
$this->addSubElements($node, $element, $domNode);
return $element;
}
/**
* @param \DomElement $domElement
* @param ElementInterface $element
* @return \DomElement
*/
public function buildDomElement(\DomElement $domElement, ElementInterface $element) : \DOMElement
{
$domElement->nodeValue = $element->getValue();
foreach ($element->getAttributes() as $name => $value) {
$domElement->setAttribute($name, $value);
}
/** @var ElementInterface $subElement */
foreach ($element->getAllElements() as $subElement) {
$subDomElement = $domElement->ownerDocument->createElement($subElement->getName());
$this->buildDomElement($subDomElement, $subElement);
$domElement->appendChild($subDomElement);
}
return $domElement;
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return $node instanceof ElementsAwareInterface;
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$domElement = $document->createElement($this->getNodeName());
if ($node instanceof ElementsAwareInterface) {
foreach ($node->getElementIterator($this->getNodeName()) as $element) {
$this->buildDomElement($domElement, $element);
}
}
$rootElement->appendChild($domElement);
}
}
src/FeedIo/Rule/PublicId.php 0000666 00000001613 13165210352 0011604 0 ustar 00 setPublicId($element->nodeValue);
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getPublicId();
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$rootElement->appendChild($document->createElement($this->getNodeName(), $node->getPublicId()));
}
}
src/FeedIo/Rule/Structure.php 0000666 00000003421 13165210352 0012110 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
use FeedIo\RuleSet;
class Structure extends RuleAbstract
{
const NODE_NAME = 'structure';
/**
* @var \FeedIo\RuleSet
*/
protected $ruleSet;
/**
* @param string $nodeName
* @param RuleSet $ruleSet
*/
public function __construct(string $nodeName = null, RuleSet $ruleSet = null)
{
parent::__construct($nodeName);
$this->ruleSet = is_null($ruleSet) ? new RuleSet() : $ruleSet;
}
/**
* @param NodeInterface $node
* @param \DOMElement $element
* @return mixed
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
foreach ($element->childNodes as $domNode) {
if ($domNode instanceof \DomElement) {
$rule = $this->ruleSet->get($domNode->tagName);
$rule->setProperty($node, $domNode);
}
}
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return true;
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$element = $document->createElement($this->getNodeName());
/** @var RuleAbstract $rule */
foreach ($this->ruleSet->getRules() as $rule) {
$rule->apply($document, $element, $node);
}
$rootElement->appendChild($element);
}
}
src/FeedIo/Rule/Title.php 0000666 00000002672 13165210352 0011200 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Rule;
use FeedIo\Feed\NodeInterface;
use FeedIo\RuleAbstract;
class Title extends RuleAbstract
{
const NODE_NAME = 'title';
/**
* Sets the accurate $item property according to the DomElement content
*
* @param NodeInterface $node
* @param \DOMElement $element
*/
public function setProperty(NodeInterface $node, \DOMElement $element) : void
{
$node->setTitle($element->nodeValue);
}
/**
* Tells if the node contains the expected value
*
* @param NodeInterface $node
* @return bool
*/
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getTitle();
}
/**
* Creates and adds the element(s) to the docuement's rootElement
*
* @param \DomDocument $document
* @param \DOMElement $rootElement
* @param NodeInterface $node
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$title = htmlspecialchars($node->getTitle());
$element = $document->createElement(static::NODE_NAME, $title);
$rootElement->appendChild($element);
}
}
src/FeedIo/RuleAbstract.php 0000666 00000005203 13165210352 0011574 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\Feed\NodeInterface;
abstract class RuleAbstract
{
const NODE_NAME = 'node';
/**
* @var string
*/
protected $nodeName;
/**
* @param string $nodeName
*/
public function __construct(string $nodeName = null)
{
$this->nodeName = is_null($nodeName) ? static::NODE_NAME : $nodeName;
}
/**
* @return string
*/
public function getNodeName() : string
{
return $this->nodeName;
}
/**
* @param \DOMElement $element
* @param string $name
* @return string|null
*/
public function getAttributeValue(\DOMElement $element, $name) : ? string
{
if ($element->hasAttribute($name)) {
return $element->getAttribute($name);
}
return null;
}
/**
* @param \DOMElement $element
* @param string $name
* @return string|null
*/
public function getChildValue(\DOMElement $element, string $name) : ? string
{
$list = $element->getElementsByTagName($name);
if ($list->length > 0) {
return $list->item(0)->nodeValue;
}
return null;
}
/**
* adds the accurate DomElement content according to the node's property
*
* @param \DomDocument $document
* @param \DOMElement $rootElement
* @param NodeInterface $node
*/
public function apply(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
if ($this->hasValue($node)) {
$this->addElement($document, $rootElement, $node);
}
}
/**
* Sets the accurate $item property according to the DomElement content
*
* @param NodeInterface $node
* @param \DOMElement $element
*/
abstract public function setProperty(NodeInterface $node, \DOMElement $element) : void;
/**
* Tells if the node contains the expected value
*
* @param NodeInterface $node
* @return bool
*/
abstract protected function hasValue(NodeInterface $node) : bool;
/**
* Creates and adds the element(s) to the document's rootElement
*
* @param \DomDocument $document
* @param \DOMElement $rootElement
* @param NodeInterface $node
*/
abstract protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void;
}
src/FeedIo/RuleSet.php 0000666 00000004526 13165210352 0010573 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\Rule\OptionalField;
class RuleSet
{
/**
* @var \ArrayIterator
*/
protected $rules;
/**
* @var array
*/
protected $aliases = array();
/**
* @var RuleAbstract
*/
protected $default;
/**
* @param RuleAbstract $default default rule
*/
public function __construct(RuleAbstract $default = null)
{
$this->rules = new \ArrayIterator(array());
$this->default = is_null($default) ? new OptionalField() : $default;
}
/**
* @return RuleAbstract
*/
public function getDefault() : RuleAbstract
{
return $this->default;
}
/**
* @return array
*/
public function getRules() : array
{
return $this->rules->getArrayCopy();
}
/**
* @param RuleAbstract $rule
* @param array $aliases
* @return RuleSet
*/
public function add(RuleAbstract $rule, array $aliases = array()) : RuleSet
{
$this->rules->offsetSet(strtolower($rule->getNodeName()), $rule);
$this->addAliases($rule->getNodeName(), $aliases);
return $this;
}
/**
* @param string $name
* @param array $aliases
* @return RuleSet
*/
public function addAliases(string $name, array $aliases) : RuleSet
{
foreach ($aliases as $alias) {
$this->aliases[strtolower($alias)] = strtolower($name);
}
return $this;
}
/**
* @param string $name
* @return RuleAbstract
* @throws NotFoundException
*/
public function get(string $name) : RuleAbstract
{
$name = $this->getNameForAlias(strtolower($name));
if ($this->rules->offsetExists($name)) {
return $this->rules->offsetGet($name);
}
return $this->default;
}
/**
* @param $alias
* @return string
*/
public function getNameForAlias(string $alias) : string
{
if (array_key_exists($alias, $this->aliases)) {
return $this->aliases[$alias];
}
return $alias;
}
}
src/FeedIo/Standard/Atom.php 0000666 00000005057 13165210352 0011650 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Standard;
use DOMDocument;
use FeedIo\Reader\Document;
use FeedIo\Rule\Atom\Author;
use FeedIo\Rule\Atom\LinkNode;
use FeedIo\Rule\Description;
use FeedIo\Rule\PublicId;
use FeedIo\Rule\Atom\Category;
use FeedIo\RuleSet;
class Atom extends XmlAbstract
{
/**
* Atom document must have a Foo Bar
'), ]; $feed = new Feed(); $feed->setTitle('feed title'); foreach ($items as $item) { $feed->add($item); } $formatter = new JsonFormatter(); $string = $formatter->toString($feed); $this->assertJson($string); $json = json_decode($string, true); $this->assertEquals('feed title', $json['title']); $this->assertCount(2, $json['items']); foreach ($json['items'] as $item) { $this->assertArrayHasKey('title', $item); $this->assertArrayHasKey('url', $item); $this->assertArrayHasKey('author', $item); $this->assertArrayHasKey('date_published', $item); } } public function testIsHtml() { $formatter = new JsonFormatter(); $this->assertTrue($formatter->isHtml('lorem ipsum
')); $this->assertFalse($formatter->isHtml('lorem ipsum')); } protected function getItem($title, $description) { $item = new Item(); $author = new Item\Author(); $author->setName('foo bar'); $item->setAuthor($author); $media = new Item\Media(); $media->setUrl('http://something'); $item->addMedia($media); $item->setLink('http://item-url'); $item->addCategory(new Category()); $item->setLastModified(new \DateTime()); $item->setTitle($title); $item->setDescription($description); return $item; } } tests/FeedIo/Parser/AtomTest.php 0000666 00000002523 13165210352 0012552 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FeedIo\Parser; use FeedIo\Feed; use FeedIo\Rule\DateTimeBuilder; use FeedIo\Standard\Atom; use \PHPUnit\Framework\TestCase; class AtomTest extends ParserTestAbstract { const SAMPLE_FILE = 'sample-atom.xml'; const ENCLOSURE_FILE = 'enclosure-atom.xml'; /** * @var \FeedIo\Parser\Atom */ protected $object; /** * @return \FeedIo\StandardAbstract */ public function getStandard() { return new Atom(new DateTimeBuilder()); } public function testEnclosure() { $document = $this->buildDomDocument(static::ENCLOSURE_FILE); $feed = $this->object->parse($document, new Feed()); $count = 0; foreach ($feed as $item) { $count++; $this->assertTrue($item->hasMedia()); $media = $item->getMedias()->current(); $this->assertInstanceOf('\FeedIo\Feed\Item\MediaInterface', $media); $this->assertEquals('video/mpeg', $media->getType()); $this->assertInternalType('string', $media->getUrl()); } $this->assertEquals(1, $count); } } tests/FeedIo/Parser/JsonParserTest.php 0000666 00000001374 13165210352 0013743 0 ustar 00 parse($this->getDocument(), $feed); $this->assertEquals('JSON Feed', $feed->getTitle()); } } tests/FeedIo/Parser/ParserTestAbstract.php 0000666 00000006641 13165210352 0014577 0 ustar 00 getStandard(); $this->object = new Parser($standard, new NullLogger()); } public function testCanHandle() { $document = $this->buildDomDocument(static::SAMPLE_FILE); $this->assertTrue($this->object->getStandard()->canHandle($document)); } public function testGetMainElement() { $document = $this->buildDomDocument(static::SAMPLE_FILE); $element = $this->object->getStandard()->getMainElement($document->getDOMDocument()); $this->assertInstanceOf('\DomElement', $element); } public function testBuildFeedRuleSet() { $ruleSet = $this->object->getStandard()->buildFeedRuleSet(); $this->assertInstanceOf('\FeedIo\RuleSet', $ruleSet); } public function testBuildItemRuleSet() { $ruleSet = $this->object->getStandard()->buildItemRuleSet(); $this->assertInstanceOf('\FeedIo\RuleSet', $ruleSet); } public function testParseBody() { $document = $this->buildDomDocument(static::SAMPLE_FILE); $feed = $this->object->parse($document, new Feed()); $this->assertInstanceOf('\FeedIo\Feed', $feed); $this->assertNotEmpty($feed->getTitle(), 'title must not be empty'); $this->assertNotEmpty($feed->getLink(), 'link must not be empty'); $this->assertNotEmpty($feed->getLastModified(), 'lastModified must not be empty'); $this->assertTrue($feed->valid(), 'the feed must contain an item'); $this->runCategoriesTest($feed); $item = $feed->current(); $this->assertInstanceOf('\FeedIo\Feed\ItemInterface', $item); if ($item instanceof \FeedIo\Feed\ItemInterface) { $this->assertNotEmpty($item->getTitle()); $this->assertNotEmpty($item->getDescription()); $this->assertNotEmpty($item->getPublicId()); $this->assertNotEmpty($item->getLastModified()); $this->assertNotEmpty($item->getLink()); $this->assertCount(1, $item->getAllElements()); $this->assertTrue($item->hasElement('extra')); $this->runCategoriesTest($item); } } protected function runCategoriesTest(\FeedIo\Feed\NodeInterface $node) { $categories = $node->getCategories(); $this->assertCount(1, $categories); $category = $categories->current(); $this->assertInstanceOf('\FeedIo\Feed\Node\CategoryInterface', $category); $this->assertNotEmpty($category->getTerm()); $this->assertNotEmpty($category->getLabel()); } /** * @param $filename * @return Document */ protected function buildDomDocument($filename) { $file = dirname(__FILE__)."/../../samples/{$filename}"; $domDocument = new \DOMDocument(); $domDocument->load($file, LIBXML_NOBLANKS | LIBXML_COMPACT); return new Document($domDocument->saveXML()); } } tests/FeedIo/Parser/RdfTest.php 0000666 00000003037 13165210352 0012366 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FeedIo\Parser; use FeedIo\Feed; use FeedIo\Rule\DateTimeBuilder; use FeedIo\Standard\Rdf; use \PHPUnit\Framework\TestCase; class RdfTest extends ParserTestAbstract { const SAMPLE_FILE = 'sample-rdf.xml'; /** * @return \FeedIo\StandardAbstract */ public function getStandard() { return new Rdf(new DateTimeBuilder()); } public function testParseBody() { $document = $this->buildDomDocument(static::SAMPLE_FILE); $feed = $this->object->parse($document, new Feed()); $this->assertInstanceOf('\FeedIo\Feed', $feed); $this->assertNotEmpty($feed->getTitle(), 'title must not be empty'); $this->assertNotEmpty($feed->getLink(), 'link must not be empty'); $this->assertNotEmpty($feed->getLastModified(), 'lastModified must not be empty'); $this->assertTrue($feed->valid(), 'the feed must contain an item'); $item = $feed->current(); $this->assertInstanceOf('\FeedIo\Feed\ItemInterface', $item); if ($item instanceof \FeedIo\Feed\ItemInterface) { $this->assertNotEmpty($item->getTitle()); $this->assertNotEmpty($item->getDescription()); $this->assertNotEmpty($item->getLastModified()); $this->assertNotEmpty($item->getLink()); } } } tests/FeedIo/Parser/RssTest.php 0000666 00000002417 13165210352 0012423 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FeedIo\Parser; use FeedIo\Feed; use FeedIo\Rule\DateTimeBuilder; use FeedIo\Standard\Rss; use \PHPUnit\Framework\TestCase; class RssTest extends ParserTestAbstract { const SAMPLE_FILE = 'rss/sample-rss.xml'; const ENCLOSURE_FILE = 'rss/rss-enclosure.xml'; /** * @return \FeedIo\StandardAbstract */ public function getStandard() { return new Rss(new DateTimeBuilder()); } public function testEnclosure() { $document = $this->buildDomDocument(static::ENCLOSURE_FILE); $feed = $this->object->parse($document, new Feed()); $count = 0; foreach ($feed as $item) { $count++; $this->assertTrue($item->hasMedia()); $media = $item->getMedias()->current(); $this->assertInstanceOf('\FeedIo\Feed\Item\MediaInterface', $media); $this->assertEquals('audio/mpeg', $media->getType()); $this->assertInternalType('string', $media->getUrl()); } $this->assertEquals(1, $count); } } tests/FeedIo/ParserTest.php 0000666 00000012726 13165210352 0011660 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FeedIo; use FeedIo\Feed\Item; use FeedIo\Parser\XmlParser as Parser; use FeedIo\Reader\Document; use FeedIo\Rule\DateTimeBuilder; use FeedIo\Standard\Rss; use Psr\Log\NullLogger; use \PHPUnit\Framework\TestCase; class ParserTest extends TestCase { /** * @var \FeedIo\Parser */ protected $object; public function setUp() { $date = new DateTimeBuilder(); $date->addDateFormat(\DateTime::ATOM); $standard = $this->getMockForAbstractClass( '\FeedIo\StandardAbstract', array($date), 'StandardMock', true, true, true, ['canHandle', 'getMainElement', 'buildFeedRuleSet'] ); $standard->expects($this->any())->method('canHandle')->will($this->returnValue(true)); $standard->expects($this->any())->method('buildFeedRuleSet')->will($this->returnValue(new RuleSet())); $standard->expects($this->any())->method('getMainElement')->will($this->returnValue(new \DOMElement('test'))); $this->object = new Parser($standard, new NullLogger()); } public function testParse() { $document = new \DOMDocument(); $document->loadXML('A paragraph
second paragraph
A paragraph
We — Manton Reece and Brent Simmons — have noticed that JSON has become the developers’ choice for APIs, and that developers will often go out of their way to avoid XML. JSON is simpler to read and write, and it’s less prone to bugs.
\n\nSo we developed JSON Feed, a format similar to RSS and Atom but in JSON. It reflects the lessons learned from our years of work reading and publishing feeds.
\n\nSee the spec. It’s at version 1, which may be the only version ever needed. If future versions are needed, version 1 feeds will still be valid feeds.
\n\nWe have a WordPress plugin and, coming soon, a JSON Feed Parser for Swift. As more code is written, by us and others, we’ll update the code page.
\n\nSee Mapping RSS and Atom to JSON Feed for more on the similarities between the formats.
\n\nThis website — the Markdown files and supporting resources — is up on GitHub, and you’re welcome to comment there.
\n\nThis website is also a blog, and you can subscribe to the RSS feed or the JSON feed (if your reader supports it).
\n\nWe worked with a number of people on this over the course of several months. We list them, and thank them, at the bottom of the spec. But — most importantly — Craig Hockenberry spent a little time making it look pretty. :)
", "date_published": "2017-05-17T08:02:12-07:00" } ] } tests/samples/rss/expected-rss.xml 0000666 00000001354 13165210352 0013316 0 ustar 00A paragraph
A paragraph
A paragraph
A paragraph