Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PSR-18 HTTP clients #777

Merged
merged 42 commits into from
Aug 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
20302b8
Create Psr7Response and Psr18Client
Art4 Jan 25, 2023
c023ea3
Psr18Client follows redirects
Art4 Jan 26, 2023
4ff0bc1
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 Jan 26, 2023
69cf5fb
Add tests with local files for FileClient
Art4 Jan 26, 2023
b5ae752
Add tests with local files for Psr18Client
Art4 Jan 26, 2023
188c154
Fix tests for PHP < 8.0
Art4 Jan 26, 2023
b0a0a0a
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 Jan 26, 2023
9dd5bf9
Add SimplePie::set_http_client(), add tests
Art4 Jan 27, 2023
9b5a099
Add integration test for loading a feed from cache
Art4 Jan 27, 2023
3bfd01c
Add integration tests for PSR-16 cache and PSR-18 client
Art4 Jan 27, 2023
b36e934
Add code examples in README.markdown for PSR-18 HTTP client and PSR-1…
Art4 Jan 27, 2023
b8b2373
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 Jan 30, 2023
b7f4650
Deprecate method SimplePie\SimplePie::set_file()
Art4 Jan 30, 2023
54a99bd
set SimplePie->$file to private, improve user notices
Art4 Jan 30, 2023
d6d2224
Fix phpdoc
Art4 Jan 30, 2023
74de990
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 Feb 13, 2023
27bee6e
Provide PSR implementations to Sanitize instead of internal HTTP Client
Art4 Feb 16, 2023
9059c41
Do not require internal Response interface in Sniffer
Art4 Feb 16, 2023
87f77e5
Accept internal Response implementations in Locator and Sniffer
Art4 Feb 16, 2023
803cc9a
Accept PSR implementations in Locator constructor, deprecate old argu…
Art4 Feb 16, 2023
136dab9
BC: Do not cache FileClient, or we wont be able to change the timeout
Art4 Feb 16, 2023
b9688df
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 Feb 16, 2023
16b440d
remove package phpdoc annotations
Art4 Feb 17, 2023
1a57dcd
Add Locator::set_http_client(), update CHANGELOG.md
Art4 Feb 17, 2023
69e6594
Restore BC in Locator::__construct()
Art4 Feb 17, 2023
374c7c7
Set new set_http_client() methods to final
Art4 Feb 17, 2023
3c3bf9a
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 Mar 20, 2023
c9a8f0e
Replace copyright comments with spdx notes
Art4 Mar 20, 2023
5361353
Merge branch 'split-file-into-client-and-response' into add-psr18-htt…
Art4 May 19, 2023
9660095
Fix tests
Art4 May 19, 2023
0c3fde1
fix code style
Art4 May 19, 2023
1ece520
Merge branch 'master' into add-psr18-http-client-support
Art4 May 23, 2023
67471c5
Merge branch 'master' into add-psr18-http-client-support
Art4 Jun 12, 2023
7554d6f
Fix parameter doc
Art4 Jun 12, 2023
a998d64
Merge branch 'master' into add-psr18-http-client-support
Art4 Aug 9, 2023
53d005b
Merge branch 'master' into add-psr18-http-client-support
Art4 Aug 25, 2023
3811a7d
run php-cs-fixer
Art4 Aug 25, 2023
33e27f8
Fix PHPStan errors
Art4 Aug 25, 2023
9a820ac
Psr7Response should always return full stream as body content
Art4 Aug 25, 2023
7201979
finetune tests
Art4 Aug 25, 2023
aceabdd
fix tests
Art4 Aug 25, 2023
c7330e6
ignore not fixable phpstan errors
Art4 Aug 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/simplepie/simplepie/compare/1.8.0...master)

### Added

