.github/FUNDING.yml 0000644 00000000025 14037133544 0007722 0 ustar 00 github: [alexdebril] .github/workflows/ci.yml 0000644 00000001132 14037133544 0011260 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: Coding Style uses: php-actions/composer@v5 with: command: "src:lint" - name: PHPUnit Tests uses: php-actions/phpunit@v2 with: php_extensions: xdebug bootstrap: tests/bootstrap.php configuration: phpunit.xml.dist args: --coverage-text - name: PHP Stan uses: php-actions/phpstan@v2 with: path: src level: 5 .gitignore 0000644 00000000067 14037133544 0006543 0 ustar 00 artifacts/ vendor/ .php_cs.cache .phpunit.result.cache .scrutinizer.yml 0000644 00000000233 14037133544 0007730 0 ustar 00 imports: - php tools: external_code_coverage: true filter: excluded_paths: - "examples/" - "bin/" - "doc/" - "tests/" .travis.yml 0000644 00000001637 14037133544 0006670 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 14037133544 0006140 0 ustar 00 * @alexdebril CODE_OF_CONDUCT.md 0000644 00000006222 14037133544 0007351 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 14037133544 0005560 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 14037133544 0006217 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 00000027144 14037133544 0006037 0 ustar 00 # feed-io [![Latest Stable Version](https://poser.pugx.org/debril/feed-io/v/stable.png)](https://packagist.org/packages/debril/feed-io) [![Build Status](https://github.com/alexdebril/feed-io/actions/workflows/ci.yml/badge.svg)](https://github.com/alexdebril/feed-io/actions/workflows/ci.yml/) [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 - 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 - DateTime detection and conversion - A generic HTTP ClientInterface - Guzzle Client integration This library is highly extensible and is designed to adapt to many situations, so if you don't find a solution through the documentation feel free to ask in the [discussions](https://github.com/alexdebril/feed-io/discussions). # Installation Use Composer to add feed-io into your project's requirements : ```sh composer require debril/feed-io ``` # Requirements | feed-io | PHP | | --------| ---- | | 4.x | 7.1+ | | 5.0 | 8.0+ | feed-io 4 requires PHP 7.1+, feed-io 5 requires PHP 8.0+. All versions relies on `psr/log` and `guzzle`. it suggests `monolog` for logging. Monolog is not the only library suitable to handle feed-io's logs, you can use any PSR/Log compliant library instead. # 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); // 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. ## 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'); ``` ## Adding a StyleSheet ```php $feed = new FeedIo\Feed; $feed->setTitle('...'); $styleSheet = new StyleSheet('http://url-of-the-xsl-stylesheet.xsl'); $feed->setStyleSheet($styleSheet); ``` ## 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. ## Built with PHP Storm Most of feed-io's code was written using [PHP Storm](https://www.jetbrains.com/phpstorm/) courtesy of Jetbrains. UPGRADE-3.0.md 0000644 00000002406 14037133544 0006461 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 14037133544 0006461 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. UPGRADE-5.0.md 0000644 00000002572 14037133544 0006467 0 ustar 00 # UPGRADE FROM 4.x to 5.0 Several major changes in version 5.0: - Full migration to PHP 8.0 - `readSince()` has been removed alongside with the `FilterInterface` - `readAsync()` has been removed - `description` is an attribute of the **feed**, whereas the **item** contains `summary` and `content` ## readSince() removal Despite how convenient `readSince()` can be, it doesn't work in every case. It's because some feeds doesn't include the publication date for each item, making impossible to filter the outdated ones and keep only the fresh material. As a consequence, the client application is forced to check the item's existence in its database exactly like it would do without the date filtering. That makes `readSince()` pointless, so it's better to remove it. The `FilterInterface` is also removed as it's not used for any other matter. ## readAsync() removal It's a complicated piece of code, complex to use and that could be easily replaced with another architecture to perform concurrent reads. It's not worth keeping it, so the best choice is to stick with the most used reading method. ## `description` / `content` naming change get/setDescription is now at the Feed's level, items expose get/setSummary and get/setContent. ## No more complaining about malformed date When a feed-io is malformed, feed-io won't throw an exception anymore. It will return current date instead bin/feedio 0000755 00000002376 14037133544 0006511 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 00000002621 14037133544 0007273 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": ">=8.0", "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", "phpstan/phpstan": "^0.12.81" }, "suggest": { "monolog/monolog": "Allows to handle logs" }, "autoload": { "psr-4": {"FeedIo\\": "src/FeedIo"} }, "scripts": { "src:php-stan": "./vendor/bin/phpstan analyse src/ --level 5", "src:fix": "./vendor/bin/php-cs-fixer fix src", "src:lint": "./vendor/bin/php-cs-fixer fix --dry-run --diff src", "src:install-hook": "chmod +x pre-commit.sh && cp pre-commit.sh .git/hooks/pre-commit" }, "config": { "preferred-install": { "*": "dist" } } } composer.lock 0000644 00000502543 14037133544 0007262 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": "598a2c9173160ae2c8e3e3fde67c30d1", "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.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "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.1" }, "time": "2021-03-07T09:25:29+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.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { "php": ">=7.2.0" }, "type": "library", "autoload": { "psr-4": { "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "https://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/1.1.1" }, "time": "2021-03-05T17:36:06+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.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79", "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79", "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": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "keywords": [ "cli", "command line", "console", "terminal" ], "support": { "source": "https://github.com/symfony/console/tree/v5.2.5" }, "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-03-06T13:42:15+00:00" }, { "name": "symfony/polyfill-ctype", "version": "v1.22.1", "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.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": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-intl-grapheme", "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "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.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": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", "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.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": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", "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.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": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-php73", "version": "v1.22.1", "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.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": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-php80", "version": "v1.22.1", "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.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": "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.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", "reference": "4e78d7d47061fa183639927ec40d607973699609" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", "reference": "4e78d7d47061fa183639927ec40d607973699609", "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", "i18n", "string", "unicode", "utf-8", "utf8" ], "support": { "source": "https://github.com/symfony/string/tree/v5.2.4" }, "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-02-16T10:20:28+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.12.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/annotations/zipball/b17c5014ef81d212ac539f07a1001832df1b6d3b", "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b", "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", "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.12.1" }, "time": "2021-02-21T21:00:45+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.3", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", "reference": "ab99202fccff2a9f97592fbe1b5c76dd06df3513" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ab99202fccff2a9f97592fbe1b5c76dd06df3513", "reference": "ab99202fccff2a9f97592fbe1b5c76dd06df3513", "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/Test/TokensWithObservedTransformers.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.3" }, "funding": [ { "url": "https://github.com/keradus", "type": "github" } ], "time": "2021-03-10T19:39:05+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.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", "reference": "bae7c545bef187884426f042434e561ab1ddb182", "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.1.0" }, "time": "2021-02-23T14:00:09+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": "phpstan/phpstan", "version": "0.12.81", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", "shasum": "" }, "require": { "php": "^7.1|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" }, "bin": [ "phpstan", "phpstan.phar" ], "type": "library", "extra": { "branch-alias": { "dev-master": "0.12-dev" } }, "autoload": { "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", "source": "https://github.com/phpstan/phpstan/tree/0.12.81" }, "funding": [ { "url": "https://github.com/ondrejmirtes", "type": "github" }, { "url": "https://www.patreon.com/phpstan", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", "type": "tidelift" } ], "time": "2021-03-08T22:03:02+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.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", "reference": "d08d6ec121a425897951900ab692b612a61d6240" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", "reference": "d08d6ec121a425897951900ab692b612a61d6240", "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": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" }, "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-02-18T17:12:37+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.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/710d364200997a5afde34d9fe57bd52f3cc1e108", "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108", "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": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/filesystem/tree/v5.2.4" }, "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-02-12T10:38:38+00:00" }, { "name": "symfony/finder", "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", "reference": "0d639a0943822626290d169965804f79400e6a04" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", "reference": "0d639a0943822626290d169965804f79400e6a04", "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": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/finder/tree/v5.2.4" }, "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-02-15T18:55:04+00:00" }, { "name": "symfony/options-resolver", "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", "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": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ "config", "configuration", "options" ], "support": { "source": "https://github.com/symfony/options-resolver/tree/v5.2.4" }, "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-27T12:56:27+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.1", "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.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": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/process", "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "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": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/process/tree/v5.2.4" }, "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-27T10:15:41+00:00" }, { "name": "symfony/stopwatch", "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b12274acfab9d9850c52583d136a24398cdf1a0c", "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c", "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": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/stopwatch/tree/v5.2.4" }, "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-27T10:15:41+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.10.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.10-dev" } }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ "assert", "check", "validate" ], "support": { "issues": "https://github.com/webmozarts/assert/issues", "source": "https://github.com/webmozarts/assert/tree/1.10.0" }, "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.0", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*" }, "platform-dev": [], "plugin-api-version": "2.0.0" } doc/specifications-support.md 0000644 00000011437 14037133544 0012362 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 | | description | description | description | getDescription | setDescription | | category | category | N/A | getCategories | addCategory | | author | author | author(s) | 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 | getContent | setContent | | summary | N/A | summary | getSummary | setSummary | | 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 14037133544 0011241 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 14037133544 0011111 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 14037133544 0012377 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 14037133544 0010555 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 00000002041 14037133544 0011601 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'); $feed->setDescription('my greate 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->setContent("Hope you like the code you are reading"); $item->setSummary('my summary'); $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 14037133544 0010337 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 14037133544 0012037 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 14037133544 0011674 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 00000002010 14037133544 0010665 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); $media = new \FeedIo\Feed\Item\Media(); $media->setUrl('http://localhost/some-resource.jpg'); $media->setType('image/jpeg'); $item->addMedia($media); return $item; } examples/media-reading.php 0000644 00000001212 14037133544 0011561 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 14037133544 0010152 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 14037133544 0012400 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 14037133544 0011646 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 14037133544 0011022 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 14037133544 0010725 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 14037133544 0011171 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 00000000736 14037133544 0007731 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 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 00000004016 14037133544 0012566 0 ustar 00 initElements();
$this->categories = new ArrayIterator();
}
public function set(string $name, string $value = null) : NodeInterface
{
$element = $this->newElement();
$element->setName($name);
$element->setValue($value);
$this->addElement($element);
return $this;
}
public function getAuthor() : ? AuthorInterface
{
return $this->author;
}
public function setAuthor(AuthorInterface $author = null) : NodeInterface
{
$this->author = $author;
return $this;
}
public function newAuthor() : AuthorInterface
{
return new Author();
}
public function getCategories() : iterable
{
return $this->categories;
}
public function getCategoriesGenerator() : Generator
{
foreach ($this->categories as $category) {
yield $category->getlabel();
}
}
public function addCategory(CategoryInterface $category) : NodeInterface
{
$this->categories->append($category);
return $this;
}
public function newCategory() : CategoryInterface
{
return new Category();
}
public function getTitle() : ? string
{
return $this->title;
}
public function setTitle(string $title = null) : NodeInterface
{
$this->title = $title;
return $this;
}
public function getPublicId() : ? string
{
return $this->publicId;
}
public function setPublicId(string $publicId = null) : NodeInterface
{
$this->publicId = $publicId;
return $this;
}
public function getLastModified() : ? DateTime
{
return $this->lastModified;
}
public function setLastModified(DateTime $lastModified = null) : NodeInterface
{
$this->lastModified = $lastModified;
return $this;
}
public function getHost(): ? string
{
return $this->host;
}
public function getLink() : ? string
{
return $this->link;
}
public function setLink(string $link = null) : NodeInterface
{
$this->link = $link;
$this->setHost($link);
return $this;
}
protected function setHost(string $link = null): void
{
if (!is_null($link)) {
$this->host = '//' . parse_url($link, PHP_URL_HOST);
}
}
public function getValue(string $name) : ? string
{
foreach ($this->getElementIterator($name) as $element) {
return $element->getValue();
}
return null;
}
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 00000002357 14037133544 0012517 0 ustar 00 term;
}
/**
* @param string|null $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|null $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|null $label
* @return CategoryInterface
*/
public function setLabel(string $label = null) : CategoryInterface
{
$this->label = $label;
return $this;
}
}
src/FeedIo/Feed/Node/CategoryInterface.php 0000644 00000001536 14037133544 0014336 0 ustar 00 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|null
*/
public function getValue() : ? string
{
return $this->value;
}
/**
* @param string|null $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|null $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 00000003021 14037133544 0014141 0 ustar 00 http://example.org/some-sound.mp3
*
* you must set the ElementInstance's properties this way
*
*
* $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 00000001712 14037133544 0014037 0 ustar 00 getInnerIterator()->current();
return (0 == strcasecmp($this->name, $element->getName()));
}
}
src/FeedIo/Feed/NodeInterface.php 0000644 00000006116 14037133544 0012560 0 ustar 00 href;
}
public function getType(): string
{
return $this->type;
}
}
src/FeedIo/FeedInterface.php 0000644 00000004415 14037133544 0011673 0 ustar 00
* // $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
{
protected Reader $reader;
public function __construct(
protected ClientInterface $client,
protected LoggerInterface $logger,
protected ?SpecificationInterface $specification = null
) {
if (is_null($this->specification)) {
$this->specification = new Specification($this->logger);
}
$this->reader = new Reader($this->client, $this->logger);
foreach ($this->specification->getAllStandards() as $standard) {
$parser = $this->specification->newParser($standard->getSyntaxFormat(), $standard);
$this->reader->addParser($parser);
}
}
/**
* Adds a standard to the Specification.
*
* @see Specification
* @param string $name
* @param StandardAbstract $standard
* @return $this
*/
public function addStandard(string $name, StandardAbstract $standard): self
{
$this->specification->addStandard($name, $standard);
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);
}
/**
* Read the feed hosted at `$url` and populate the `$feed` accordingly.
* `$modifiedSince` is used to return an empty result if the HTTP's response is 'not modified'
*
* @param string $url
* @param FeedInterface|null $feed
* @param DateTime|null $modifiedSince
* @return Result
*/
public function read(string $url, FeedInterface $feed = null, DateTime $modifiedSince = null) : Result
{
if (is_null($feed)) {
$feed = new Feed();
}
$this->logAction($feed, "read access : $url into a feed instance");
$result = $this->reader->read($url, $feed, $modifiedSince);
$this->specification->getFixerSet()->correct($result);
return $result;
}
/**
* Get a PSR-7 compliant response for the given feed
*
* @param 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->specification->getStandard($standard)->getFormatter();
$responseBuilder = new ResponseBuilder($maxAge, $public);
return $responseBuilder->createResponse($standard, $formatter, $feed);
}
/**
* Return the feed in the XML or JSON format according to the `$standard` value (can be "rss", "atom" or "json").
*
* @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->specification->getStandard($standard)->getFormatter();
return $formatter->toString($feed);
}
/**
* Convert to RSS format
*
* @param FeedInterface $feed
* @return string
*/
public function toRss(FeedInterface $feed) : string
{
return $this->format($feed, 'rss');
}
/**
* Convert to Atom
*
* @param FeedInterface $feed
* @return string
*/
public function toAtom(FeedInterface $feed) : string
{
return $this->format($feed, 'atom');
}
/**
* Convert to JSON Feed
*
* @param FeedInterface $feed
* @return string
*/
public function toJson(FeedInterface $feed) : string
{
return $this->format($feed, 'json');
}
/**
* Add a new item to the list of the crooked date formats supported
*
* @param array $formats
* @return $this
*/
public function addDateFormats(array $formats) : FeedIo
{
foreach ($formats as $format) {
$this->getDateTimeBuilder()->addDateFormat($format);
}
return $this;
}
/**
* Get a direct access to the DateTime builder
*
* @return DateTimeBuilderInterface
*/
public function getDateTimeBuilder() : DateTimeBuilderInterface
{
return $this->specification->getDateTimeBuilder();
}
/**
* Get the Reader used by feed-io
*
* @return Reader
*/
public function getReader() : Reader
{
return $this->reader;
}
/**
* Log what just happened
*
* @param FeedInterface $feed
* @param string $message
* @return $this
*/
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 00000000147 14037133544 0012217 0 ustar 00 toArray($feed));
}
public function toArray(FeedInterface $feed) : array
{
$out = 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)),
]);
$this->handleAuthor($feed, $out);
return $out;
}
public function itemsToArray(FeedInterface $feed) : Traversable
{
foreach ($feed as $item) {
yield $this->itemToArray($item);
}
}
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);
}
public function itemToBaseArray(Feed\ItemInterface $item) : array
{
$offset = $this->isHtml($item->getContent()) ? 'content_html':'content_text';
return [
'id' => $item->getPublicId(),
'title' => $item->getTitle(),
'summary'=> $item->getSummary(),
$offset => $item->getContent(),
'url' => $item->getLink(),
];
}
public function isHtml(?string $string) : bool
{
return !! $string && $string !== strip_tags($string);
}
public function handleAuthor(Feed\NodeInterface $node, array &$array) : array
{
if (! is_null($node->getAuthor())) {
$array['authors'] = [array_filter([
'name' => $node->getAuthor()->getName(),
'url' => $node->getAuthor()->getUri(),
])];
}
return $array;
}
public function handleMedia(Feed\ItemInterface $item, array &$array) : array
{
if ($item->hasMedia()) {
$attachments = [];
/** @var Feed\Item\MediaInterface $media */
foreach ($item->getMedias() as $media) {
$attachments[] = array_filter([
'url' => $media->getUrl(),
'mime_type' => $media->getType(),
'title' => $media->getTitle(),
'size_in_bytes' => $media->getLength(),
]);
}
$array['attachments'] = $attachments;
}
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 00000007667 14037133544 0013612 0 ustar 00 standard->getFeedRuleSet();
$mainElement = $this->standard->getMainElement($document);
$this->buildElements($rules, $document, $mainElement, $feed);
return $this;
}
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;
}
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);
}
}
public function getAllRules(RuleSet $ruleSet, NodeInterface $node) : iterable
{
$rules = $ruleSet->getRules();
if ($node instanceof ElementsAwareInterface) {
$optionalFields = $node->listElements();
foreach ($optionalFields as $optionalField) {
$rules[$optionalField] = new OptionalField($optionalField);
}
}
return $rules;
}
public function getEmptyDocument() : DOMDocument
{
return new DOMDocument('1.0', 'utf-8');
}
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();
}
public function toDom(FeedInterface $feed) : DOMDocument
{
$document = $this->getDocument();
$this->setHeaders($document, $feed);
$this->setItems($document, $feed);
$this->setNS($document, $feed);
$this->setStyleSheet($document, $feed);
return $document;
}
public function setNS(DOMDocument $document, FeedInterface $feed)
{
$firstChild = $document->firstChild;
if ($firstChild instanceof DOMElement) {
foreach ($feed->getNS() as $namespace => $dtd) {
$firstChild->setAttributeNS(
'http://www.w3.org/2000/xmlns/', // xmlns namespace URI
'xmlns:'.$namespace,
$dtd
);
}
}
}
public function setItems(DOMDocument $document, FeedInterface $feed) : XmlFormatter
{
foreach ($feed as $item) {
$this->addItem($document, $item);
}
return $this;
}
public function setStyleSheet(DOMDocument $document, FeedInterface $feed): XmlFormatter
{
$styleSheet = $feed->getStyleSheet();
if ($styleSheet instanceof StyleSheet) {
$attributes = sprintf('type="%s" href="%s"', $styleSheet->getType(), $styleSheet->getHref());
$xsl = $document->createProcessingInstruction('xml-stylesheet', $attributes);
$document->insertBefore($xsl, $document->firstChild);
}
return $this;
}
}
src/FeedIo/FormatterInterface.php 0000644 00000000331 14037133544 0012764 0 ustar 00 ($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 00000010267 14037133544 0012533 0 ustar 00 getJsonAsArray();
$feed->setTitle($this->readOffset($data, 'title'));
$feed->setDescription($this->readOffset($data, 'description'));
$feed->setLink($this->readOffset($data, 'home_page_url'));
$feed->setUrl($this->readOffset($data, 'feed_url'));
$feed->setLogo($this->readOffset($data, 'icon'));
$this->readAuthor($feed, $data);
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->setContent($this->readOffset($dataItem, 'content_text', $contentHtml));
$item->setSummary($this->readOffset($dataItem, 'summary'));
$item->setLink($this->readOffset($dataItem, 'url'));
$this->readAuthor($item, $dataItem);
$this->readMedias($item, $dataItem);
$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;
}
protected function readMedias(ItemInterface $item, array $data): void
{
if (array_key_exists('attachments', $data)) {
foreach ($data['attachments'] as $attachment) {
$media = new Item\Media();
$media
->setType($attachment['mime_type'])
->setUrl($attachment['url'])
->setLength($attachment['size_in_bytes'] ?? null)
->setTitle($attachment['title'] ?? null);
$item->addMedia($media);
}
}
}
protected function readAuthor(NodeInterface $node, array $data): void
{
if (array_key_exists('author', $data)) {
$author = $this->extractAuthor($data['author']);
$node->setAuthor($author);
}
if (array_key_exists('authors', $data) && is_array($data['authors'])) {
$author = $this->extractAuthor(reset($data['authors']));
$node->setAuthor($author);
}
}
protected function extractAuthor(array $data): Author
{
$author = new Author();
$author->setName($this->readOffset($data, 'name'));
$author->setUri($this->readOffset($data, 'url'));
$author->setEmail($this->readOffset($data, 'email'));
return $author;
}
}
src/FeedIo/Parser/MissingFieldsException.php 0000644 00000000220 14037133544 0015050 0 ustar 00 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 00000005256 14037133544 0012364 0 ustar 00 standard instanceof XmlAbstract) {
return (strtolower($this->standard->getItemNodeName()) === strtolower($tagName));
}
return false;
}
public function parseContent(Document $document, FeedInterface $feed) : FeedInterface
{
if ($this->standard instanceof XmlAbstract) {
$element = $this->standard->getMainElement($document->getDOMDocument());
$this->parseNode($feed, $element, $this->standard->getFeedRuleSet());
}
return $feed;
}
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;
}
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;
}
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->getItemRuleSet());
$this->addValidItem($item, $newItem);
} else {
$rule = $ruleSet->get($node->tagName);
$rule->setProperty($item, $node);
}
}
protected function getItemRuleSet(): RuleSet
{
if ($this->standard instanceof XmlAbstract) {
return $this->standard->getItemRuleSet();
}
throw new FeedIoException('Not an XML Standard');
}
}
src/FeedIo/ParserAbstract.php 0000644 00000002663 14037133544 0012132 0 ustar 00 standard->canHandle($document)) {
throw new UnsupportedFormatException('this is not a supported format');
}
$this->checkBodyStructure($document, $this->standard->getMandatoryFields());
$this->parseContent($document, $feed);
return $feed;
}
public function getStandard() : StandardAbstract
{
return $this->standard;
}
public function addValidItem(FeedInterface $feed, NodeInterface $item) : ParserAbstract
{
if ($item instanceof ItemInterface) {
$feed->add($item);
}
return $this;
}
abstract public function parseContent(Document $document, FeedInterface $feed) : FeedInterface;
abstract public function checkBodyStructure(Document $document, iterable $mandatoryFields) : bool;
}
src/FeedIo/Reader.php 0000644 00000007461 14037133544 0010415 0 ustar 00 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;
}
/**
* @param string $url
* @param FeedInterface $feed
* @param DateTime|null $modifiedSince
* @return Result
*/
public function read(string $url, FeedInterface $feed, DateTime $modifiedSince = null) : Result
{
$this->logger->debug("start reading {$url}");
if (is_null($modifiedSince)) {
$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->debug("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 00000003771 14037133544 0012173 0 ustar 00 content = trim(str_replace("\xEF\xBB\xBF", '', $content));
}
public function startWith(string $character) : bool
{
return mb_substr($this->content, 0, 1) === $character;
}
public function isJson() : bool
{
return $this->startWith('{');
}
public function isXml() : bool
{
return $this->startWith('<');
}
public function getDOMDocument() : DOMDocument
{
if (is_null($this->domDocument)) {
$this->domDocument = $this->loadDomDocument();
}
return $this->domDocument;
}
public function getJsonAsArray() : array
{
if (is_null($this->jsonArray)) {
$this->jsonArray = $this->loadJsonAsArray();
}
return $this->jsonArray;
}
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;
}
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 00000002257 14037133544 0014674 0 ustar 00 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/PublicId.php 0000644 00000001752 14037133544 0013162 0 ustar 00 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 00000000641 14037133544 0013147 0 ustar 00 logger = $logger;
return $this;
}
abstract public function correct(Result $result) : FixerAbstract;
}
src/FeedIo/Reader/FixerSet.php 0000644 00000000645 14037133544 0012143 0 ustar 00 fixers[] = $fixer;
return $this;
}
public function correct(Result $result) : FixerSet
{
foreach ($this->fixers as $fixer) {
$fixer->correct($result);
}
return $this;
}
}
src/FeedIo/Reader/NoAccurateParserException.php 0000644 00000000223 14037133544 0015462 0 ustar 00 date = new DateTime();
}
public function getDate() : DateTime
{
return $this->date;
}
public function getDocument() : Document
{
return $this->document;
}
public function getFeed() : FeedInterface
{
return $this->feed;
}
public function getModifiedSince() : ?DateTime
{
return $this->modifiedSince;
}
public function getResponse() : ResponseInterface
{
return $this->response;
}
public function getUrl() : string
{
return $this->url;
}
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);
}
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 00000011025 14037133544 0014123 0 ustar 00 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 00000002302 14037133544 0012251 0 ustar 00 newAuthor();
$author->setName($this->getChildValue($element, 'name'));
$author->setUri($this->getChildValue($element, 'uri'));
$author->setEmail($this->getChildValue($element, 'email'));
$node->setAuthor($author);
}
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getAuthor();
}
protected function addElement(DomDocument $document, DOMElement $rootElement, NodeInterface $node) : void
{
$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 00000002314 14037133544 0012567 0 ustar 00 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/Content.php 0000644 00000001763 14037133544 0012433 0 ustar 00 setContent(
$this->getProcessedContent($element, $node)
);
}
}
protected function addElement(DomDocument $document, DOMElement $rootElement, NodeInterface $node) : void
{
if ($node instanceof ItemInterface) {
$rootElement->appendChild(
$this->generateElement($document, $node->getContent())
);
}
}
protected function hasValue(NodeInterface $node): bool
{
if ($node instanceof ItemInterface) {
return !! $node->getContent();
}
return false;
}
}
src/FeedIo/Rule/Atom/Link.php 0000644 00000002163 14037133544 0011711 0 ustar 00 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 00000002577 14037133544 0012530 0 ustar 00 setUrlAttributeName('href');
$this->ruleSet = new RuleSet(new Link('related'));
$this->ruleSet->add($mediaRule, ['media', 'enclosure']);
}
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);
}
}
protected function hasValue(NodeInterface $node) : bool
{
return true;
}
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 00000002251 14037133544 0011712 0 ustar 00 setLogo($element->nodeValue);
}
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
return $node instanceof FeedInterface && !! $node->getLogo();
}
/**
* @inheritDoc
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
if (!($node instanceof FeedInterface) || is_null($node->getLogo())) {
return;
}
$rootElement->appendChild($document->createElement($this->getNodeName(), $node->getLogo()));
}
}
src/FeedIo/Rule/Atom/Media.php 0000644 00000000753 14037133544 0012036 0 ustar 00 setAttribute('rel', 'enclosure');
return $element;
}
}
src/FeedIo/Rule/Atom/Summary.php 0000644 00000001764 14037133544 0012457 0 ustar 00 setSummary(
$this->getProcessedContent($element, $node)
);
}
}
protected function addElement(DomDocument $document, DOMElement $rootElement, NodeInterface $node) : void
{
if ($node instanceof ItemInterface) {
$rootElement->appendChild(
$this->generateElement($document, $node->getSummary())
);
}
}
protected function hasValue(NodeInterface $node): bool
{
if ($node instanceof ItemInterface) {
return !! $node->getSummary();
}
return false;
}
}
src/FeedIo/Rule/Author.php 0000644 00000002552 14037133544 0011360 0 ustar 00 newAuthor();
$author->setName($element->nodeValue);
$node->setAuthor($author);
}
}
/**
* Tells if the node contains the expected value
*
* @param NodeInterface $node
* @return bool
*/
protected function hasValue(NodeInterface $node) : bool
{
return $node instanceof ItemInterface && !! $node->getAuthor();
}
/**
* Creates and adds the element(s) to the docuement's rootElement
*
* @param \DomDocument $document
* @param \DOMElement $rootElement
* @param NodeInterface $node
*/
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
{
$rootElement->appendChild($document->createElement($this->getNodeName(), $node->getAuthor()->getName()));
}
}
src/FeedIo/Rule/Category.php 0000644 00000002625 14037133544 0011674 0 ustar 00 newCategory();
$category->setScheme($this->getAttributeValue($element, 'domain'))
->setLabel($element->nodeValue)
->setTerm($element->nodeValue);
$node->addCategory($category);
}
protected function hasValue(NodeInterface $node) : bool
{
return !! $node->getCategories();
}
protected function addElement(DomDocument $document, DOMElement $rootElement, NodeInterface $node) : void
{
foreach ($node->getCategories() as $category) {
$rootElement->appendChild($this->createCategoryElement($document, $category));
}
}
public function createCategoryElement(DomDocument $document, CategoryInterface $category) : DOMElement
{
$element = $document->createElement(
$this->getNodeName(),
is_null($category->getTerm()) ? $category->getLabel():$category->getTerm()
);
if (!! $category->getScheme()) {
$element->setAttribute('domain', $category->getScheme());
}
return $element;
}
}
src/FeedIo/Rule/Content.php 0000644 00000002066 14037133544 0011530 0 ustar 00 setContent($this->getProcessedContent($element, $node));
}
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
if ($node instanceof ItemInterface) {
return !! $node->getContent();
}
return false;
}
protected function addElement(DomDocument $document, DOMElement $rootElement, NodeInterface $node) : void
{
if ($node instanceof ItemInterface) {
$element = $this->generateElement($document, $node->getContent());
$rootElement->appendChild($element);
}
}
}
src/FeedIo/Rule/DateTimeBuilder.php 0000644 00000007115 14037133544 0013121 0 ustar 00 setTimezone(new DateTimeZone(date_default_timezone_get()));
}
public function addDateFormat(string $dateFormat) : DateTimeBuilderInterface
{
$this->dateFormats[] = $dateFormat;
return $this;
}
public function setDateFormats(array $dateFormats) : DateTimeBuilderInterface
{
$this->dateFormats = $dateFormats;
return $this;
}
public function getLastGuessedFormat() : string
{
return $this->lastGuessedFormat;
}
public function guessDateFormat(string $date) : ? string
{
foreach ($this->dateFormats as $format) {
$test = DateTime::createFromFormat($format, $date);
if ($test instanceof \DateTime) {
$this->lastGuessedFormat = $format;
return $format;
}
}
return null;
}
public function convertToDateTime(string $string) : DateTime
{
$string = trim($string);
foreach ([$this->getLastGuessedFormat(), $this->guessDateFormat($string) ] as $format) {
$date = $this->newDate((string) $format, $string);
if ($date instanceof \DateTime) {
$date->setTimezone($this->getTimezone());
return $date;
}
}
if ($this->logger) {
$this->logger->notice("unsupported date format : {$string}, returning current datetime");
}
$date = new DateTime(timezone: $this->getFeedTimezone());
$date->setTimezone($this->getTimezone());
return $date;
}
public function getFeedTimezone() : ?DateTimeZone
{
return $this->feedTimezone;
}
public function setFeedTimezone(DateTimeZone $timezone) : void
{
$this->feedTimezone = $timezone;
}
public function resetFeedTimezone() : void
{
$this->feedTimezone = null;
}
public function getServerTimezone() : ?DateTimeZone
{
return $this->serverTimezone;
}
public function setServerTimezone(DateTimeZone $timezone) : void
{
$this->serverTimezone = $timezone;
}
public function getTimezone() : ?DateTimeZone
{
return $this->getServerTimezone();
}
public function setTimezone(DateTimeZone $timezone) : void
{
$this->setServerTimezone($timezone);
}
protected function newDate(string $format, string $string): DateTime|bool
{
if (!! $this->getFeedTimezone()) {
return DateTime::createFromFormat($format, $string, $this->getFeedTimezone());
}
return DateTime::createFromFormat($format, $string);
}
}
src/FeedIo/Rule/DateTimeBuilderInterface.php 0000644 00000002603 14037133544 0014737 0 ustar 00 setDescription($this->getProcessedContent($element, $node));
}
}
/**
* @inheritDoc
*/
protected function hasValue(NodeInterface $node) : bool
{
if ($node instanceof FeedInterface) {
return !! $node->getDescription();
}
return false;
}
protected function addElement(DomDocument $document, DOMElement $rootElement, NodeInterface $node) : void
{
if ($node instanceof FeedInterface) {
$element = $this->generateElement($document, $node->getDescription());
$rootElement->appendChild($element);
}
}
}
src/FeedIo/Rule/Image.php 0000644 00000001442 14037133544 0011135 0 ustar 00 setUrl($element->textContent);
$node->addMedia($media);
}
}
protected function hasValue(NodeInterface $node): bool
{
return false;
}
protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node): void
{
throw new \RuntimeException("you should not try to write a Foo Bar
'), ]; $feed = new Feed(); $feed->setTitle('feed title'); $feed->setLogo(self::LOGO); $author = new Item\Author(); $author->setName('alex'); $feed->setAuthor($author); foreach ($items as $item) { $feed->add($item); } $formatter = new JsonFormatter(); $string = $formatter->toString($feed); $this->assertJson($string); $json = json_decode($string, true); $this->assertEquals('alex', $json['authors'][0]['name']); $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('authors', $item); $this->assertArrayHasKey('date_published', $item); } $this->assertEquals(['name' => 'foo bar'], $json['items'][0]['authors'][0]); } 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); return $item; } } tests/FeedIo/Parser/AtomTest.php 0000644 00000002505 14037133544 0012554 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 00000003604 14037133544 0013743 0 ustar 00 parse($this->getDocument(), $feed); $this->assertEquals('JSON Feed', $feed->getTitle()); $this->assertEquals('https://jsonfeed.org/graphics/icon.png', $feed->getLogo()); $this->assertEquals('Brent Simmons and Manton Reece', $feed->getAuthor()->getName()); $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->assertCount(1, $items[0]['medias']); $this->assertEquals('http://localhost/some-resource.jpg', $items[0]['medias'][0]['url']); $this->assertEquals('image/jpg', $items[0]['medias'][0]['type']); $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 14037133544 0013262 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 00000006643 14037133544 0014603 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->getContent()); $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 00000003033 14037133544 0012364 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->getContent()); $this->assertNotEmpty($item->getLastModified()); $this->assertNotEmpty($item->getLink()); } } } tests/FeedIo/Parser/RssTest.php 0000644 00000003464 14037133544 0012430 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 14037133544 0014272 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 00000010430 14037133544 0011650 0 ustar 00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FeedIo; 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 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", "attachments": [ { "url": "http://localhost/some-resource.jpg", "mime_type": "image/jpg" } ] }, { "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", "authors": [{ "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 00000002016 14037133545 0013315 0 ustar 00A paragraph
A paragraph
A paragraph
A paragraph