.github/FUNDING.yml 0000644 00000000025 14004621533 0007714 0 ustar 00 github: [alexdebril] .github/workflows/ci.yml 0000644 00000000574 14004621533 0011263 0 ustar 00 name: CI on: [push] jobs: build-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: php-actions/composer@v5 - name: PHPUnit Tests uses: php-actions/phpunit@v2 with: php_extensions: xdebug bootstrap: tests/bootstrap.php configuration: phpunit.xml.dist args: --coverage-text .gitignore 0000644 00000000041 14004621533 0006525 0 ustar 00 artifacts/ vendor/ .php_cs.cache .scrutinizer.yml 0000644 00000000233 14004621533 0007722 0 ustar 00 imports: - php tools: external_code_coverage: true filter: excluded_paths: - "examples/" - "bin/" - "doc/" - "tests/" .travis.yml 0000644 00000001637 14004621533 0006662 0 ustar 00 language: php php: - 7.3 - 7.4 - 8.0 env: global: - PHPUNIT_BIN='vendor/bin/phpunit' - PHPUNIT_CONFIG='phpunit.xml.dist' - PHPUNIT_FLAGS='--stop-on-failure --verbose' - COMPOSER_FLAGS='' matrix: include: - php: '7.4' env: - PHPUNIT_FLAGS="--stop-on-failure --verbose --coverage-text --coverage-clover=coverage.xml" before_script: - composer config --global repo.packagist composer https://packagist.org - composer install $COMPOSER_FLAGS script : - $PHPUNIT_BIN -c $PHPUNIT_CONFIG $PHPUNIT_FLAGS - if [ -f "coverage.xml" ]; then ./vendor/bin/php-cs-fixer fix --dry-run src/; fi - ./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 14004621533 0006132 0 ustar 00 * @alexdebril CODE_OF_CONDUCT.md 0000644 00000006222 14004621533 0007343 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 14004621533 0005552 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 14004621533 0006211 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 00000033141 14004621533 0006023 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. ## 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(); } ``` In order to save bandwidth, feed-io estimates the next time it will be relevant to read the feed and get new items from it. ```php $nextUpdate = $result->getNextUpdate(); echo "computed next update: {$nextUpdate->format(\DATE_ATOM)}"; // you may need to access the statistics $updateStats = $result->getUpdateStats(); echo "average interval in seconds: {$updateStats->getAverageInterval()}"; ``` feed-io calculates the next update time by first detecting if the feed was active in the last 7 days and if not we consider it as sleepy. The next update date for a sleepy feed is set to the next day at the same time. If the feed isn't sleepy we use the average interval and the median interval by adding those intervals to the feed's last modified date and compare the result to the current time. If the result is in the future, then it's returned as the next update time. If none of them are in the future, we considered the feed will be updated quite soon, so the next update time is one hour later from the moment of the calculation. Please note: the fixed delays for sleepy and closed to be updated feeds can be set through `Result::getNextUpdate()` arguments, see [Result](src/FeedIo/Reader/Result.php) for more details. ### 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'); ``` ## Configure feed-io using the Factory We saw in the [reading section](#reading) that to get a simple `FeedIo` instance we can simply call the `Factory` statically and let it return a fresh `FeedIo` composed of the main dependencies it needs to work. The problem is that we may want to inject configuration to its underlying components, such as configuring Guzzle to ignore SSL errors. For that, we will inject the configuration through `Factory::create()` parameters, first one being for the logging system, and the second one for the HTTP Client (well, Guzzle). ### Configure Guzzle through the Factory A few lines above, we talked about ignoring ssl errors, let's see how to configure Guzzle to do this: ```php $feedIo = \FeedIo\Factory::create( ['builder' => 'NullLogger'], // assuming you want feed-io to keep quiet ['builder' => 'GuzzleClient', 'config' => ['verify' => false]] )->getFeedIo(); ``` It's important to specify the "builder", as it's the class that will be in charge of actually building the instance. ### 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 14004621533 0006453 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 14004621533 0006453 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 14004621533 0006503 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 00000002323 14004621533 0007264 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", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "guzzlehttp/guzzle": "~6.2|~7.0", "psr/log": "~1.0", "symfony/console": "~3.4|~4.0|~5.0" }, "require-dev": { "phpunit/phpunit": "~9.3.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 00000476147 14004621533 0007266 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": "8ae9c2383f21d481577c9f3fea28dc0d", "packages": [ { "name": "guzzlehttp/guzzle", "version": "7.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79", "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.4", "guzzlehttp/psr7": "^1.7", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0" }, "provide": { "psr/http-client-implementation": "1.0" }, "require-dev": { "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", "psr/log": "^1.1" }, "suggest": { "ext-curl": "Required for CURL handler support", "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { "dev-master": "7.1-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" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://sagikazarmark.hu" } ], "description": "Guzzle is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", "psr-18", "psr-7", "rest", "web service" ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", "source": "https://github.com/guzzle/guzzle/tree/7.2.0" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://github.com/alexeyshockov", "type": "github" }, { "url": "https://github.com/gmponos", "type": "github" } ], "time": "2020-10-10T11:47:56+00:00" }, { "name": "guzzlehttp/promises", "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", "reference": "60d379c243457e073cff02bc323a2a86cb355631" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", "reference": "60d379c243457e073cff02bc323a2a86cb355631", "shasum": "" }, "require": { "php": ">=5.5" }, "require-dev": { "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "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" ], "support": { "issues": "https://github.com/guzzle/promises/issues", "source": "https://github.com/guzzle/promises/tree/1.4.0" }, "time": "2020-09-30T07:37:28+00:00" }, { "name": "guzzlehttp/psr7", "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3", "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3", "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.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.7-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" ], "support": { "issues": "https://github.com/guzzle/psr7/issues", "source": "https://github.com/guzzle/psr7/tree/1.7.0" }, "time": "2020-09-30T07:37:11+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" ], "support": { "issues": "https://github.com/php-fig/container/issues", "source": "https://github.com/php-fig/container/tree/master" }, "time": "2017-02-14T16:28:37+00:00" }, { "name": "psr/http-client", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", "homepage": "https://github.com/php-fig/http-client", "keywords": [ "http", "http-client", "psr", "psr-18" ], "support": { "source": "https://github.com/php-fig/http-client/tree/master" }, "time": "2020-06-29T06:28:15+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" ], "support": { "source": "https://github.com/php-fig/http-message/tree/master" }, "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "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" ], "support": { "source": "https://github.com/php-fig/log/tree/1.1.3" }, "time": "2020-03-23T09:12:05+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.", "support": { "issues": "https://github.com/ralouphie/getallheaders/issues", "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, "time": "2019-03-08T08:55:37+00:00" }, { "name": "symfony/console", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", "reference": "47c02526c532fb381374dab26df05e7313978976" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/console/zipball/47c02526c532fb381374dab26df05e7313978976", "reference": "47c02526c532fb381374dab26df05e7313978976", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", "symfony/polyfill-php80": "^1.15", "symfony/service-contracts": "^1.1|^2", "symfony/string": "^5.1" }, "conflict": { "symfony/dependency-injection": "<4.4", "symfony/dotenv": "<5.1", "symfony/event-dispatcher": "<4.4", "symfony/lock": "<4.4", "symfony/process": "<4.4" }, "provide": { "psr/log-implementation": "1.0" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/event-dispatcher": "^4.4|^5.0", "symfony/lock": "^4.4|^5.0", "symfony/process": "^4.4|^5.0", "symfony/var-dumper": "^4.4|^5.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" }, "type": "library", "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", "keywords": [ "cli", "command line", "console", "terminal" ], "support": { "source": "https://github.com/symfony/console/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-12-18T08:03:05+00:00" }, { "name": "symfony/polyfill-ctype", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", "shasum": "" }, "require": { "php": ">=7.1" }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "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" ], "support": { "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-intl-grapheme", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", "reference": "267a9adeb8ecb8071040a740930e077cdfb987af" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/267a9adeb8ecb8071040a740930e077cdfb987af", "reference": "267a9adeb8ecb8071040a740930e077cdfb987af", "shasum": "" }, "require": { "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, "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 intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "grapheme", "intl", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", "reference": "6e971c891537eb617a00bb07a43d182a6915faba" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba", "reference": "6e971c891537eb617a00bb07a43d182a6915faba", "shasum": "" }, "require": { "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, "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 for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "intl", "normalizer", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T17:09:11+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", "shasum": "" }, "require": { "php": ">=7.1" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "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" ], "support": { "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-php73", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", "shasum": "" }, "require": { "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "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" ], "support": { "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-php80", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", "shasum": "" }, "require": { "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, "files": [ "bootstrap.php" ], "classmap": [ "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Ion Bazan", "email": "ion.bazan@gmail.com" }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/service-contracts", "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", "shasum": "" }, "require": { "php": ">=7.2.5", "psr/container": "^1.0" }, "suggest": { "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" }, "thanks": { "name": "symfony/contracts", "url": "https://github.com/symfony/contracts" } }, "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" ], "support": { "source": "https://github.com/symfony/service-contracts/tree/master" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-09-07T11:33:47+00:00" }, { "name": "symfony/string", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/string/zipball/5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "~1.15" }, "require-dev": { "symfony/error-handler": "^4.4|^5.0", "symfony/http-client": "^4.4|^5.0", "symfony/translation-contracts": "^1.1|^2", "symfony/var-exporter": "^4.4|^5.0" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\String\\": "" }, "files": [ "Resources/functions.php" ], "exclude-from-classmap": [ "/Tests/" ] }, "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 String component", "homepage": "https://symfony.com", "keywords": [ "grapheme", "i18n", "string", "unicode", "utf-8", "utf8" ], "support": { "source": "https://github.com/symfony/string/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-12-05T07:33:16+00:00" } ], "packages-dev": [ { "name": "composer/semver", "version": "3.2.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^0.12.54", "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.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" ], "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", "source": "https://github.com/composer/semver/tree/3.2.4" }, "funding": [ { "url": "https://packagist.com", "type": "custom" }, { "url": "https://github.com/composer", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], "time": "2020-11-13T08:59:24+00:00" }, { "name": "composer/xdebug-handler", "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", "reference": "f28d44c286812c714741478d968104c5e604a1d4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4", "reference": "f28d44c286812c714741478d968104c5e604a1d4", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" }, "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" ], "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/xdebug-handler/issues", "source": "https://github.com/composer/xdebug-handler/tree/1.4.5" }, "funding": [ { "url": "https://packagist.com", "type": "custom" }, { "url": "https://github.com/composer", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], "time": "2020-11-13T08:04:11+00:00" }, { "name": "doctrine/annotations", "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/annotations/zipball/ce77a7ba1770462cd705a91a151b6c3746f9c6ad", "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad", "shasum": "" }, "require": { "doctrine/lexer": "1.*", "ext-tokenizer": "*", "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/cache": "1.*", "doctrine/coding-standard": "^6.0 || ^8.1", "phpstan/phpstan": "^0.12.20", "phpunit/phpunit": "^7.5 || ^9.1.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.11.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": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], "support": { "issues": "https://github.com/doctrine/annotations/issues", "source": "https://github.com/doctrine/annotations/tree/1.11.1" }, "time": "2020-10-26T10:28:16+00:00" }, { "name": "doctrine/instantiator", "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^8.0", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", "phpstan/phpstan": "^0.12", "phpstan/phpstan-phpunit": "^0.12", "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", "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": "https://ocramius.github.io/" } ], "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" ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", "source": "https://github.com/doctrine/instantiator/tree/1.4.0" }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { "url": "https://www.patreon.com/phpdoctrine", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", "type": "tidelift" } ], "time": "2020-11-10T18:47:58+00:00" }, { "name": "doctrine/lexer", "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^6.0", "phpstan/phpstan": "^0.11.8", "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.2.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" ], "support": { "issues": "https://github.com/doctrine/lexer/issues", "source": "https://github.com/doctrine/lexer/tree/1.2.1" }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { "url": "https://www.patreon.com/phpdoctrine", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", "type": "tidelift" } ], "time": "2020-05-25T17:44:05+00:00" }, { "name": "friendsofphp/php-cs-fixer", "version": "v2.18.0", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", "reference": "cbc5b50bfa2688a1afca20e5a8c71f058e9ccbef" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/cbc5b50bfa2688a1afca20e5a8c71f058e9ccbef", "reference": "cbc5b50bfa2688a1afca20e5a8c71f058e9ccbef", "shasum": "" }, "require": { "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^1.2", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", "php": "^5.6 || ^7.0 || ^8.0", "php-cs-fixer/diff": "^1.3", "symfony/console": "^3.4.43 || ^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": { "justinrainbow/json-schema": "^5.0", "keradus/cli-executor": "^1.4", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.4.2", "php-cs-fixer/accessible-object": "^1.0", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", "phpspec/prophecy-phpunit": "^1.1 || ^2.0", "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.13 || ^9.5", "phpunitgoodpractices/polyfill": "^1.5", "phpunitgoodpractices/traits": "^1.9.1", "sanmai/phpunit-legacy-adapter": "^6.4 || ^8.2.1", "symfony/phpunit-bridge": "^5.2.1", "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, "suggest": { "ext-dom": "For handling output formats in XML", "ext-mbstring": "For handling non-UTF8 characters.", "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/Test/IsIdenticalConstraint.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", "support": { "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.18.0" }, "funding": [ { "url": "https://github.com/keradus", "type": "github" } ], "time": "2021-01-18T03:31:06+00:00" }, { "name": "monolog/monolog", "version": "1.26.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/2209ddd84e7ef1256b7af205d0717fb62cfc9c33", "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33", "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", "php-amqplib/php-amqplib": "~2.4", "php-console/php-console": "^3.1.3", "phpstan/phpstan": "^0.12.59", "phpunit/phpunit": "~4.5", "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", "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" ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", "source": "https://github.com/Seldaek/monolog/tree/1.26.0" }, "funding": [ { "url": "https://github.com/Seldaek", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", "type": "tidelift" } ], "time": "2020-12-14T12:56:38+00:00" }, { "name": "myclabs/deep-copy", "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "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" ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], "time": "2020-11-13T09:40:50+00:00" }, { "name": "nikic/php-parser", "version": "v4.10.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=7.0" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" ], "type": "library", "extra": { "branch-alias": { "dev-master": "4.9-dev" } }, "autoload": { "psr-4": { "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", "keywords": [ "parser", "php" ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" }, "time": "2020-12-20T10:01:03+00:00" }, { "name": "phar-io/manifest", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "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)", "support": { "issues": "https://github.com/phar-io/manifest/issues", "source": "https://github.com/phar-io/manifest/tree/master" }, "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "e4782611070e50613683d2b9a57730e9a3ba5451" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451", "reference": "e4782611070e50613683d2b9a57730e9a3ba5451", "shasum": "" }, "require": { "php": "^7.2 || ^8.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", "support": { "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.0.4" }, "time": "2020-12-13T23:18:30+00:00" }, { "name": "php-cs-fixer/diff", "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/diff.git", "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/dbd31aeb251639ac0b9e7e29405c1441907f5759", "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759", "shasum": "" }, "require": { "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", "symfony/process": "^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" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" }, { "name": "SpacePossum" } ], "description": "sebastian/diff v2 backport support for PHP5.6", "homepage": "https://github.com/PHP-CS-Fixer", "keywords": [ "diff" ], "support": { "issues": "https://github.com/PHP-CS-Fixer/diff/issues", "source": "https://github.com/PHP-CS-Fixer/diff/tree/v1.3.1" }, "time": "2020-10-14T08:39:05+00:00" }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { "dev-2.x": "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" ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", "version": "5.2.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", "shasum": "" }, "require": { "ext-filter": "*", "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.3", "webmozart/assert": "^1.9.1" }, "require-dev": { "mockery/mockery": "~1.3.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.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" }, { "name": "Jaap van Otterdijk", "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" }, "time": "2020-09-03T19:13:55+00:00" }, { "name": "phpdocumentor/type-resolver", "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { "ext-tokenizer": "*" }, "type": "library", "extra": { "branch-alias": { "dev-1.x": "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", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" }, "time": "2020-09-17T18:55:26+00:00" }, { "name": "phpspec/prophecy", "version": "1.12.2", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", "reference": "245710e971a030f42e08f4912863805570f23d39" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39", "reference": "245710e971a030f42e08f4912863805570f23d39", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", "php": "^7.2 || ~8.0, <8.1", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { "phpspec/phpspec": "^6.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.11.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" ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", "source": "https://github.com/phpspec/prophecy/tree/1.12.2" }, "time": "2020-12-19T10:15:11+00:00" }, { "name": "phpunit/php-code-coverage", "version": "9.2.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1", "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.10.2", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", "sebastian/code-unit-reverse-lookup": "^2.0.2", "sebastian/complexity": "^2.0", "sebastian/environment": "^5.1.2", "sebastian/lines-of-code": "^1.0.3", "sebastian/version": "^3.0.1", "theseer/tokenizer": "^1.2.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcov": "*", "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "9.2-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" ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-11-28T06:44:49+00:00" }, { "name": "phpunit/php-file-iterator", "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "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": "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" ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:57:25+00:00" }, { "name": "phpunit/php-invoker", "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "ext-pcntl": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcntl": "*" }, "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", "role": "lead" } ], "description": "Invoke callables with a timeout", "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ "process" ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:58:55+00:00" }, { "name": "phpunit/php-text-template", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "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", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T05:33:50+00:00" }, { "name": "phpunit/php-timer", "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-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" ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", "version": "9.3.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "f7316ea106df7c9507f4fdaa88c47bc10a3b27a1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f7316ea106df7c9507f4fdaa88c47bc10a3b27a1", "reference": "f7316ea106df7c9507f4fdaa88c47bc10a3b27a1", "shasum": "" }, "require": { "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.1", "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.11.1", "phpunit/php-code-coverage": "^9.1.11", "phpunit/php-file-iterator": "^3.0.4", "phpunit/php-invoker": "^3.1", "phpunit/php-text-template": "^2.0.2", "phpunit/php-timer": "^5.0.1", "sebastian/cli-parser": "^1.0", "sebastian/code-unit": "^1.0.5", "sebastian/comparator": "^4.0.3", "sebastian/diff": "^4.0.2", "sebastian/environment": "^5.1.2", "sebastian/exporter": "^4.0.2", "sebastian/global-state": "^5.0", "sebastian/object-enumerator": "^4.0.2", "sebastian/resource-operations": "^3.0.2", "sebastian/type": "^2.2.1", "sebastian/version": "^3.0.1" }, "require-dev": { "ext-pdo": "*", "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { "ext-soap": "*", "ext-xdebug": "*" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "9.3-dev" } }, "autoload": { "classmap": [ "src/" ], "files": [ "src/Framework/Assert/Functions.php" ] }, "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" ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "source": "https://github.com/sebastianbergmann/phpunit/tree/9.3.11" }, "funding": [ { "url": "https://phpunit.de/donate.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-24T08:08:49+00:00" }, { "name": "psr/event-dispatcher", "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/event-dispatcher.git", "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { "php": ">=7.2.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Standard interfaces for event handling.", "keywords": [ "events", "psr", "psr-14" ], "support": { "issues": "https://github.com/php-fig/event-dispatcher/issues", "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, "time": "2019-01-08T18:20:26+00:00" }, { "name": "sebastian/cli-parser", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:08:49+00:00" }, { "name": "sebastian/code-unit", "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "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": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/diff": "^4.0", "sebastian/exporter": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-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": "Bernhard Schussek", "email": "bschussek@2bepublished.at" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T15:49:45+00:00" }, { "name": "sebastian/complexity", "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { "nikic/php-parser": "^4.7", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "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", "role": "lead" } ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T15:52:27+00:00" }, { "name": "sebastian/diff", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff", "udiff", "unidiff", "unified diff" ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.1-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" ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-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" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:24:23+00:00" }, { "name": "sebastian/global-state", "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.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" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T15:55:19+00:00" }, { "name": "sebastian/lines-of-code", "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { "nikic/php-parser": "^4.6", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-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/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "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": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-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": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:17:30+00:00" }, { "name": "sebastian/resource-operations", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.0" }, "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": "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", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:45:17+00:00" }, { "name": "sebastian/type", "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.3-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": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:18:59+00:00" }, { "name": "sebastian/version", "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { "php": ">=7.3" }, "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": "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", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:39:44+00:00" }, { "name": "symfony/deprecation-contracts", "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", "shasum": "" }, "require": { "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" }, "thanks": { "name": "symfony/contracts", "url": "https://github.com/symfony/contracts" } }, "autoload": { "files": [ "function.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": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/deprecation-contracts/tree/master" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-09-07T11:33:47+00:00" }, { "name": "symfony/event-dispatcher", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", "reference": "1c93f7a1dff592c252574c79a8635a8a80856042" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1c93f7a1dff592c252574c79a8635a8a80856042", "reference": "1c93f7a1dff592c252574c79a8635a8a80856042", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", "symfony/event-dispatcher-contracts": "^2", "symfony/polyfill-php80": "^1.15" }, "conflict": { "symfony/dependency-injection": "<4.4" }, "provide": { "psr/event-dispatcher-implementation": "1.0", "symfony/event-dispatcher-implementation": "2.0" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/error-handler": "^4.4|^5.0", "symfony/expression-language": "^4.4|^5.0", "symfony/http-foundation": "^4.4|^5.0", "symfony/service-contracts": "^1.1|^2", "symfony/stopwatch": "^4.4|^5.0" }, "suggest": { "symfony/dependency-injection": "", "symfony/http-kernel": "" }, "type": "library", "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", "support": { "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-12-18T08:03:05+00:00" }, { "name": "symfony/event-dispatcher-contracts", "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ba7d54483095a198fa51781bc608d17e84dffa2", "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2", "shasum": "" }, "require": { "php": ">=7.2.5", "psr/event-dispatcher": "^1" }, "suggest": { "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" }, "thanks": { "name": "symfony/contracts", "url": "https://github.com/symfony/contracts" } }, "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" ], "support": { "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.2.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-09-07T11:33:47+00:00" }, { "name": "symfony/filesystem", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/fa8f8cab6b65e2d99a118e082935344c5ba8c60d", "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8" }, "type": "library", "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", "support": { "source": "https://github.com/symfony/filesystem/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-11-30T17:05:38+00:00" }, { "name": "symfony/finder", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", "reference": "0b9231a5922fd7287ba5b411893c0ecd2733e5ba" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/finder/zipball/0b9231a5922fd7287ba5b411893c0ecd2733e5ba", "reference": "0b9231a5922fd7287ba5b411893c0ecd2733e5ba", "shasum": "" }, "require": { "php": ">=7.2.5" }, "type": "library", "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", "support": { "source": "https://github.com/symfony/finder/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-12-08T17:02:38+00:00" }, { "name": "symfony/options-resolver", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/87a2a4a766244e796dd9cb9d6f58c123358cd986", "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", "symfony/polyfill-php73": "~1.0", "symfony/polyfill-php80": "^1.15" }, "type": "library", "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" ], "support": { "source": "https://github.com/symfony/options-resolver/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-10-24T12:08:07+00:00" }, { "name": "symfony/polyfill-php70", "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/5f03a781d984aae42cebd18e7912fa80f02ee644", "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644", "shasum": "" }, "require": { "php": ">=7.1" }, "type": "metapackage", "extra": { "branch-alias": { "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "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" ], "support": { "source": "https://github.com/symfony/polyfill-php70/tree/v1.20.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-php72", "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", "shasum": "" }, "require": { "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "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" ], "support": { "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/process", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", "reference": "bd8815b8b6705298beaa384f04fabd459c10bedd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/bd8815b8b6705298beaa384f04fabd459c10bedd", "reference": "bd8815b8b6705298beaa384f04fabd459c10bedd", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-php80": "^1.15" }, "type": "library", "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", "support": { "source": "https://github.com/symfony/process/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-12-08T17:03:37+00:00" }, { "name": "symfony/stopwatch", "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", "reference": "2b105c0354f39a63038a1d8bf776ee92852813af" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2b105c0354f39a63038a1d8bf776ee92852813af", "reference": "2b105c0354f39a63038a1d8bf776ee92852813af", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/service-contracts": "^1.0|^2" }, "type": "library", "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", "support": { "source": "https://github.com/symfony/stopwatch/tree/v5.2.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2020-11-01T16:14:45+00:00" }, { "name": "theseer/tokenizer", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": "^7.2 || ^8.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", "support": { "issues": "https://github.com/theseer/tokenizer/issues", "source": "https://github.com/theseer/tokenizer/tree/master" }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], "time": "2020-07-12T23:59:07+00:00" }, { "name": "webmozart/assert", "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", "vimeo/psalm": "<3.9.1" }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", "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" ], "support": { "issues": "https://github.com/webmozart/assert/issues", "source": "https://github.com/webmozart/assert/tree/master" }, "time": "2020-07-08T17:02:28+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*" }, "platform-dev": [], "plugin-api-version": "2.0.0" } doc/specifications-support.md 0000644 00000011437 14004621533 0012354 0 ustar 00 # synopsis This document explains which attributes are supported by feed-io and how to access them ## top level document : feed (atom) / channel (rss) / 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 14004621533 0011233 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 14004621533 0011103 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 14004621533 0012371 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 14004621533 0010547 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 14004621533 0011603 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 14004621533 0010331 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 14004621533 0012031 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 14004621533 0011666 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 14004621533 0010667 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 14004621533 0011553 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 14004621533 0010144 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 14004621533 0012372 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 14004621533 0011640 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 14004621533 0011014 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 14004621533 0010717 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 14004621533 0011163 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 14004621533 0007715 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 14004621533 0012567 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 00000013226 14004621533 0010731 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, ArrayableInterface
{
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);
$properties['elements'] = iterator_to_array($this->getElementsGenerator());
$properties['categories'] = iterator_to_array($this->getCategoriesGenerator());
foreach ($properties as $name => $property) {
if ($property instanceof \DateTime) {
$properties[$name] = $property->format(\DateTime::ATOM);
} elseif ($property instanceof \ArrayIterator) {
$properties[$name] = [];
foreach ($property as $entry) {
if ($entry instanceof ArrayableInterface) {
$entry = $entry->toArray();
}
$properties[$name] []= $entry;
}
} elseif ($property instanceof ArrayableInterface) {
$properties[$name] = $property->toArray();
}
}
return $properties;
}
}
src/FeedIo/Feed/Node/Category.php 0000644 00000003011 14004621533 0012475 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 14004621533 0014325 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 14004621533 0012313 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 14004621533 0014144 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 14004621533 0014032 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 14004621533 0012553 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 00000003163 14004621533 0011664 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo;
use FeedIo\Feed\NodeInterface;
use FeedIo\Feed\ItemInterface;
/**updat
* 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 14004621533 0012204 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 14004621533 0013131 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 14004621533 0012246 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 14004621533 0013736 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 14004621533 0013567 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 14004621533 0012760 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 00000003142 14004621533 0013221 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}",
];
// Feed could have no items
if ($feed->getLastModified() instanceof \DateTime) {
$headers['Last-Modified'] = $feed->getLastModified()->format(\DateTime::RSS);
}
return new Response(200, $headers, $formatter->toString($feed));
}
}
src/FeedIo/Parser/JsonParser.php 0000644 00000006426 14004621533 0012527 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 14004621533 0015053 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 14004621533 0016020 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 14004621533 0013046 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 14004621533 0012355 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 14004621533 0012130 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 00000011774 14004621533 0010411 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('1800-01-01');
}
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 14004621533 0012155 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 14004621533 0014661 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 14004621533 0014017 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 14004621533 0015013 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 14004621533 0013147 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 14004621533 0013143 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 14004621533 0012125 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 14004621533 0015465 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 14004621533 0014151 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 00000006661 14004621533 0011666 0 ustar 00
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FeedIo\Reader;
use FeedIo\Adapter\ResponseInterface;
use FeedIo\FeedInterface;
use FeedIo\Reader\Result\UpdateStats;
/**
* 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 \FeedIo\Reader\Result\UpdateStats
*/
protected $updateStats;
/**
* @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;
}
/**
* @param int $minDelay
* @param int $sleepyDelay
* @param int $sleepyDuration
* @param float $marginRatio
* @return \DateTime
*/
public function getNextUpdate(
int $minDelay = UpdateStats::DEFAULT_MIN_DELAY,
int $sleepyDelay = UpdateStats::DEFAULT_SLEEPY_DELAY,
int $sleepyDuration = UpdateStats::DEFAULT_DURATION_BEFORE_BEING_SLEEPY,
float $marginRatio = UpdateStats::DEFAULT_MARGIN_RATIO
): \DateTime {
$updateStats = $this->getUpdateStats();
return $updateStats->computeNextUpdate($minDelay, $sleepyDelay, $sleepyDuration, $marginRatio);
}
/**
* @return UpdateStats
*/
public function getUpdateStats(): UpdateStats
{
if (is_null($this->updateStats)) {
$this->updateStats = new UpdateStats($this->getFeed());
}
return $this->updateStats;
}
}
src/FeedIo/Reader/Result/UpdateStats.php 0000644 00000011536 14004621533 0014124 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\Result;
use FeedIo\Feed\ItemInterface;
use FeedIo\FeedInterface;
class UpdateStats
{
/**
* default update delay applied when average or median intervals are outdated
*/
const DEFAULT_MIN_DELAY = 3600;
/**
* default update delay applied when the feed is considered sleepy
*/
const DEFAULT_SLEEPY_DELAY = 86400;
/**
* default duration after which the feed is considered sleepy
*/
const DEFAULT_DURATION_BEFORE_BEING_SLEEPY = 7 * 86400;
/**
* default margin ratio applied to update time calculation
*/
const DEFAULT_MARGIN_RATIO = 0.1;
/**
* @var FeedInterface
*/
protected $feed;
/**
* @var array
*/
protected $intervals;
/**
* UpdateStats constructor.
* @param FeedInterface $feed
*/
public function __construct(FeedInterface $feed)
{
$this->feed = $feed;
$this->intervals = $this->computeIntervals($this->extractDates($feed));
}
/**
* @param int $minDelay
* @param int $sleepyDelay
* @param int $sleepyDuration
* @param float $marginRatio
* @return \DateTime
*/
public function computeNextUpdate(
int $minDelay = self::DEFAULT_MIN_DELAY,
int $sleepyDelay = self::DEFAULT_SLEEPY_DELAY,
int $sleepyDuration = self::DEFAULT_DURATION_BEFORE_BEING_SLEEPY,
float $marginRatio = self::DEFAULT_MARGIN_RATIO
): \DateTime {
if ($this->isSleepy($sleepyDuration, $marginRatio)) {
return (new \DateTime())->setTimestamp(time() + $sleepyDelay);
}
$feedTimeStamp = $this->getFeedTimestamp();
$now = time();
$intervals = [
$this->getAverageInterval(),
$this->getMedianInterval(),
];
sort($intervals);
$newTimestamp = $now + $minDelay;
foreach ($intervals as $interval) {
$computedTimestamp = $this->addInterval($feedTimeStamp, $interval, $marginRatio);
if ($computedTimestamp > $now) {
$newTimestamp = $computedTimestamp;
break;
}
}
return (new \DateTime())->setTimestamp($newTimestamp);
}
/**
* @param int $sleepyDuration
* @param float $marginRatio
* @return bool
*/
public function isSleepy(int $sleepyDuration, float $marginRatio): bool
{
return time() > $this->addInterval($this->getFeedTimestamp(), $sleepyDuration, $marginRatio);
}
/**
* @param int $ts
* @param int $interval
* @param float $marginRatio
* @return int
*/
public function addInterval(int $ts, int $interval, float $marginRatio): int
{
return $ts + intval($interval + $marginRatio * $interval);
}
/**
* @return array
*/
public function getIntervals(): array
{
return $this->intervals;
}
/**
* @return int
*/
public function getMinInterval(): int
{
return count($this->intervals) ? min($this->intervals):0;
}
/**
* @return int
*/
public function getMaxInterval(): int
{
return count($this->intervals) ? max($this->intervals):0;
}
/**
* @return int
*/
public function getAverageInterval(): int
{
$total = array_sum($this->intervals);
return count($this->intervals) ? intval(floor($total / count($this->intervals))):0;
}
/**
* @return int
*/
public function getMedianInterval(): int
{
sort($this->intervals);
$num = floor(count($this->intervals) / 2);
return isset($this->intervals[$num]) ? $this->intervals[$num]:0;
}
private function computeIntervals(array $dates): array
{
rsort($dates);
$intervals = [];
$current = 0;
foreach ($dates as $date) {
if ($current > 0) {
$intervals[] = $current - $date;
}
$current = $date;
}
return $intervals;
}
private function extractDates(FeedInterface $feed): array
{
$dates = [];
foreach ($feed as $item) {
$dates[] = $this->getTimestamp($item) ?? $this->getFeedTimestamp();
}
return $dates;
}
private function getTimestamp(ItemInterface $item): ? int
{
if (! is_null($item->getLastModified())) {
return $item->getLastModified()->getTimestamp();
}
return null;
}
private function getFeedTimestamp(): int
{
return !! $this->feed->getLastModified() ? $this->feed->getLastModified()->getTimestamp():time();
}
}
src/FeedIo/Rule/Atom/Author.php 0000644 00000003161 14004621533 0012247 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 14004621533 0012572 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 00000002534 14004621533 0011705 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')) {
$this->selectAlternateLink($node, $element);
}
}
protected function selectAlternateLink(NodeInterface $node, \DOMElement $element): void
{
if (
($element->hasAttribute('rel') && $element->getAttribute('rel') == 'alternate')
|| is_null($node->getLink())
) {
$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 14004621533 0012511 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 14004621533 0011716 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); } $this->assertEquals(['name' => 'foo bar'], $json['items'][0]['author']); } 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 00000002505 14004621533 0012546 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->assertIsString($media->getUrl()); } $this->assertEquals(1, $count); } } tests/FeedIo/Parser/JsonParserTest.php 0000644 00000003105 14004621533 0013731 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']['name']); $this->assertNull($items[2]['author']['uri']); $this->assertNull($items[2]['author']['email']); $this->assertEquals('Manton Reece', $items[3]['author']['name']); $this->assertEquals('http://manton.org', $items[3]['author']['uri']); $this->assertEquals('manton@micro.blog', $items[3]['author']['email']); } } tests/FeedIo/Parser/MediaRSSTest.php 0000644 00000004007 14004621533 0013254 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(): void { $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 00000006647 14004621533 0014601 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 14004621533 0012362 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 00000003464 14004621533 0012422 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->assertIsString($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 14004621533 0014264 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 00000012154 14004621533 0011647 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(): void { $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 14004621533 0013313 0 ustar 00A paragraph
A paragraph
A paragraph
A paragraph