.github/FUNDING.yml 0000644 00000000025 13652327720 0007725 0 ustar 00 github: [alexdebril] .gitignore 0000644 00000000041 13652327720 0006536 0 ustar 00 artifacts/ vendor/ .php_cs.cache .scrutinizer.yml 0000644 00000000233 13652327720 0007733 0 ustar 00 imports: - php tools: external_code_coverage: true filter: excluded_paths: - "examples/" - "bin/" - "doc/" - "tests/" .travis.yml 0000644 00000001610 13652327720 0006662 0 ustar 00 language: php php: - 7.2 - 7.3 - 7.4 - nightly env: global: - PHPUNIT_BIN='vendor/bin/phpunit' - PHPUNIT_CONFIG='phpunit.xml.dist' - PHPUNIT_FLAGS='--stop-on-failure --verbose' matrix: include: - php: '7.3' 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 - ./bin/feedio check tests/feeds.txt 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 CODEOWNERS 0000644 00000000016 13652327720 0006143 0 ustar 00 * @alexdebril CODE_OF_CONDUCT.md 0000644 00000006222 13652327720 0007354 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 0000644 00000002065 13652327720 0005563 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 0000644 00000000377 13652327720 0006222 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 0000644 00000027034 13652327720 0006040 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) [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 - Feeds auto-discovery through HTML headers - a Command line interface to discover and read feeds - Multiple feeds reading at once through asynchronous requests - PSR-7 Response generation with accurate cache headers - 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 - Feed logo support (RSS + Atom) - 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 : https://debril.org/categories/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(); } ``` ### Asynchronous reading of several feeds at once Thanks to Guzzle, feed-io is able to fetch several feeds at once through asynchronous requests. If you're willing to get more information about the way it works, you can read [Guzzle's documentation](http://docs.guzzlephp.org/en/stable/quickstart.html#async-requests). To read feeds using asynchronous requests with feed-io, you need to send a pool of `\FeedIo\Async\Request` objects to `\FeedIo\FeedIo::readAsync` and handle the result with a `\FeedIo\Async\CallbackInterface` of your own. You can also use `\FeedIo\Async\DefaultCallback` in order to test the feature. Each `\FeedIo\Async\Request` is a request you want to perform, it embeds the feed's URL and optionnally a `\DateTime` to define the `modified-since` attribute of the request. The `CallbackInterface` instance needs two methods : ```php /** * @param Result $result */ public function process(Result $result) : void; /** * @param Request $request * @param \Exception $exception */ public function handleError(Request $request, \Exception $exception) : void; ``` `process()` is called on successful reading and parsing to let you process the result. Otherwise `handleError()` will be triggered on faulty calls. Here is an example : [PDOCallback](examples/PDOCallback.php) ## Feeds discovery A web page can refer to one or more feeds in its headers, feed-io provides a way to discover them : ```php $feedIo = \FeedIo\Factory::create()->getFeedIo(); $feeds = $feedIo->discover($url); foreach( $feeds as $feed ) { echo "discovered feed : {$feed}"; } ``` Or you can use feed-io's command line : ```shell ./vendor/bin/feedio discover https://a-website.org ``` You'll get all discovered feeds in the output. ## 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(); // add namespaces $feed->setNS( 'itunes', //namespace 'http://www.itunes.com/dtds/podcast-1.0.dtd' //dtd for the namespace ); $feed->set('itunes,title', 'Sample Title'); //OR any other element defined in the namespace. $item->addElement('itunes:category', 'Education'); // 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); ``` ## Creating a valid PSR-7 Response with a feed You can turn a `\FeedIo\FeedInstance` directly into a PSR-7 valid response using `\FeedIo\FeedIo::getPsrResponse()` : ```php $feed = new \FeedIo\Feed; // feed the beast ... $item = new \FeedIo\Feed\Item; $item->set ... $feed->add($item); $atomResponse = $feedIo->getPsrResponse($feed, 'atom'); $jsonResponse = $feedIo->getPsrResponse($feed, 'json'); ``` ## 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); ``` ### Guzzle Configuration You can configure Guzzle before injecting it to `FeedIo` : ```php use FeedIo\FeedIo; use FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client as GuzzleClient; use \Psr\Log\NullLogger; // We want to timeout after 3 seconds $guzzle = new GuzzleClient(['timeout' => 3]); $client = new Client($guzzle); $logger = new NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); ``` Please read [Guzzle's documentation](http://docs.guzzlephp.org/en/stable/index.html) to get more information about its configuration. #### Caching Middleware usage To prevent your application from hitting the same feeds multiple times, you can inject [Kevin Rob's cache middleware](https://github.com/Kevinrob/guzzle-cache-middleware) into Guzzle's instance : ```php use FeedIo\FeedIo; use FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client As GuzzleClient; use GuzzleHttp\HandlerStack; use Kevinrob\GuzzleCache\CacheMiddleware; use Psr\Log\NullLogger; // Create default HandlerStack $stack = HandlerStack::create(); // Add this middleware to the top with `push` $stack->push(new CacheMiddleware(), 'cache'); // Initialize the client with the handler option $guzzle = new GuzzleClient(['handler' => $stack]); $client = new Client($guzzle); $logger = new NullLogger(); $feedIo = new \FeedIo\FeedIo($client, $logger); ``` As feeds' content may vary often, caching may result in unwanted behaviors. ### Inject a custom Logger You can inject any Logger you want as long as it implements `Psr\Log\LoggerInterface`. Monolog does, but it's the only library : https://packagist.org/providers/psr/log-implementation ```php use FeedIo\FeedIo; use FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client as GuzzleClient; use Custom\Logger; $client = new Client(new GuzzleClient()); $logger = new Logger(); $feedIo = new FeedIo($client, $logger); ``` ### Inject a custom HTTP Client Warning : it is highly recommended to use the default Guzzle Client integration. If you really want to use another library to read feeds, you need to create your own `FeedIo\Adapter\ClientInterface` class to embed interactions with the library : ```php use FeedIo\FeedIo; use Custom\Adapter\Client; use Library\Client as LibraryClient; use Psr\Log\NullLogger; $client = new Client(new LibraryClient()); $logger = new NullLogger(); $feedIo = new FeedIo($client, $logger); ``` ### Factory or Dependency Injection ? Choosing between using the Factory or build `FeedIo` without it is a question you must ask yourself at some point of your project. The Factory is mainly designed to let you use feed-io with the lesser efforts and get your first results in a small amount of time. However, it doesn't let you benefit of all Monolog's and Guzzle's features, which could be annoying. Dependency injection will also let you choose another library to handle logs if you need to. ## 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 The whole documentation is available at https://feed-io.net UPGRADE-3.0.md 0000644 00000002406 13652327720 0006464 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 0000644 00000002531 13652327720 0006464 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 0000755 00000002376 13652327720 0006514 0 ustar 00 #!/usr/bin/env php setName('feed-io : the CLI feed reader'); $application->add(new \FeedIo\Command\CheckCommand()); $application->add(new \FeedIo\Command\ReadCommand()); $application->add(new \FeedIo\Command\DiscoverCommand()); $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 0000644 00000002202 13652327720 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", "symfony/console": "~3.4|~4.0|~5.0" }, "require-dev": { "phpunit/phpunit": "~7.5.0", "monolog/monolog": "1.*", "friendsofphp/php-cs-fixer": "^2.4" }, "suggest": { "monolog/monolog": "Allows to handle logs" }, "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 0000644 00000326533 13652327720 0007270 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#installing-dependencies", "This file is @generated automatically" ], "content-hash": "1552eddc71002cff2afdb9941df2b35b", "packages": [ { "name": "guzzlehttp/guzzle", "version": "6.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", "reference": "0895c932405407fd3a7368b6910c09a24d26db11" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", "reference": "0895c932405407fd3a7368b6910c09a24d26db11", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.6.1", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.1" }, "suggest": { "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { "dev-master": "6.3-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\": "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 is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", "rest", "web service" ], "time": "2019-10-23T15:58:00+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.6.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", "reference": "239400de7a173fe9901b9ac7c06497751f00727a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", "reference": "239400de7a173fe9901b9ac7c06497751f00727a", "shasum": "" }, "require": { "php": ">=5.4.0", "psr/http-message": "~1.0", "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { "ext-zlib": "*", "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" }, "suggest": { "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.6-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", "psr-7", "request", "response", "stream", "uri", "url" ], "time": "2019-07-01T23:21:34+00:00" }, { "name": "psr/container", "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", "homepage": "https://github.com/php-fig/container", "keywords": [ "PSR-11", "container", "container-interface", "container-interop", "psr" ], "time": "2017-02-14T16:28:37+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.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1.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": "2019-11-01T11:05:21+00:00" }, { "name": "ralouphie/getallheaders", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/ralouphie/getallheaders.git", "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { "php": ">=5.6" }, "require-dev": { "php-coveralls/php-coveralls": "^2.1", "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", "autoload": { "files": [ "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Ralph Khattar", "email": "ralph.khattar@gmail.com" } ], "description": "A polyfill for getallheaders.", "time": "2019-03-08T08:55:37+00:00" }, { "name": "symfony/console", "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", "reference": "f0aea3df20d15635b3cb9730ca5eea1c65b7f201" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/console/zipball/f0aea3df20d15635b3cb9730ca5eea1c65b7f201", "reference": "f0aea3df20d15635b3cb9730ca5eea1c65b7f201", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", "symfony/service-contracts": "^1.1|^2" }, "conflict": { "symfony/dependency-injection": "<3.4", "symfony/event-dispatcher": "<4.3|>=5", "symfony/lock": "<4.4", "symfony/process": "<3.3" }, "provide": { "psr/log-implementation": "1.0" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "^3.4|^4.0|^5.0", "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/event-dispatcher": "^4.3", "symfony/lock": "^4.4|^5.0", "symfony/process": "^3.4|^4.0|^5.0", "symfony/var-dumper": "^4.3|^5.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.4-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": "2019-12-01T10:06:17+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.12-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": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php73", "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/2ceb49eaccb9352bff54d22570276bb75ba4a188", "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Php73\\": "" }, "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.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/service-contracts", "version": "v1.1.7", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffcde9615dc5bb4825b9f6aed07716f1f57faae0", "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0", "shasum": "" }, "require": { "php": "^7.1.3", "psr/container": "^1.0" }, "suggest": { "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" } }, "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": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ "abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards" ], "time": "2019-09-17T11:12:18+00:00" } ], "packages-dev": [ { "name": "composer/semver", "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e", "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e", "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": "2019-03-19T17:25:45+00:00" }, { "name": "composer/xdebug-handler", "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f", "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0", "psr/log": "^1.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "autoload": { "psr-4": { "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "John Stevenson", "email": "john-stevenson@blueyonder.co.uk" } ], "description": "Restarts a process without xdebug.", "keywords": [ "Xdebug", "performance" ], "time": "2019-05-27T17:52:04+00:00" }, { "name": "doctrine/annotations", "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc", "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc", "shasum": "" }, "require": { "doctrine/lexer": "1.*", "php": "^7.1" }, "require-dev": { "doctrine/cache": "1.*", "phpunit/phpunit": "^7.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.7.x-dev" } }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, { "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": "2019-10-01T18:55:10+00:00" }, { "name": "doctrine/instantiator", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "a2c590166b2133a4633738648b6b064edae0814a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", "reference": "a2c590166b2133a4633738648b6b064edae0814a", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "doctrine/coding-standard": "^6.0", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.13", "phpstan/phpstan-phpunit": "^0.11", "phpstan/phpstan-shim": "^0.11", "phpunit/phpunit": "^7.0" }, "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://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], "time": "2019-03-17T17:37:11+00:00" }, { "name": "doctrine/lexer", "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea", "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea", "shasum": "" }, "require": { "php": "^7.2" }, "require-dev": { "doctrine/coding-standard": "^6.0", "phpstan/phpstan": "^0.11.8", "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ "annotations", "docblock", "lexer", "parser", "php" ], "time": "2019-07-30T19:33:28+00:00" }, { "name": "friendsofphp/php-cs-fixer", "version": "v2.16.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c8afb599858876e95e8ebfcd97812d383fa23f02", "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02", "shasum": "" }, "require": { "composer/semver": "^1.4", "composer/xdebug-handler": "^1.2", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", "php": "^5.6 || ^7.0", "php-cs-fixer/diff": "^1.3", "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", "symfony/finder": "^3.0 || ^4.0 || ^5.0", "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", "symfony/polyfill-php70": "^1.0", "symfony/polyfill-php72": "^1.4", "symfony/process": "^3.0 || ^4.0 || ^5.0", "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" }, "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "justinrainbow/json-schema": "^5.0", "keradus/cli-executor": "^1.2", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.1", "php-cs-fixer/accessible-object": "^1.0", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", "phpunitgoodpractices/traits": "^1.8", "symfony/phpunit-bridge": "^4.3 || ^5.0", "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." }, "bin": [ "php-cs-fixer" ], "type": "application", "autoload": { "psr-4": { "PhpCsFixer\\": "src/" }, "classmap": [ "tests/Test/AbstractFixerTestCase.php", "tests/Test/AbstractIntegrationCaseFactory.php", "tests/Test/AbstractIntegrationTestCase.php", "tests/Test/Assert/AssertTokensTrait.php", "tests/Test/IntegrationCase.php", "tests/Test/IntegrationCaseFactory.php", "tests/Test/IntegrationCaseFactoryInterface.php", "tests/Test/InternalIntegrationCaseFactory.php", "tests/TestCase.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Dariusz Rumiński", "email": "dariusz.ruminski@gmail.com" } ], "description": "A tool to automatically fix PHP code style", "time": "2019-11-25T22:10:32+00:00" }, { "name": "monolog/monolog", "version": "1.25.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf", "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf", "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": "2019-09-06T13:49:17+00:00" }, { "name": "myclabs/deep-copy", "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", "shasum": "" }, "require": { "php": "^7.1" }, "replace": { "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" }, "files": [ "src/DeepCopy/deep_copy.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", "keywords": [ "clone", "copy", "duplicate", "object", "object graph" ], "time": "2019-08-09T12:45:53+00:00" }, { "name": "paragonie/random_compat", "version": "v9.99.99", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", "shasum": "" }, "require": { "php": "^7" }, "require-dev": { "phpunit/phpunit": "4.*|5.*", "vimeo/psalm": "^1" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", "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", "polyfill", "pseudorandom", "random" ], "time": "2018-07-02T15:55:56+00:00" }, { "name": "phar-io/manifest", "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "phar-io/version": "^2.0", "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": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "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": "2018-07-08T19:19:57+00:00" }, { "name": "php-cs-fixer/diff", "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/diff.git", "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", "shasum": "" }, "require": { "php": "^5.6 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "symfony/process": "^3.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" }, { "name": "SpacePossum" } ], "description": "sebastian/diff v2 backport support for PHP5.6", "homepage": "https://github.com/PHP-CS-Fixer", "keywords": [ "diff" ], "time": "2018-02-15T16:58:55+00:00" }, { "name": "phpdocumentor/reflection-common", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", "shasum": "" }, "require": { "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "~6" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.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": "2018-08-07T13:53:10+00:00" }, { "name": "phpdocumentor/reflection-docblock", "version": "4.3.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e", "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e", "shasum": "" }, "require": { "php": "^7.0", "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", "webmozart/assert": "^1.0" }, "require-dev": { "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "time": "2019-09-12T14:27:41+00:00" }, { "name": "phpdocumentor/type-resolver", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "shasum": "" }, "require": { "php": "^7.1", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { "ext-tokenizer": "^7.1", "mockery/mockery": "~1", "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.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" } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "time": "2019-08-22T18:11:29+00:00" }, { "name": "phpspec/prophecy", "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.8.x-dev" } }, "autoload": { "psr-4": { "Prophecy\\": "src/Prophecy" } }, "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": "2019-10-03T11:07:50+00:00" }, { "name": "phpunit/php-code-coverage", "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", "php": "^7.1", "phpunit/php-file-iterator": "^2.0", "phpunit/php-text-template": "^1.2.1", "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^3.1 || ^4.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { "phpunit/phpunit": "^7.0" }, "suggest": { "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "6.1-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 provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "time": "2018-10-31T16:06:48+00:00" }, { "name": "phpunit/php-file-iterator", "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "050bedf145a257b1ff02746c31894800e5122946" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", "reference": "050bedf145a257b1ff02746c31894800e5122946", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.1" }, "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": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "time": "2018-09-13T20:33:42+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": "2.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.1-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": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "time": "2019-06-07T04:22:29+00:00" }, { "name": "phpunit/php-token-stream", "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1-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": "2019-09-17T06:23:10+00:00" }, { "name": "phpunit/phpunit", "version": "7.5.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "4c92a15296e58191a4cd74cff3b34fc8e374174a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4c92a15296e58191a4cd74cff3b34fc8e374174a", "reference": "4c92a15296e58191a4cd74cff3b34fc8e374174a", "shasum": "" }, "require": { "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "myclabs/deep-copy": "^1.7", "phar-io/manifest": "^1.0.2", "phar-io/version": "^2.0", "php": "^7.1", "phpspec/prophecy": "^1.7", "phpunit/php-code-coverage": "^6.0.7", "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.1", "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", "sebastian/environment": "^4.0", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0", "sebastian/version": "^2.0.1" }, "conflict": { "phpunit/phpunit-mock-objects": "*" }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-soap": "*", "ext-xdebug": "*", "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "7.5-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": "2019-10-28T10:37:36+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": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { "php": "^7.1", "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-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": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.0", "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-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", "udiff", "unidiff", "unified diff" ], "time": "2019-02-04T06:01:07+00:00" }, { "name": "sebastian/environment", "version": "4.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.5" }, "suggest": { "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-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": "2019-05-05T09:05:15+00:00" }, { "name": "sebastian/exporter", "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "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": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" }, { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "time": "2019-09-14T09:02:43+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": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { "php": "^7.1" }, "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": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "time": "2018-10-04T04:07:39+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/event-dispatcher", "version": "v4.3.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", "reference": "6229f58993e5a157f6096fc7145c0717d0be8807" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6229f58993e5a157f6096fc7145c0717d0be8807", "reference": "6229f58993e5a157f6096fc7145c0717d0be8807", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/event-dispatcher-contracts": "^1.1" }, "conflict": { "symfony/dependency-injection": "<3.4" }, "provide": { "psr/event-dispatcher-implementation": "1.0", "symfony/event-dispatcher-implementation": "1.1" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0", "symfony/http-foundation": "^3.4|^4.0", "symfony/service-contracts": "^1.1", "symfony/stopwatch": "~3.4|~4.0" }, "suggest": { "symfony/dependency-injection": "", "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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": "2019-10-01T16:40:32+00:00" }, { "name": "symfony/event-dispatcher-contracts", "version": "v1.1.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", "shasum": "" }, "require": { "php": "^7.1.3" }, "suggest": { "psr/event-dispatcher": "", "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\EventDispatcher\\": "" } }, "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": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ "abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards" ], "time": "2019-09-17T09:54:03+00:00" }, { "name": "symfony/filesystem", "version": "v4.3.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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": "2019-08-20T14:07:54+00:00" }, { "name": "symfony/finder", "version": "v4.3.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", "shasum": "" }, "require": { "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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": "2019-10-30T12:53:54+00:00" }, { "name": "symfony/options-resolver", "version": "v4.3.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", "shasum": "" }, "require": { "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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": "2019-10-28T20:59:01+00:00" }, { "name": "symfony/polyfill-ctype", "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", "reference": "550ebaac289296ce228a706d0867afc34687e3f4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "ctype", "polyfill", "portable" ], "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php70", "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", "reference": "54b4c428a0054e254223797d2713c31e08610831" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", "reference": "54b4c428a0054e254223797d2713c31e08610831", "shasum": "" }, "require": { "paragonie/random_compat": "~1.0|~2.0|~9.99", "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.12-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": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php72", "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", "reference": "04ce3335667451138df4307d6a9b61565560199e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", "reference": "04ce3335667451138df4307d6a9b61565560199e", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.12-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": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/process", "version": "v4.3.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", "shasum": "" }, "require": { "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/stopwatch", "version": "v4.3.6", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/1e4ff456bd625be5032fac9be4294e60442e9b71", "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/service-contracts": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.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": "2019-08-07T11:52:19+00:00" }, { "name": "theseer/tokenizer", "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "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": "2019-06-13T22:48:21+00:00" }, { "name": "webmozart/assert", "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "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": "2019-08-24T08:43:50+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.1" }, "platform-dev": [] } doc/specifications-support.md 0000644 00000011437 13652327720 0012365 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) / top-level (json) interface : FeedInterface | atom | rss | json | getter | setter | | --------------- | ----------------------- | ------------- | --------------- | --------------- | | title | title | title | getTitle | setTitle | | link | link | home_page_url | getLink | setLink | | link (rel=self) | N/A | feed_url | getLink | setLink | | updated | pubDate / lastBuildDate | N/A | getLastModified | setLastModified | | id | N/A | N/A | getPublicId | setPublicId | | N/A | description | description | getDescription | setDescription | | category | category | N/A | getCategories | addCategory | | author | author | author (wip) | getAuthor | setAuthor | | contributor | N/A | N/A | not supported | not supported | | logo | image | icon | getLogo | setLogo | | rights | copyright | N/A | not supported | not supported | | subtitle | N/A | N/A | not supported | not supported | | lang | language | N/A | getLanguage | setLanguage | | base | N/A | N/A | not supported | not supported | | generator | generator | N/A | not supported | not supported | | N/A | managingEditor | N/A | not supported | not supported | | N/A | webMaster | N/A | not supported | not supported | | N/A | docs | N/A | not supported | not supported | | N/A | cloud | hubs | not supported | not supported | | N/A | ttl | N/A | not supported | not supported | | N/A | rating | N/A | not supported | not supported | | N/A | textInput | N/A | not supported | not supported | | N/A | skipdays | N/A | not supported | not supported | | N/A | skipHours | N/A | not supported | not supported | | N/A | N/A | expired | not supported | not supported | ## entry (atom) / item (rss) / item (json) Interface : ItemInterface | atom | rss | json | getter | setter | | ------------------- | ----------- | --------------------------- | --------------- | --------------- | | title | title | title | getTitle | setTitle | | link | link | url | getLink | setLink | | link | enclosure | image (get only) | getMedias | addMedia | | updated / published | pubDate | date_published | getLastModified | setLastModified | | id | guid | id | getPublicId | setPublicId | | content | description | content_html / content_text | getDescription | setDescription | | summary | N/A | summary | not supported | not supported | | source | source | N/A | not supported | not supported | | category | category | tags (wip) | getCategories | addCategory | | author | N/A | author | getAuthor | setAuthor | | contributor | N/A | N/A | not supported | not supported | | N/A | comments | N/A | not supported | not supported | | rights | N/A | N/A | not supported | not supported | | N/A | N/A | external_url | not supported | not supported | | N/A | N/A | banner_image | not supported | not supported | | N/A | N/A | attachments | not supported | not supported | | N/A | N/A | date_modified | not supported | not supported | examples/PdoCallback.php 0000644 00000005242 13652327720 0011244 0 ustar 00 pdo = $pdo; $this->logger = $logger; } /** * @return PDO */ public function getPdo(): PDO { return $this->pdo; } /** * @return LoggerInterface */ public function getLogger(): LoggerInterface { return $this->logger; } /** * @param \FeedIo\Reader\Result $result */ public function process(\FeedIo\Reader\Result $result): void { $feed = $result->getFeed(); $this->getLogger()->info("Received : {$feed->getTitle()} - storing it"); $this->persistFeed($feed); foreach($feed as $item) { $this->persistItem($item); } } /** * @param \FeedIo\FeedInterface $feed */ protected function persistFeed(\FeedIo\FeedInterface $feed) : void { $this->getLogger()->info("storing feed {$feed->getLink()}"); // SQL Stuff //$this->getPdo()->exec('INSERT / UPDATE'); } /** * @param \FeedIo\Feed\ItemInterface $item */ protected function persistItem(\FeedIo\Feed\ItemInterface $item) : void { $this->getLogger()->info("storing item {$item->getTitle()}"); // SQL Stuff // $this->getPdo()->exec('INSERT INTO ...'); } /** * @param \FeedIo\Async\Request $request * @param Exception $exception */ public function handleError(\FeedIo\Async\Request $request, \Exception $exception): void { $this->getLogger()->warning("Error reading {$request->getUrl()} : {$exception->getMessage()}"); } } $logger = (new FeedIo\Factory\Builder\MonologBuilder())->getLogger(); $pdo = new PDO('sqlite:memory:'); $callback = new PdoCallback($pdo, $logger); $feedIo = new \FeedIo\FeedIo(new \FeedIo\Adapter\Guzzle\Client(new \GuzzleHttp\Client()), $logger); $requests = [ new FeedIo\Async\Request('https://jsonfeed.org/feed.json'), new FeedIo\Async\Request('https://jsonfeed.org/xml/rss.xml'), new FeedIo\Async\Request('https://packagist.org/feeds/releases.rss'), new FeedIo\Async\Request('https://packagist.org/feeds/packages.rss'), new FeedIo\Async\Request('https://debril.org/feed/'), new FeedIo\Async\Request('https://localhost:8000'), ]; $feedIo->readAsync($requests, $callback); examples/bootstrap.php 0000644 00000000511 13652327720 0011114 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/change-user-agent.php 0000644 00000001552 13652327720 0012402 0 ustar 00 push( Middleware::log( $logger, new MessageFormatter('{request}') ) ); $client = new \FeedIo\Adapter\Guzzle\Client( new GuzzleHttp\Client([ 'handler' => $stack ]), 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36' ); $feedIo = new \FeedIo\FeedIo($client, $logger); $result = $feedIo->read('http://php.net/feed.atom'); echo "feed title : {$result->getFeed()->getTitle()} \n "; $client->setUserAgent('Another User Agent'); $feedIo->read('http://php.net/feed.atom'); echo "feed title : {$result->getFeed()->getTitle()} \n "; examples/factory.php 0000644 00000001556 13652327720 0010560 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 0000644 00000001734 13652327720 0011614 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 0000644 00000001716 13652327720 0010342 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 0000644 00000002675 13652327720 0012042 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 0000644 00000000731 13652327720 0011677 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 0000644 00000001541 13652327720 0010700 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 0000644 00000001212 13652327720 0011564 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 0000644 00000001500 13652327720 0010155 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 0000644 00000002413 13652327720 0012403 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 0000644 00000001374 13652327720 0011651 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 0000644 00000002552 13652327720 0011025 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 0000644 00000001612 13652327720 0010730 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; } } } examples/test-async.php 0000644 00000001315 13652327720 0011174 0 ustar 00 getLogger(); $feedIo = new \FeedIo\FeedIo(new \FeedIo\Adapter\Guzzle\Client(new \GuzzleHttp\Client()), $logger); $feedIo->readAsync($requests, new \FeedIo\Async\DefaultCallback($logger)); phpunit.xml.dist 0000644 00000000604 13652327720 0007726 0 ustar 00
* // will display http://example.org/video.mpeg
* echo $media->getUrl();
*
*/
interface MediaInterface
{
/**
* @return string
*/
public function getNodeName() : string;
/**
* @param string $nodeName
* @return MediaInterface
*/
public function setNodeName(string $nodeName) : MediaInterface;
/**
* @return bool
*/
public function isThumbnail() : bool;
/**
* @return string
*/
public function getType() : ? string;
/**
* @param string $type
* @return MediaInterface
*/
public function setType(?string $type) : MediaInterface;
/**
* @return string
*/
public function getUrl() : ? string;
/**
* @param string $url
* @return MediaInterface
*/
public function setUrl(?string $url) : MediaInterface;
/**
* @return string
*/
public function getLength() : ? string;
/**
* @param string $length
* @return MediaInterface
*/
public function setLength(?string $length) : MediaInterface;
/**
* @return string
*/
public function getTitle() : ? string;
/**
* @param string $title
* @return MediaInterface
*/
public function setTitle(?string $title) : MediaInterface;
/**
* @return string
*/
public function getDescription() : ? string;
/**
* @param string $description
* @return MediaInterface
*/
public function setDescription(?string $description) : MediaInterface;
/**
* @return string
*/
public function getThumbnail() : ? string;
/**
* @param string $thumbnail
* @return MediaInterface
*/
public function setThumbnail(?string $thumbnail) : MediaInterface;
}
src/FeedIo/Feed/ItemInterface.php 0000644 00000004257 13652327720 0012600 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 0000644 00000012242 13652327720 0010737 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;
/**
* @var string
*/
protected $host;
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 getHost(): ? string
{
return $this->host;
}
/**
* @return string
*/
public function getLink() : ? string
{
return $this->link;
}
/**
* @param string $link
* @return NodeInterface
*/
public function setLink(string $link = null) : NodeInterface
{
$this->link = $link;
$this->setHost($link);
return $this;
}
/**
* @param string|null $link
*/
protected function setHost(string $link = null): void
{
if (!is_null($link)) {
$this->host = '//' . parse_url($link, PHP_URL_HOST);
}
}
/**
* @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 0000644 00000003011 13652327720 0012506 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 0000644 00000002073 13652327720 0014336 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 0000644 00000004030 13652327720 0012324 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 0000644 00000003372 13652327720 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\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 0000644 00000002415 13652327720 0014043 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 0000644 00000006126 13652327720 0012564 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 host
*
* @return string
*/
public function getHost() : ? string;
/**
* 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 0000644 00000003156 13652327720 0011677 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;
/**
* 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;
/**
* @return string $language
*/
public function getLanguage(): ? string ;
/**
* @param string $language
* @return FeedInterface
*/
public function setLanguage(string $language = null): FeedInterface;
/**
* @return string
*/
public function getLogo() : ? string ;
/**
* @param string $logo
* @return NodeInterface
*/
public function setLogo(string $logo = 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 \FeedIo\Adapter\ClientInterface;
*/
protected $client;
/**
* @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, DateTimeBuilderInterface $dateTimeBuilder = null)
{
$this->client = $client;
$this->logger = $logger;
$this->dateTimeBuilder = $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\LastModifiedSince(),
new Reader\Fixer\HttpLastModified(),
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 $reader
* @return FeedIo
*/
public function setReader(Reader $reader) : FeedIo
{
$this->reader = $reader;
return $this;
}
/**
* Discover feeds from the webpage's headers
* @param string $url
* @return array
*/
public function discover(string $url) : array
{
$explorer = new Explorer($this->client, $this->logger);
return $explorer->discover($url);
}
/**
* @param iterable $requests
* @param CallbackInterface $callback
* @param string $feedClass
*/
public function readAsync(iterable $requests, CallbackInterface $callback, string $feedClass = '\FeedIo\Feed') : void
{
$reader = new AsyncReader($this->reader, $this->reader->getClient(), $callback, $feedClass);
$reader->process($requests);
}
/**
* @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);
$this->resetFilters();
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;
}
/**
* Get a PSR-7 compliant response for the given feed
*
* @param \FeedIo\FeedInterface $feed
* @param string $standard
* @param int $maxAge
* @param bool $public
* @return ResponseInterface
*/
public function getPsrResponse(FeedInterface $feed, string $standard, int $maxAge = 600, bool $public = true) : ResponseInterface
{
$this->logAction($feed, "creating a PSR 7 Response in $standard format");
$formatter = $this->getStandard($standard)->getFormatter();
$responseBuilder = new ResponseBuilder($maxAge, $public);
return $responseBuilder->createResponse($standard, $formatter, $feed);
}
/**
* @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 0000644 00000000520 13652327720 0012215 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 0000644 00000001635 13652327720 0013142 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 true;
}
}
src/FeedIo/FilterInterface.php 0000644 00000000733 13652327720 0012257 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 0000644 00000007004 13652327720 0013747 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(),
'icon' => $feed->getLogo(),
'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 0000644 00000010453 13652327720 0013600 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[$optionalField] = 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);
$this->setNS($document, $feed);
return $document;
}
public function setNS(\DOMDocument $document, FeedInterface $feed)
{
$firstChild = $document->firstChild;
foreach ($feed->getNS() as $namespace => $dtd) {
$firstChild->setAttributeNS(
'http://www.w3.org/2000/xmlns/', // xmlns namespace URI
'xmlns:'.$namespace,
$dtd
);
}
}
/**
* @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 0000644 00000000702 13652327720 0012771 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/Http/ResponseBuilder.php 0000644 00000002750 13652327720 0013236 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Http;
use FeedIo\FeedInterface;
use FeedIo\FormatterInterface;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
class ResponseBuilder
{
/**
* @var int $maxAge max-age in seconds
*/
protected $maxAge;
/**
* @var bool $public is the response public
*/
protected $public;
/**
* @param int $maxAge
* @param bool $public
*/
public function __construct(int $maxAge = 600, bool $public = true)
{
$this->maxAge = $maxAge;
$this->public = $public;
}
/**
* @param string $format
* @param FormatterInterface $formatter
* @param FeedInterface $feed
* @return ResponseInterface
*/
public function createResponse(string $format, FormatterInterface $formatter, FeedInterface $feed) : ResponseInterface
{
$headers = [
'Content-Type' => ($format === 'json') ? 'application/json':'application/xhtml+xml',
'Cache-Control' => ($this->public ? 'public':'private') . ", max-age={$this->maxAge}",
'Last-Modified' => $feed->getLastModified()->format(\DateTime::RSS),
];
return new Response(200, $headers, $formatter->toString($feed));
}
}
src/FeedIo/Parser/JsonParser.php 0000644 00000006426 13652327720 0012540 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\Feed\Item\Author;
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'));
$feed->setLogo($this->readOffset($data, 'icon'));
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));
$item->setLink($this->readOffset($dataItem, 'url'));
if (array_key_exists('author', $dataItem)) {
$authorItem = $dataItem['author'];
$author = new Author();
$author->setName($this->readOffset($authorItem, 'name'));
$author->setUri($this->readOffset($authorItem, 'url'));
$author->setEmail($this->readOffset($authorItem, 'email'));
$item->setAuthor($author);
}
$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 0000644 00000000571 13652327720 0015064 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 0000644 00000000575 13652327720 0016031 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/UrlGenerator.php 0000644 00000001634 13652327720 0013057 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;
class UrlGenerator
{
/**
* @param string $host
* @param string $path
* @return string
*/
public function getAbsolutePath(string $path, string $host = null): string
{
if (! parse_url($path, PHP_URL_HOST) && $host) {
return $this->generateAbsolutePath($host, $path);
}
return $path;
}
/**
* @param string $host
* @param string $path
* @return string
*/
public function generateAbsolutePath(string $host, string $path): string
{
$path = substr($path, 0, 1) == '/' ? $path:"/{$path}";
return $host . $path;
}
}
src/FeedIo/Parser/XmlParser.php 0000644 00000006416 13652327720 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\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 0000644 00000006735 13652327720 0012141 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 0000644 00000011764 13652327720 0010421 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;
}
/**
* @return ClientInterface
*/
public function getClient(): ClientInterface
{
return $this->client;
}
/**
* @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 0000644 00000005212 13652327720 0012166 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)
{
$invalid_characters = '/[^\x9\xa\x20-\xD7FF\xE000-\xFFFD]/';
$content = preg_replace($invalid_characters, '', $content);
$this->content = trim(str_replace("\xEF\xBB\xBF", '', $content));
}
/**
* @param $character
* @return bool
*/
public function startWith(string $character) : bool
{
return mb_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/HttpLastModified.php 0000644 00000002630 13652327720 0014672 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;
use FeedIo\Reader\Result;
class HttpLastModified extends FixerAbstract
{
/**
* @param Result $result
* @return FixerAbstract
*/
public function correct(Result $result): FixerAbstract
{
$feed = $result->getFeed();
$response = $result->getResponse();
if ($this->isInvalid($feed) && $response->getLastModified() instanceof \DateTime) {
$this->logger->debug("found last modified: " . $response->getLastModified()->format(\DateTime::RSS));
$feed->setLastModified($response->getLastModified());
$this->correctItems($feed);
}
return $this;
}
protected function correctItems(FeedInterface $feed): void
{
foreach ($feed as $item) {
$item->setLastModified($feed->getLastModified());
}
}
/**
* @param FeedInterface $feed
* @return bool
*/
protected function isInvalid(FeedInterface $feed): bool
{
return is_null($feed->getLastModified()) || $feed->getLastModified() == new \DateTime('@0');
}
}
src/FeedIo/Reader/Fixer/LastModified.php 0000644 00000002511 13652327720 0014030 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;
use FeedIo\Reader\Result;
class LastModified extends FixerAbstract
{
/**
* @param Result $result
* @return FixerAbstract
*/
public function correct(Result $result) : FixerAbstract
{
$feed = $result->getFeed();
$date = new \DateTime('@0');
if (is_null($feed->getLastModified()) || $feed->getLastModified() == $date) {
$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/LastModifiedSince.php 0000644 00000001756 13652327720 0015024 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;
use FeedIo\Reader\Result;
class LastModifiedSince extends FixerAbstract
{
/**
* @param Result $result
* @return FixerAbstract
* @throws \Exception
*
*/
public function correct(Result $result) : FixerAbstract
{
$feed = $result->getFeed();
$date = new \DateTime('@0');
if (count($feed) === 0 && (is_null($feed->getLastModified()) || $feed->getLastModified() == $date)) {
$this->logger->notice("set last modified date to modifiedSince arg for feed {$feed->getTitle()}");
$feed->setLastModified($result->getModifiedSince());
}
return $this;
}
}
src/FeedIo/Reader/Fixer/PublicId.php 0000644 00000002323 13652327720 0013160 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;
use FeedIo\Reader\Result;
class PublicId extends FixerAbstract
{
/**
* @param Result $result
* @return $this
*/
public function correct(Result $result) : FixerAbstract
{
$feed = $result->getFeed();
$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 0000644 00000001462 13652327720 0013154 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 Result $result
* @return FixerAbstract
*/
abstract public function correct(Result $result) : FixerAbstract;
}
src/FeedIo/Reader/FixerSet.php 0000644 00000001501 13652327720 0012136 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 Result $result
* @return FixerSet
*/
public function correct(Result $result) : FixerSet
{
foreach ($this->fixers as $fixer) {
$fixer->correct($result);
}
return $this;
}
}
src/FeedIo/Reader/NoAccurateParserException.php 0000644 00000000574 13652327720 0015476 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 0000644 00000000565 13652327720 0014162 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 0000644 00000004647 13652327720 0011701 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 through 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 0000644 00000003161 13652327720 0012260 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
{
if ($node instanceof ItemInterface) {
$element = $document->createElement(static::NODE_NAME);
$this->appendNonEmptyChild($document, $element, 'name', $node->getAuthor()->getName());
$this->appendNonEmptyChild($document, $element, 'uri', $node->getAuthor()->getUri());
$this->appendNonEmptyChild($document, $element, 'email', $node->getAuthor()->getEmail());
$rootElement->appendChild($element);
}
}
}
src/FeedIo/Rule/Atom/Category.php 0000644 00000002665 13652327720 0012603 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());
$this->setNonEmptyAttribute($element, 'scheme', $category->getScheme());
$this->setNonEmptyAttribute($element, 'term', $category->getTerm());
$this->setNonEmptyAttribute($element, 'label', $category->getLabel());
return $element;
}
}
src/FeedIo/Rule/Atom/Link.php 0000644 00000002032 13652327720 0011707 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 0000644 00000003522 13652327720 0012522 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/Logo.php 0000644 00000002675 13652327720 0011727 0 ustar 00
* (c) Sylvain Le Gleau Foo Bar
'), ]; $feed = new Feed(); $feed->setTitle('feed title'); $feed->setLogo(self::LOGO); 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->assertEquals('http://localhost/logo.jpeg', $json['icon']); $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 0000644 00000002523 13652327720 0012557 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 0000644 00000003127 13652327720 0013746 0 ustar 00 parse($this->getDocument(), $feed); $this->assertEquals('JSON Feed', $feed->getTitle()); $this->assertEquals('https://jsonfeed.org/graphics/icon.png', $feed->getLogo()); $items = $feed->toArray()['items']; $this->assertCount(4, $items); $this->assertEquals('https://jsonfeed.org/2017/05/17/announcing_json_feed', $items[0]['link']); $this->assertNull($items[1]['link']); $this->assertNull($items[0]['author']); $this->assertNull($items[1]['author']); $this->assertEquals('Manton Reece', $items[2]['author']->getName()); $this->assertNull($items[2]['author']->getUri()); $this->assertNull($items[2]['author']->getEmail()); $this->assertEquals('Manton Reece', $items[3]['author']->getName()); $this->assertEquals('http://manton.org', $items[3]['author']->getUri()); $this->assertEquals('manton@micro.blog', $items[3]['author']->getEmail()); } } tests/FeedIo/Parser/MediaRSSTest.php 0000644 00000004001 13652327720 0013257 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\Parser\XmlParser as Parser; use FeedIo\Reader\Document; use FeedIo\Rule\DateTimeBuilder; use FeedIo\Standard\Atom; use Psr\Log\NullLogger; use \PHPUnit\Framework\TestCase; class MediaRssTest extends TestCase { const SAMPLE_FILE = 'rss/sample-youtube.xml'; /** * @return \FeedIo\StandardAbstract */ public function getStandard() { return new Atom(new DateTimeBuilder()); } public function setUp() { $standard = $this->getStandard(); $this->object = new Parser($standard, new NullLogger()); } /** * @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()); } public function testYoutubeFeed() { $document = $this->buildDomDocument(static::SAMPLE_FILE); $feed = $this->object->parse($document, new Feed()); $this->assertEquals(1, count($feed)); foreach ($feed as $item) { $this->assertTrue($item->hasMedia()); $media = $item->getMedias()->current(); $this->assertInstanceOf('\FeedIo\Feed\Item\MediaInterface', $media); $this->assertEquals('YT_VIDEO_TITLE', $media->getTitle()); $this->assertEquals('https://i2.ytimg.com/vi/YT_VIDEO_ID/hqdefault.jpg', $media->getThumbnail()); $this->assertEquals("This is a\nmultiline\ndescription", $media->getDescription()); $this->assertEquals('https://www.youtube.com/v/YT_VIDEO_ID?version=3', $media->getUrl()); } } } tests/FeedIo/Parser/ParserTestAbstract.php 0000644 00000006641 13652327720 0014604 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 0000644 00000003037 13652327720 0012373 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 0000644 00000003502 13652327720 0012424 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'; const DC_CREATOR_FILE = 'rss/rss-with-dc-creator.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); } public function testDcCreator() { $document = $this->buildDomDocument(static::DC_CREATOR_FILE); $feed = $this->object->parse($document, new Feed()); $count = 0; foreach ($feed as $item) { $count++; $author = $item->getAuthor(); $this->assertInstanceOf('\FeedIo\Feed\Item\AuthorInterface', $author); $this->assertEquals('Author Name', $author->getName()); } $this->assertEquals(1, $count); } } tests/FeedIo/Parser/UrlGeneratorTest.php 0000644 00000001574 13652327720 0014275 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 PHPUnit\Framework\TestCase; class UrlGeneratorTest extends TestCase { public function testGetAbsolutePath() { $urlGenerator = new UrlGenerator(); $this->assertEquals( 'http://localhost/folder/file.ext', $urlGenerator->getAbsolutePath('/folder/file.ext', 'http://localhost') ); } public function testGenerateAbsolutePath() { $urlGenerator = new UrlGenerator(); $this->assertEquals( 'http://localhost/folder/file.ext', $urlGenerator->generateAbsolutePath('http://localhost', '/folder/file.ext') ); } } tests/FeedIo/ParserTest.php 0000644 00000012726 13652327720 0011665 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 paragrapha link
second paragraph
A paragrapha link
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" }, { "id": "https://jsonfeed.org/2017/05/17/announcing_json_feed", "title": "Announcing JSON Feed", "content_html": "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" }, { "id": "https://jsonfeed.org/2017/05/17/announcing_json_feed", "title": "Announcing JSON Feed", "content_html": "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", "author": { "name": "Manton Reece" } }, { "id": "https://jsonfeed.org/2017/05/17/announcing_json_feed", "title": "Announcing JSON Feed", "content_html": "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", "author": { "name": "Manton Reece", "url": "http://manton.org", "email": "manton@micro.blog" } } ] } tests/samples/rss/expected-rss.xml 0000644 00000001715 13652327720 0013324 0 ustar 00A paragraph
A paragraph
A paragraph
A paragraph