- New method `SimplePie\SimplePie::set_http_client()` for providing PSR-18 HTTP client and PSR-17 factories by @Art4 in [#777](https://github.com/simplepie/simplepie/pull/777)
- New method `SimplePie\Locator::set_http_client()` for providing PSR-18 HTTP client and PSR-17 factories by @Art4 in [#777](https://github.com/simplepie/simplepie/pull/777)
- New method `SimplePie\Sanitize::set_http_client()` for providing PSR-18 HTTP client and PSR-17 factories by @Art4 in [#777](https://github.com/simplepie/simplepie/pull/777)

### Changed

- Use `idn_to_ascii` function instead of `idna_convert` library (requires `intl` extension or a [polyfill](https://github.com/symfony/polyfill-intl-idn)) by @jtojnar in [#785](https://github.com/simplepie/simplepie/pull/785)
Expand All @@ -17,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Deprecated

- The method `SimplePie\SimplePie::set_file()` is deprecated, use `SimplePie\SimplePie::set_http_client()` or `SimplePie\SimplePie::set_raw_data()` instead
- The method `SimplePie\Sanitize::pass_file_data()` is deprecated, use `SimplePie\Sanitize::set_http_client()` instead
- Passing multiple URLs to `SimplePie\SimplePie::set_feed_url()` is deprecated. You can create separate `SimplePie` instance per feed and then use `SimplePie::merge_items()` to get a single list of items. ([#795](https://github.com/simplepie/simplepie/pull/795))

Expand Down
31 changes: 31 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,37 @@ Requirements
* cURL or fsockopen()
* PCRE support

PSR-18: HTTP Client support
--------------

Since SimplePie 1.9.0 you can use a [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP client like [Guzzle](https://guzzlephp.org)
or [every other implementation](https://packagist.org/providers/psr/http-client-implementation).
Please note that you would also need [PSR-17](https://www.php-fig.org/psr/psr-17/) implementations of `RequestFactoryInterface` and an `UriFactoryInterface` implementation.

```php
$simplepie = new \SimplePie\SimplePie();
$simplepie->set_http_client(
new \GuzzleHttp\Client(),
new \GuzzleHttp\Psr7\HttpFactory(),
new \GuzzleHttp\Psr7\HttpFactory(),
);
```

PSR-16: Caching support
--------------

Since SimplePie 1.8.0 you can use the [PSR-16](https://www.php-fig.org/psr/psr-16/) cache from
[Symfony](https://symfony.com/doc/current/components/cache.html)
or [every other implementation](https://packagist.org/providers/psr/simple-cache-implementation).

```php
$simplepie = new \SimplePie\SimplePie();
$simplepie->set_cache(
new \Symfony\Component\Cache\Psr16Cache(
new \Symfony\Component\Cache\Adapter\FilesystemAdapter()
),
);
```

What comes in the package?
--------------------------
Expand Down
2 changes: 1 addition & 1 deletion build/compile.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function remove_header($contents)
$pos = strpos($contents, ';');
$namespace_name = substr($contents, 0, $pos);

$contents = $namespace_name . " {\n\n" . substr($contents, $pos+1) . "\n\n}";
$contents = $namespace_name . " {\n\n" . substr($contents, $pos + 1) . "\n\n}";
} else {
// use bracketed syntax for global namespace
$contents = "namespace {\n\n" . $contents . "\n\n}";
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"friendsofphp/php-cs-fixer": "^2.19 || ^3.8",
"mf2/mf2": "^0.5.0",
"phpstan/phpstan": "^1.10",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/simple-cache": "^1 || ^2 || ^3",
"yoast/phpunit-polyfills": "^1.0.1"
},
Expand Down
12 changes: 12 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ parameters:
- tests/

ignoreErrors:
# Ignore that only one const exists atm
-
message: "#^Strict comparison using \\!\\=\\= between 'GET' and 'GET' will always evaluate to false\\.$#"
count: 1
path: src/HTTP/Psr18Client.php

# SimplePie\Content\Type\Sniffer::__construct(): Parameter $file could be mixed due to BC.
-
message: '(Result of \|\| is always false\.)'
count: 1
path: src/Content/Type/Sniffer.php

# Not used since https://github.com/simplepie/simplepie/commit/b2eb0134d53921e75f0fa70b1cf901ed82b988b1 but cannot be removed due to BC.
- '(Constructor of class SimplePie\\Enclosure has an unused parameter \$javascript\.)'

Expand Down
8 changes: 4 additions & 4 deletions src/Cache/DB.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ protected static function prepare_simplepie_object_for_cache(\SimplePie\SimplePi
}

if (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) {
$channel =& $data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
$channel = & $data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
} elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) {
$channel =& $data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
$channel = & $data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
} elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) {
$channel =& $data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
$channel = & $data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
} elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0])) {
$channel =& $data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0];
$channel = & $data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0];
} else {
$channel = null;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Cache/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,13 +250,13 @@ public function load()

if ($items !== 0) {
if (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) {
$feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
$feed = & $data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
} elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) {
$feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
$feed = & $data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
} elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) {
$feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
$feed = & $data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
} elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0])) {
$feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0];
$feed = & $data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0];
} else {
$feed = null;
}
Expand Down
17 changes: 14 additions & 3 deletions src/Content/Type/Sniffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

namespace SimplePie\Content\Type;

use InvalidArgumentException;
use SimplePie\File;
use SimplePie\HTTP\Response;

/**
* Content-type sniffing
Expand All @@ -25,17 +27,26 @@ class Sniffer
/**
* File object
*
* @var \SimplePie\File
* @var File|Response
*/
public $file;

/**
* Create an instance of the class with the input file
*
* @param File $file Input file
* @param File|Response $file Input file
*/
public function __construct(File $file)
public function __construct(/* File */ $file)
Copy link
Contributor Author

@Art4 Art4 Feb 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the type hint is not a breaking change because we have not released 1.9.0 yet.

{
if (! is_object($file) || ! $file instanceof Response) {
// For BC we're asking for `File`, but internally we accept every `Response` implementation
throw new InvalidArgumentException(sprintf(
'%s(): Argument #1 ($file) must be of type %s',
__METHOD__,
File::class
), 1);
}

$this->file = $file;
}

Expand Down
12 changes: 6 additions & 6 deletions src/Enclosure.php
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ public function get_size()
{
$length = $this->get_length();
if ($length !== null) {
return round($length/1048576, 2);
return round($length / 1048576, 2);
}

return null;
Expand Down Expand Up @@ -809,7 +809,7 @@ public function get_width()
* @param array<string, mixed>|string $options See first parameter to {@see embed}
* @return string HTML string to output
*/
public function native_embed($options='')
public function native_embed($options = '')
{
return $this->embed($options, true);
}
Expand Down Expand Up @@ -940,9 +940,9 @@ public function embed($options = '', bool $native = false)
if ($height === 'auto') {
$width = 480;
} elseif ($widescreen) {
$width = round((intval($height)/9)*16);
$width = round((intval($height) / 9) * 16);
} else {
$width = round((intval($height)/3)*4);
$width = round((intval($height) / 3) * 4);
}
} else {
$width = '100%';
Expand All @@ -960,9 +960,9 @@ public function embed($options = '', bool $native = false)
$height = 360;
}
} elseif ($widescreen) {
$height = round((intval($width)/16)*9);
$height = round((intval($width) / 16) * 9);
} else {
$height = round((intval($width)/4)*3);
$height = round((intval($width) / 4) * 3);
}
} else {
$height = 376;
Expand Down
36 changes: 34 additions & 2 deletions src/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class File implements Response
public $url;

/**
* @var string User agent to use in requests
* @var ?string User agent to use in requests
* @deprecated Set the user agent in constructor.
*/
public $useragent;
Expand Down Expand Up @@ -266,8 +266,10 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,
} else {
$this->method = \SimplePie\SimplePie::FILE_SOURCE_LOCAL | \SimplePie\SimplePie::FILE_SOURCE_FILE_GET_CONTENTS;
if (empty($url) || !($this->body = trim(file_get_contents($url)))) {
$this->error = 'file_get_contents could not read the file';
$this->error = 'file_get_contents() could not read the file';
$this->success = false;
} else {
$this->status_code = 200;
}
}
}
Expand Down Expand Up @@ -429,6 +431,36 @@ private function flatten_headers(array $headers): array
return implode(',', $values);
}, $headers);
}

/**
* Create a File instance from another Response
*
* For BC reasons in some places there MUST be a `File` instance
* instead of a `Response` implementation
*
* @see Locator::__construct()
* @internal
*/
final public static function fromResponse(Response $response): self
{
$headers = [];

foreach ($response->get_headers() as $name => $header) {
$headers[$name] = implode(', ', $header);
}

/** @var File */
$file = (new \ReflectionClass(File::class))->newInstanceWithoutConstructor();

$file->url = $response->get_final_requested_uri();
$file->useragent = null;
$file->headers = $headers;
$file->body = $response->get_body_content();
$file->status_code = $response->get_status_code();
$file->permanent_url = $response->get_permanent_uri();

return $file;
}
}

class_alias('SimplePie\File', 'SimplePie_File');