Version v0.13.0-dev

This commit is contained in:
github-actions[bot] 2025-02-04 16:04:02 +00:00
parent 04c233f4ad
commit 4b0f3dc9ed
1425 changed files with 12711 additions and 9059 deletions

View File

@ -1 +1 @@
v0.12.1
v0.13.0-dev

671
composer.lock generated

File diff suppressed because it is too large Load Diff

20
vendor/autoload.php vendored
View File

@ -2,24 +2,6 @@
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit412d4c2afc15769a0fac36e82d8e412c::getLoader();
return ComposerAutoloaderInit6063f1b04a2dad456eda2cdae82a650e::getLoader();

View File

@ -1,5 +1,19 @@
# Changelog
## 1.9.0 (2024-04-10)
* Feature / Fix: Forward compatibility with Promise v3.
(#56 by @SimonFrings)
* Feature: Full PHP 8.3 compatibility.
(#54 by @yadaiio)
* Minor documentation improvements.
(#53 and #55 by @yadaiio)
* Update test suite to use new reactphp/async package instead of clue/reactphp-block.
(#50 and #57 by @dinooo13 and @SimonFrings)
## 1.8.0 (2022-09-01)
* Feature: Full support for PHP 8.1 and PHP 8.2.
@ -120,7 +134,7 @@
```php
$promise = $proxy->connect('imap.example.com:143');
$promise->then(null, function (Exeption $e) {
$promise->then(null, function (Exception $e) {
if ($e->getCode() === SOCKET_EACCES) {
echo 'Failed to authenticate with proxy!';
}

View File

@ -170,6 +170,8 @@ $proxy->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socket\Co
$connection->on('data', function ($chunk) use ($connection) {
echo $chunk;
});
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});
```
@ -189,6 +191,8 @@ $connector->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socke
$connection->on('data', function ($chunk) use ($connection) {
echo $chunk;
});
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});
```
@ -215,6 +219,8 @@ $connector->connect('tls://smtp.googlemail.com:465')->then(function (React\Socke
$connection->on('data', function ($chunk) use ($connection) {
echo $chunk;
});
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});
```
@ -247,7 +253,7 @@ $browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseI
```
See also [ReactPHP's HTTP client](https://github.com/reactphp/http#client-usage)
and any of the [examples](examples) for more details.
and any of the [examples](examples/) for more details.
#### Connection timeout
@ -275,6 +281,8 @@ $connector = new React\Socket\Connector(array(
$connector->connect('tcp://google.com:80')->then(function ($connection) {
// connection succeeded within 3.0 seconds
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});
```
@ -412,6 +420,8 @@ $proxy = new Clue\React\HttpProxy\ProxyConnector('http+unix:///tmp/proxy.sock');
$proxy->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
// connected…
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});
```
@ -441,7 +451,7 @@ This project follows [SemVer](https://semver.org/).
This will install the latest supported version:
```bash
composer require clue/http-proxy-react:^1.8
composer require clue/http-proxy-react:^1.9
```
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.

View File

@ -17,15 +17,20 @@
"ringcentral/psr7": "^1.2"
},
"require-dev": {
"clue/block-react": "^1.5",
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/async": "^4 || ^3 || ^2",
"react/event-loop": "^1.2",
"react/http": "^1.5"
"react/http": "^1.5",
"react/promise-timer": "^1.10"
},
"autoload": {
"psr-4": { "Clue\\React\\HttpProxy\\": "src/" }
"psr-4": {
"Clue\\React\\HttpProxy\\": "src/"
}
},
"autoload-dev": {
"psr-4": { "Clue\\Tests\\React\\HttpProxy\\": "tests/" }
"psr-4": {
"Clue\\Tests\\React\\HttpProxy\\": "tests/"
}
}
}

View File

@ -159,6 +159,8 @@ class ProxyConnector implements ConnectorInterface
// either close active connection or cancel pending connection attempt
$connecting->then(function (ConnectionInterface $stream) {
$stream->close();
}, function () {
// ignore to avoid reporting unhandled rejection
});
$connecting->cancel();
});

View File

@ -0,0 +1,2 @@
github: clue
custom: https://clue.engineering/support

View File

@ -1,2 +0,0 @@
/vendor
/composer.lock

View File

@ -1,8 +0,0 @@
language: php
php:
- 5.4
- 5.3
before_script:
- composer install --dev --prefer-source --no-interaction
script:
- phpunit --coverage-text

View File

@ -1,5 +1,18 @@
# Changelog
## 0.3.2 (2024-08-07)
* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable types.
(#19 by @clue)
* Update project structure, homepage and examples.
Add `.gitattributes` to exclude dev files from exports.
(#16, #20, #21 and #22 by @clue)
* Update test suite to use GitHub actions for continuous integration (CI),
run tests on all PHP versions up to PHP 8.3 and ensure 100% code coverage.
(#15 by @SimonFrings and #17 and #18 by @clue)
## 0.3.1 (2017-06-06)
* Fix: Fix server-side parsing of legacy inline protocol when multiple requests are processed at once
@ -9,19 +22,25 @@
* Feature: Add dedicated and faster `RequestParser` that also support the old
inline request protocol.
* Feature: Message serialization can now be handled directly by the Serializer
again without having to construct the appropriate model first.
* BC break: The `Factory` now has two distinct methods to create parsers:
* `createResponseParser()` for a client-side library
* `createRequestParser()` for a server-side library / testing framework
* BC break: Simplified parser API, now `pushIncoming()` returns an array of all
parsed message models.
* BC break: The signature for getting a serialized message from a model was
changed and now requires a Serializer passed:
```php
ModelInterface::getMessageSerialized($serializer)
```
* Many, many performance improvements
ModelInterface::getMessageSerialized($serializer)
```
* Many, many performance improvements
## 0.2.0 (2014-01-21)

21
vendor/clue/redis-protocol/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Christian Lück
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.

View File

@ -1,20 +1,25 @@
# clue/redis-protocol [![Build Status](https://travis-ci.org/clue/php-redis-protocol.png?branch=master)](https://travis-ci.org/clue/php-redis-protocol)
# clue/redis-protocol
A streaming redis protocol parser and serializer written in PHP
[![CI status](https://github.com/clue/redis-protocol/actions/workflows/ci.yml/badge.svg)](https://github.com/clue/redis-protocol/actions)
[![code coverage](https://img.shields.io/badge/code%20coverage-100%25-success)](#tests)
[![installs on Packagist](https://img.shields.io/packagist/dt/clue/redis-protocol?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/clue/redis-protocol)
This parser and serializer implementation allows you to parse redis protocol
A streaming Redis protocol (RESP) parser and serializer written in pure PHP.
This parser and serializer implementation allows you to parse Redis protocol
messages into native PHP values and vice-versa. This is usually needed by a
redis client implementation which also handles the connection socket.
Redis client implementation which also handles the connection socket.
To re-iterate: This is *not* a redis client implementation. This is a protocol
implementation that is usually used by a redis client implementation. If you're
To re-iterate: This is *not* a Redis client implementation. This is a protocol
implementation that is usually used by a Redis client implementation. If you're
looking for an easy way to build your own client implementation, then this is
for you. If you merely want to connect to a redis server and issue some
for you. If you merely want to connect to a Redis server and issue some
commands, you're probably better off using one of the existing client
implementations.
**Table of contents**
* [Support us](#support-us)
* [Quickstart example](#quickstart-example)
* [Usage](#usage)
* [Factory](#factory)
@ -22,14 +27,27 @@ implementations.
* [Model](#model)
* [Serializer](#serializer)
* [Install](#install)
* [Tests](#tests)
* [License](#license)
## Support us
We invest a lot of time developing, maintaining and updating our awesome
open-source projects. You can help us sustain this high-quality of our work by
[becoming a sponsor on GitHub](https://github.com/sponsors/clue). Sponsors get
numerous benefits in return, see our [sponsoring page](https://github.com/sponsors/clue)
for details.
Let's take these projects to the next level together! 🚀
## Quickstart example
```php
use Clue\Redis\Protocol;
<?php
$factory = new Protocol\Factory();
require __DIR__ . '/vendor/autoload.php';
$factory = new Clue\Redis\Protocol\Factory();
$parser = $factory->createResponseParser();
$serializer = $factory->createSerializer();
@ -47,6 +65,8 @@ var_dump($reply1->getValueNative()); // string(2) "OK"
var_dump($reply2->getValueNative()); // string(5) "value"
```
See also the [examples](examples/).
## Usage
### Factory
@ -59,15 +79,15 @@ your use-case).
### Parser
The library includes a streaming redis protocol parser. As such, it can safely
parse redis protocol messages and work with an incomplete data stream. For this,
The library includes a streaming Redis protocol parser. As such, it can safely
parse Redis protocol messages and work with an incomplete data stream. For this,
each included parser implements a single method
`ParserInterface::pushIncoming($chunk)`.
* The `ResponseParser` is what most redis client implementation would want to
use in order to parse incoming response messages from a redis server instance.
* The `RequestParser` can be used to test messages coming from a redis client or
even to implement a redis server.
* The `ResponseParser` is what most Redis client implementation would want to
use in order to parse incoming response messages from a Redis server instance.
* The `RequestParser` can be used to test messages coming from a Redis client or
even to implement a Redis server.
* The `MessageBuffer` decorates either of the available parsers and merely
offers some helper methods in order to work with single messages:
* `hasIncomingModel()` to check if there's a complete message in the pipeline
@ -118,17 +138,44 @@ assert($model implement Model\MultiBulkReply);
## Install
It's very unlikely you'll want to use this protocol parser standalone.
It should be added as a dependency to your redis client implementation instead.
The recommended way to install this library is [through Composer](https://getcomposer.org).
It should be added as a dependency to your Redis client implementation instead.
The recommended way to install this library is [through Composer](https://getcomposer.org/).
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
This will install the latest supported version:
```bash
$ composer require clue/redis-protocol:^0.3.1
composer require clue/redis-protocol:^0.3.2
```
More details and upgrade guides can be found in the [CHANGELOG](CHANGELOG.md).
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
This project aims to run on any platform and thus does not require any PHP
extensions and supports running on legacy PHP 5.3 through current PHP 8+.
It's *highly recommended to use the latest supported PHP version* for this project.
## Tests
To run the test suite, you first need to clone this repo and then install all
dependencies [through Composer](https://getcomposer.org/):
```bash
composer install
```
To run the test suite, go to the project root and run:
```bash
vendor/bin/phpunit
```
The test suite is set up to always ensure 100% code coverage across all
supported environments. If you have the Xdebug extension installed, you can also
generate a code coverage report locally like this:
```bash
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text
```
## License
@ -136,4 +183,7 @@ Its parser and serializer originally used to be based on
[jpd/redisent](https://github.com/jdp/redisent), which is released under the ISC
license, copyright (c) 2009-2012 Justin Poliey <justin@getglue.com>.
Other than that, this library is MIT licensed.
Other than that, this project is released under the permissive [MIT license](LICENSE).
> Did you know that I offer custom development services and issuing invoices for
sponsorships of releases and for contributions? Contact me (@clue) for details.

View File

@ -1,8 +1,8 @@
{
"name": "clue/redis-protocol",
"description": "A streaming redis wire protocol parser and serializer implementation in PHP",
"keywords": ["streaming", "redis", "protocol", "parser", "serializer"],
"homepage": "https://github.com/clue/php-redis-protocol",
"description": "A streaming Redis protocol (RESP) parser and serializer written in pure PHP.",
"keywords": ["streaming", "redis", "protocol", "resp", "parser", "serializer"],
"homepage": "https://github.com/clue/redis-protocol",
"license": "MIT",
"authors": [
{
@ -13,7 +13,17 @@
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
},
"autoload": {
"psr-0": { "Clue\\Redis\\Protocol": "src" }
"psr-4": {
"Clue\\Redis\\Protocol\\": "src/"
}
} ,
"autoload-dev": {
"psr-4": {
"Clue\\Tests\\Redis\\Protocol\\": "tests/"
}
}
}

View File

@ -1,22 +0,0 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
use Clue\Redis\Protocol;
$factory = new Protocol\Factory();
$parser = $factory->createResponseParser();
$serializer = $factory->createSerializer();
$fp = fsockopen('tcp://localhost', 6379);
fwrite($fp, $serializer->getRequestMessage('SET', array('name', 'value')));
fwrite($fp, $serializer->getRequestMessage('GET', array('name')));
// the commands are pipelined, so this may parse multiple responses
$models = $parser->pushIncoming(fread($fp, 4096));
$reply1 = array_shift($models);
$reply2 = array_shift($models);
var_dump($reply1->getValueNative()); // string(2) "OK"
var_dump($reply2->getValueNative()); // string(5) "value"

View File

@ -1,31 +0,0 @@
<?php
use Clue\Redis\Protocol\ProtocolBuffer;
use Clue\Redis\Protocol\Factory;
require __DIR__ . '/../vendor/autoload.php';
$factory = new Factory();
$parser = $factory->createResponseParser();
$serializer = $factory->createSerializer();
$n = isset($argv[1]) ? (int)$argv[1] : 10000; // number of dummy messages to parse
$cs = 4096; // pretend we can only read 7 bytes at once. more like 4096/8192 usually
echo 'benchmarking ' . $n . ' messages (chunksize of ' . $cs .' bytes)' . PHP_EOL;
$time = microtime(true);
$stream = '';
for ($i = 0; $i < $n; ++$i) {
$stream .= $serializer->getRequestMessage('set', array('var' . $i, 'value' . $i));
}
echo round(microtime(true) - $time, 3) . 's for serialization' . PHP_EOL;
$time = microtime(true);
for ($i = 0, $l = strlen($stream); $i < $l; $i += $cs) {
$parser->pushIncoming(substr($stream, $i, $cs));
}
echo round(microtime(true) - $time, 3) . 's for parsing' . PHP_EOL;

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<testsuites>
<testsuite name="Redis Protocol Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src/</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -2,7 +2,6 @@
namespace Clue\Redis\Protocol\Model;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Serializer\SerializerInterface;
class BulkReply implements ModelInterface

View File

@ -2,8 +2,8 @@
namespace Clue\Redis\Protocol\Model;
use Exception;
use Clue\Redis\Protocol\Serializer\SerializerInterface;
use Exception;
/**
*
@ -14,7 +14,7 @@ class ErrorReply extends Exception implements ModelInterface
/**
* create error status reply (single line error message)
*
* @param string|ErrorReplyException $message
* @param string $message
* @return string
*/
public function __construct($message, $code = 0, $previous = null)

View File

@ -2,7 +2,6 @@
namespace Clue\Redis\Protocol\Model;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Serializer\SerializerInterface;
class IntegerReply implements ModelInterface

View File

@ -2,9 +2,9 @@
namespace Clue\Redis\Protocol\Model;
use Clue\Redis\Protocol\Serializer\SerializerInterface;
use InvalidArgumentException;
use UnexpectedValueException;
use Clue\Redis\Protocol\Serializer\SerializerInterface;
class MultiBulkReply implements ModelInterface
{
@ -19,8 +19,11 @@ class MultiBulkReply implements ModelInterface
* @param array|null $data
* @throws InvalidArgumentException
*/
public function __construct(array $data = null)
public function __construct($data = null)
{
if ($data !== null && !is_array($data)) { // manual type check to support legacy PHP < 7.1
throw new InvalidArgumentException('Argument #1 ($data) expected array|null');
}
$this->data = $data;
}

View File

@ -2,9 +2,6 @@
namespace Clue\Redis\Protocol\Model;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Model\BulkReply;
use Clue\Redis\Protocol\Model\MultiBulkReply;
use Clue\Redis\Protocol\Serializer\SerializerInterface;
class Request implements ModelInterface

View File

@ -14,7 +14,7 @@ class StatusReply implements ModelInterface
/**
* create status reply (single line message)
*
* @param string|Status $message
* @param string $message
* @return string
*/
public function __construct($message)

View File

@ -2,9 +2,6 @@
namespace Clue\Redis\Protocol\Parser;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Parser\ParserException;
interface ParserInterface
{
/**
@ -20,8 +17,8 @@ interface ParserInterface
* message model will be returned once the parser has sufficient data.
*
* @param string $dataChunk
* @return ModelInterface[] 0+ message models
* @throws ParserException if the message can not be parsed
* @return \Clue\Redis\Protocol\Model\ModelInterface[] 0+ message models
* @throws \Clue\Redis\Protocol\Parser\ParserException if the message can not be parsed
* @see self::popIncomingModel()
*/
public function pushIncoming($dataChunk);

View File

@ -2,7 +2,6 @@
namespace Clue\Redis\Protocol\Parser;
use Clue\Redis\Protocol\Parser\ParserException;
use Clue\Redis\Protocol\Model\Request;
class RequestParser implements ParserInterface

View File

@ -2,14 +2,12 @@
namespace Clue\Redis\Protocol\Parser;
use Clue\Redis\Protocol\Parser\ParserInterface;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Model\BulkReply;
use Clue\Redis\Protocol\Model\ErrorReply;
use Clue\Redis\Protocol\Model\IntegerReply;
use Clue\Redis\Protocol\Model\MultiBulkReply;
use Clue\Redis\Protocol\Model\StatusReply;
use Clue\Redis\Protocol\Parser\ParserException;
/**
* Simple recursive redis wire protocol parser
@ -143,7 +141,6 @@ class ResponseParser implements ParserInterface
break;
default:
throw new ParserException('Invalid message can not be parsed: "' . $reply . '"');
break;
}
/* Party on */
return $response;

View File

@ -2,14 +2,13 @@
namespace Clue\Redis\Protocol\Serializer;
use Clue\Redis\Protocol\Model\StatusReply;
use InvalidArgumentException;
use Exception;
use Clue\Redis\Protocol\Model\BulkReply;
use Clue\Redis\Protocol\Model\IntegerReply;
use Clue\Redis\Protocol\Model\ErrorReply;
use Clue\Redis\Protocol\Model\MultiBulkReply;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Model\MultiBulkReply;
use Clue\Redis\Protocol\Model\Request;
class RecursiveSerializer implements SerializerInterface

View File

@ -2,7 +2,6 @@
namespace Clue\Redis\Protocol\Serializer;
use Clue\Redis\Protocol\Model\ErrorReplyException;
use Clue\Redis\Protocol\Model\ModelInterface;
use Clue\Redis\Protocol\Model\MultiBulkReply;

View File

@ -1,34 +0,0 @@
<?php
use Clue\Redis\Protocol\Factory;
class FactoryTest extends TestCase
{
private $factory;
public function setUp()
{
$this->factory = new Factory();
}
public function testCreateResponseParser()
{
$parser = $this->factory->createResponseParser();
$this->assertInstanceOf('Clue\Redis\Protocol\Parser\ParserInterface', $parser);
}
public function testCreateRequestParser()
{
$parser = $this->factory->createRequestParser();
$this->assertInstanceOf('Clue\Redis\Protocol\Parser\ParserInterface', $parser);
}
public function testCreateSerializer()
{
$serializer = $this->factory->createSerializer();
$this->assertInstanceOf('Clue\Redis\Protocol\Serializer\SerializerInterface', $serializer);
}
}

View File

@ -1,22 +0,0 @@
<?php
use Clue\Redis\Protocol\Serializer\RecursiveSerializer;
abstract class AbstractModelTest extends TestCase
{
protected $serializer;
abstract protected function createModel($value);
public function setUp()
{
$this->serializer = new RecursiveSerializer();
}
public function testConstructor()
{
$model = $this->createModel(null);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\ModelInterface', $model);
}
}

View File

@ -1,43 +0,0 @@
<?php
use Clue\Redis\Protocol\Model\BulkReply;
class BulkReplyTest extends AbstractModelTest
{
protected function createModel($value)
{
return new BulkReply($value);
}
public function testStringReply()
{
$model = $this->createModel('test');
$this->assertEquals('test', $model->getValueNative());
$this->assertEquals("$4\r\ntest\r\n", $model->getMessageSerialized($this->serializer));
}
public function testEmptyStringReply()
{
$model = $this->createModel('');
$this->assertEquals('', $model->getValueNative());
$this->assertEquals("$0\r\n\r\n", $model->getMessageSerialized($this->serializer));
}
public function testIntegerCast()
{
$model = $this->createModel(123);
$this->assertEquals('123', $model->getValueNative());
$this->assertEquals("$3\r\n123\r\n", $model->getMessageSerialized($this->serializer));
}
public function testNullBulkReply()
{
$model = $this->createModel(null);
$this->assertEquals(null, $model->getValueNative());
$this->assertEquals("$-1\r\n", $model->getMessageSerialized($this->serializer));
}
}

View File

@ -1,19 +0,0 @@
<?php
use Clue\Redis\Protocol\Model\ErrorReply;
class ErrorReplyTest extends AbstractModelTest
{
protected function createModel($value)
{
return new ErrorReply($value);
}
public function testError()
{
$model = $this->createModel('ERR error');
$this->assertEquals('ERR error', $model->getValueNative());
$this->assertEquals("-ERR error\r\n", $model->getMessageSerialized($this->serializer));
}
}

View File

@ -1,40 +0,0 @@
<?php
use Clue\Redis\Protocol\Model\IntegerReply;
class IntegerReplyTest extends AbstractModelTest
{
protected function createModel($value)
{
return new IntegerReply($value);
}
public function testIntegerReply()
{
$model = $this->createModel(0);
$this->assertEquals(0, $model->getValueNative());
$this->assertEquals(":0\r\n", $model->getMessageSerialized($this->serializer));
}
public function testFloatCasted()
{
$model = $this->createModel(-12.99);
$this->assertEquals(-12, $model->getValueNative());
$this->assertEquals(":-12\r\n", $model->getMessageSerialized($this->serializer));
$model = $this->createModel(14.99);
$this->assertEquals(14, $model->getValueNative());
$this->assertEquals(":14\r\n", $model->getMessageSerialized($this->serializer));
}
public function testBooleanCasted()
{
$model = $this->createModel(true);
$this->assertEquals(1, $model->getValueNative());
$this->assertEquals(":1\r\n", $model->getMessageSerialized($this->serializer));
$model = $this->createModel(false);
$this->assertEquals(0, $model->getValueNative());
$this->assertEquals(":0\r\n", $model->getMessageSerialized($this->serializer));
}
}

View File

@ -1,115 +0,0 @@
<?php
use Clue\Redis\Protocol\Model\MultiBulkReply;
use Clue\Redis\Protocol\Model\BulkReply;
use Clue\Redis\Protocol\Model\IntegerReply;
class MultiBulkReplyTest extends AbstractModelTest
{
protected function createModel($value)
{
return new MultiBulkReply($value);
}
public function testEmptyArray()
{
$model = $this->createModel(array());
$this->assertEquals(array(), $model->getValueNative());
$this->assertEquals("*0\r\n", $model->getMessageSerialized($this->serializer));
$this->assertFalse($model->isRequest());
}
public function testNullMultiBulkReply()
{
$model = $this->createModel(null);
$this->assertEquals(null, $model->getValueNative());
$this->assertEquals("*-1\r\n", $model->getMessageSerialized($this->serializer));
$this->assertFalse($model->isRequest());
return $model;
}
/**
* @param MultiBulkReply $model
* @depends testNullMultiBulkReply
* @expectedException UnexpectedValueException
*/
public function testNullMultiBulkReplyIsNotARequest(MultiBulkReply $model)
{
$model->getRequestModel();
}
public function testSingleBulkEnclosed()
{
$model = $this->createModel(array(new BulkReply('test')));
$this->assertEquals(array('test'), $model->getValueNative());
$this->assertEquals("*1\r\n$4\r\ntest\r\n", $model->getMessageSerialized($this->serializer));
$this->assertTrue($model->isRequest());
// this can be represented by a request
$request = $model->getRequestModel();
$this->assertEquals($model->getValueNative(), $request->getValueNative());
// representing the request as a reply should return our original instance
$reply = $request->getReplyModel();
$this->assertEquals($model, $reply);
return $model;
}
/**
* @depends testSingleBulkEnclosed
*/
public function testStringEnclosedEqualsSingleBulk(MultiBulkReply $expected)
{
$model = $this->createModel(array('test'));
$this->assertEquals($expected->getValueNative(), $model->getValueNative());
$this->assertEquals($expected->getMessageSerialized($this->serializer), $model->getMessageSerialized($this->serializer));
$this->assertTrue($model->isRequest());
}
public function testMixedReply()
{
$model = $this->createModel(array(new BulkReply('test'), new IntegerReply(123)));
$this->assertEquals(array('test', 123), $model->getValueNative());
$this->assertEquals("*2\r\n$4\r\ntest\r\n:123\r\n", $model->getMessageSerialized($this->serializer));
$this->assertFalse($model->isRequest());
return $model;
}
/**
* @param MultiBulkReply $model
* @depends testMixedReply
* @expectedException UnexpectedValueException
*/
public function testMixedReplyIsNotARequest(MultiBulkReply $model)
{
$model->getRequestModel();
}
public function testMultiStrings()
{
$model = $this->createModel(array('SET', 'a', 'b'));
$this->assertEquals(array('SET', 'a', 'b'), $model->getValueNative());
$this->assertTrue($model->isRequest());
$request = $model->getRequestModel();
// this can be represented by a request
$request = $model->getRequestModel();
$this->assertEquals($model->getValueNative(), $request->getValueNative());
}
}

View File

@ -1,37 +0,0 @@
<?php
use Clue\Redis\Protocol\Model\Request;
class RequestTest extends AbstractModelTest
{
protected function createModel($value)
{
return new Request('QUIT');
}
public function testPing()
{
$model = new Request('PING');
$this->assertEquals('PING', $model->getCommand());
$this->assertEquals(array(), $model->getArgs());
$this->assertEquals(array('PING'), $model->getValueNative());
$this->assertEquals("*1\r\n$4\r\nPING\r\n", $model->getMessageSerialized($this->serializer));
$reply = $model->getReplyModel();
$this->assertEquals($model->getValueNative(), $reply->getValueNative());
}
public function testGet()
{
$model = new Request('GET', array('a'));
$this->assertEquals('GET', $model->getCommand());
$this->assertEquals(array('a'), $model->getArgs());
$this->assertEquals(array('GET', 'a'), $model->getValueNative());
$this->assertEquals("*2\r\n$3\r\nGET\r\n$1\r\na\r\n", $model->getMessageSerialized($this->serializer));
$reply = $model->getReplyModel();
$this->assertEquals($model->getValueNative(), $reply->getValueNative());
}
}

View File

@ -1,19 +0,0 @@
<?php
use Clue\Redis\Protocol\Model\StatusReply;
class StatusReplyTest extends AbstractModelTest
{
protected function createModel($value)
{
return new StatusReply($value);
}
public function testStatusOk()
{
$model = $this->createModel('OK');
$this->assertEquals('OK', $model->getValueNative());
$this->assertEquals("+OK\r\n", $model->getMessageSerialized($this->serializer));
}
}

View File

@ -1,67 +0,0 @@
<?php
use Clue\Redis\Protocol\Parser\ParserInterface;
use Clue\Redis\Protocol\Parser\MessageBuffer;
abstract class AbstractParserTest extends TestCase
{
/**
*
* @var ParserInterface
*/
protected $parser;
abstract protected function createParser();
public function setUp()
{
$this->parser = $this->createParser();
$this->assertInstanceOf('Clue\Redis\Protocol\Parser\ParserInterface', $this->parser);
}
public function testParsingMessageOne()
{
// getRequestMessage('test')
$message = $expected = "*1\r\n$4\r\ntest\r\n";
$models = $this->parser->pushIncoming($message);
$this->assertCount(1, $models);
$model = reset($models);
$this->assertEquals(array('test'), $model->getValueNative());
}
public function testParsingMessageTwoPartial()
{
// getRequestMessage('test', array('second'))
$message = "*2\r\n$4\r\ntest\r\n$6\r\nsecond\r\n";
$this->assertEquals(array(), $this->parser->pushIncoming(substr($message, 0, 1)));
$this->assertEquals(array(), $this->parser->pushIncoming(substr($message, 1, 1)));
$this->assertEquals(array(), $this->parser->pushIncoming(substr($message, 2, 1)));
$this->assertEquals(array(), $this->parser->pushIncoming(substr($message, 3, 10)));
$this->assertCount(1, $models = $this->parser->pushIncoming(substr($message, 13)));
$model = reset($models);
$this->assertEquals(array('test', 'second'), $model->getValueNative());
}
public function testMessageBuffer()
{
$buffer = new MessageBuffer($this->parser);
$this->assertFalse($buffer->hasIncomingModel());
$data = "*1\r\n$4\r\ntest\r\n";
$this->assertCount(1, $models = $buffer->pushIncoming($data));
$this->assertTrue($buffer->hasIncomingModel());
$expected = reset($models);
$this->assertSame($expected, $buffer->popIncomingModel());
$this->assertFalse($buffer->hasIncomingModel());
$this->setExpectedException('UnderflowException');
$buffer->popIncomingModel();
}
}

View File

@ -1,132 +0,0 @@
<?php
use Clue\Redis\Protocol\Parser\RequestParser;
class RequestParserTest extends AbstractParserTest
{
protected function createParser()
{
return new RequestParser();
}
public function testSimplePingRequest()
{
$message = "*1\r\n$4\r\nping\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$request = reset($models);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\Request', $request);
$this->assertEquals('ping', $request->getCommand());
$this->assertEquals(array(), $request->getArgs());
return $request;
}
/**
*
* @param Request $expected
* @depends testSimplePingRequest
*/
public function testInlinePingRequest($expected)
{
$message = "ping\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$request = reset($models);
$this->assertEquals($expected, $request);
}
public function testInlineWhitespaceIsIgnored()
{
$message = " set name value \r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$request = reset($models);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\Request', $request);
$this->assertEquals('set', $request->getCommand());
$this->assertEquals(array('name', 'value'), $request->getArgs());
}
public function testIncompleteSuccessive()
{
$this->assertEquals(array(), $this->parser->pushIncoming("*1\r\n"));
$this->assertEquals(array(), $this->parser->pushIncoming("$4\r\n"));
$this->assertEquals(array(), $this->parser->pushIncoming("test"));
$this->assertCount(1, $models = $this->parser->pushIncoming("\r\n"));
}
public function testNullMultiBulkRequestIsIgnored()
{
$message = "*-1\r\n";
$this->assertEquals(array(), $this->parser->pushIncoming($message));
}
public function testEmptyMultiBulkRequestIsIgnored()
{
$message = "*0\r\n";
$this->assertEquals(array(), $this->parser->pushIncoming($message));
}
public function testEmptyInlineIsIgnored()
{
$message = "\r\n";
$this->assertEquals(array(), $this->parser->pushIncoming($message));
}
public function testInlineParsesMultipleRequestsAtOnce()
{
$message = "hello\r\n\world\r\ntest\r\n";
$this->assertCount(3, $models = $this->parser->pushIncoming($message));
}
public function testEmptyInlineAroundInlineIsIgnored()
{
$message = "\r\n\r\n" . "ping\r\n\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$request = reset($models);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\Request', $request);
$this->assertEquals('ping', $request->getCommand());
$this->assertEquals(array(), $request->getArgs());
}
public function testWhitespaceInlineIsIgnored()
{
$message = " \r\n";
$this->assertEquals(array(), $this->parser->pushIncoming($message));
}
/**
* @expectedException Clue\Redis\Protocol\Parser\ParserException
*/
public function testInvalidMultiBulkMustContainBulk()
{
$message = "*1\r\n:123\r\n";
$this->parser->pushIncoming($message);
}
/**
* @expectedException Clue\Redis\Protocol\Parser\ParserException
*/
public function testInvalidBulkLength()
{
$message = "*1\r\n$-1\r\n";
$this->parser->pushIncoming($message);
}
}

View File

@ -1,130 +0,0 @@
<?php
use Clue\Redis\Protocol\Parser\ResponseParser;
class RecursiveParserTest extends AbstractParserTest
{
protected function createParser()
{
return new ResponseParser();
}
public function testPartialIncompleteBulkReply()
{
$this->assertEquals(array(), $this->parser->pushIncoming("$20\r\nincompl"));
}
public function testParsingStatusReplies()
{
// C: PING
$message = "+PONG\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals('PONG', $data);
// C: SET key value
$message = "+OK\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals('OK', $data);
}
public function testParsingErrorReply()
{
$message = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$exception = reset($models);
$this->assertInstanceOf('Exception', $exception);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\ErrorReply', $exception);
$this->assertEquals('WRONGTYPE Operation against a key holding the wrong kind of value', $exception->getMessage());
}
public function testParsingIntegerReply()
{
// C: INCR mykey
$message = ":1\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals(1, $data);
}
public function testParsingBulkReply()
{
// C: GET mykey
$message = "$6\r\nfoobar\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals("foobar", $data);
}
public function testParsingNullBulkReply()
{
// C: GET nonexistingkey
$message = "$-1\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals(null, $data);
}
public function testParsingEmptyMultiBulkReply()
{
// C: LRANGE nokey 0 1
$message = "*0\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals(array(), $data);
}
public function testParsingNullMultiBulkReply()
{
// C: BLPOP key 1
$message = "*-1\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals(null, $data);
}
public function testParsingMultiBulkReplyWithMixedElements()
{
$message = "*5\r\n:1\r\n:2\r\n:3\r\n:4\r\n$6\r\nfoobar\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals(array(1, 2, 3, 4, 'foobar'), $data);
}
public function testParsingMultiBulkReplyWithIncompletePush()
{
$this->assertCount(0, $this->parser->pushIncoming("*5\r\n:1\r\n:2\r"));
$this->assertCount(1, $models = $this->parser->pushIncoming("\n:3\r\n:4\r\n$6\r\nfoobar\r\n"));
$data = reset($models)->getValueNative();
$this->assertEquals(array(1, 2, 3, 4, 'foobar'), $data);
}
public function testParsingMultiBulkReplyWithNullElement()
{
$message = "*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n";
$this->assertCount(1, $models = $this->parser->pushIncoming($message));
$data = reset($models)->getValueNative();
$this->assertEquals(array('foo', null, 'bar'), $data);
}
/**
* @expectedException Clue\Redis\Protocol\Parser\ParserException
*/
public function testParseError()
{
$this->parser->pushIncoming("invalid string\r\n");
}
}

View File

@ -1,141 +0,0 @@
<?php
use Clue\Redis\Protocol\Serializer\SerializerInterface;
use Clue\Redis\Protocol\Model\Status;
use Clue\Redis\Protocol\Model\ErrorReplyException;
//use Exception;
abstract class AbstractSerializerTest extends TestCase
{
/**
* @return SerializerInterface
*/
abstract protected function createSerializer();
public function setUp()
{
$this->serializer = $this->createSerializer();
}
public function testIntegerReply()
{
$model = $this->serializer->createReplyModel(0);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\IntegerReply', $model);
$this->assertEquals(0, $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(0));
}
public function testFloatCastIntegerReply()
{
$model = $this->serializer->createReplyModel(-12.99);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\IntegerReply', $model);
$this->assertEquals(-12, $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(-12.99));
$model = $this->serializer->createReplyModel(14.99);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\IntegerReply', $model);
$this->assertEquals(14, $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(14.99));
}
public function testBooleanCastIntegerReply()
{
$model = $this->serializer->createReplyModel(true);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\IntegerReply', $model);
$this->assertEquals(1, $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(true));
$model = $this->serializer->createReplyModel(false);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\IntegerReply', $model);
$this->assertEquals(0, $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(false));
}
public function testStringReply()
{
$model = $this->serializer->createReplyModel('test');
$this->assertInstanceOf('Clue\Redis\Protocol\Model\BulkReply', $model);
$this->assertEquals('test', $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage('test'));
}
public function testNullCastNullBulkReply()
{
$model = $this->serializer->createReplyModel(null);
$this->assertInstanceOf('Clue\Redis\Protocol\Model\BulkReply', $model);
$this->assertEquals(null, $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(null));
}
public function testEmptyArrayMultiBulkReply()
{
$model = $this->serializer->createReplyModel(array());
$this->assertInstanceOf('Clue\Redis\Protocol\Model\MultiBulkReply', $model);
$this->assertEquals(array(), $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(array()));
}
public function testArrayMultiBulkReply()
{
$model = $this->serializer->createReplyModel(array('test', 123));
$this->assertInstanceOf('Clue\Redis\Protocol\Model\MultiBulkReply', $model);
$this->assertEquals(array('test', 123), $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(array('test', 123)));
}
public function testErrorReply()
{
$model = $this->serializer->createReplyModel(new Exception('ERR failure'));
$this->assertInstanceOf('Clue\Redis\Protocol\Model\ErrorReply', $model);
$this->assertEquals('ERR failure', $model->getValueNative());
$this->assertEquals($model->getMessageSerialized($this->serializer), $this->serializer->getReplyMessage(new Exception('ERR failure')));
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidArgument()
{
$this->serializer->createReplyModel((object)array());
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidReplyData()
{
$this->serializer->getReplyMessage((object)array());
}
/**
*
* @param array $data
* @dataProvider provideRequestMessage
*/
public function testRequestMessage($command, $args)
{
// the model is already unit-tested, so just compare against its message
$model = $this->serializer->createRequestModel($command, $args);
$message = $this->serializer->getRequestMessage($command, $args);
$this->assertEquals($model->getMessageSerialized($this->serializer), $message);
}
public function provideRequestMessage()
{
return array(
array('PING', array()),
array('GET', array('a')),
array('SET', array('a', 'b')),
array('SET', array('empty', ''))
);
}
// public function testBenchCreateRequest()
// {
// for ($i = 0; $i < 100000; ++$i) {
// $this->serializer->createReplyModel(array('a', 'b', 'c'));
// }
// }
}

View File

@ -1,11 +0,0 @@
<?php
use Clue\Redis\Protocol\Serializer\RecursiveSerializer;
class RecursiveSerializerTest extends AbstractSerializerTest
{
protected function createSerializer()
{
return new RecursiveSerializer();
}
}

View File

@ -1,7 +0,0 @@
<?php
(include_once __DIR__ . '/../vendor/autoload.php') OR die(PHP_EOL . 'ERROR: composer autoloader not found, run "composer install" or see README for instructions' . PHP_EOL);
class TestCase extends PHPUnit_Framework_TestCase
{
}

View File

@ -1,5 +1,24 @@
# Changelog
## 2.8.0 (2025-01-03)
This is a compatibility release that contains backported features from the `3.x` branch.
Once v3 is released, it will be the way forward for this project.
* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable types.
(#165 and #170 by @clue)
## 2.7.0 (2024-01-05)
This is a compatibility release that contains backported features from the `3.x` branch.
Once v3 is released, it will be the way forward for this project.
* Feature: Forward compatibility with Promise v3.
(#152 by @clue)
* Feature: Full PHP 8.3 compatibility and update test suite.
(#151 by @clue)
## 2.6.0 (2022-05-09)
* Feature: Support PHP 8.1 release.

View File

@ -610,7 +610,7 @@ This project follows [SemVer](https://semver.org/).
This will install the latest supported version:
```bash
$ composer require clue/redis-react:^2.6
$ composer require clue/redis-react:^2.8
```
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.

View File

@ -12,21 +12,25 @@
],
"require": {
"php": ">=5.3",
"clue/redis-protocol": "0.3.*",
"clue/redis-protocol": "^0.3.2",
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"react/event-loop": "^1.2",
"react/promise": "^2.0 || ^1.1",
"react/promise-timer": "^1.8",
"react/socket": "^1.9"
"react/promise": "^3.2 || ^2.0 || ^1.1",
"react/promise-timer": "^1.11",
"react/socket": "^1.16"
},
"require-dev": {
"clue/block-react": "^1.1",
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
"clue/block-react": "^1.5",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
},
"autoload": {
"psr-4": { "Clue\\React\\Redis\\": "src/" }
"psr-4": {
"Clue\\React\\Redis\\": "src/"
}
},
"autoload-dev": {
"psr-4": { "Clue\\Tests\\React\\Redis\\": "tests/" }
"psr-4": {
"Clue\\Tests\\React\\Redis\\": "tests/"
}
}
}

View File

@ -25,10 +25,20 @@ class Factory
/**
* @param ?LoopInterface $loop
* @param ?ConnectorInterface $connector
* @param ?ProtocolFactory $protocol
* @param ?ProtocolFactory $protocol (internal, should not usually be passed)
*/
public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null, ProtocolFactory $protocol = null)
public function __construct($loop = null, $connector = null, $protocol = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}
if ($connector !== null && !$connector instanceof ConnectorInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($connector) expected null|React\Socket\ConnectorInterface');
}
if ($protocol !== null && !$protocol instanceof ProtocolFactory) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($protocol) expected null|Clue\Redis\Protocol\Factory');
}
$this->loop = $loop ?: Loop::get();
$this->connector = $connector ?: new Connector(array(), $this->loop);
$this->protocol = $protocol ?: new ProtocolFactory();
@ -84,6 +94,8 @@ class Factory
// either close successful connection or cancel pending connection attempt
$connecting->then(function (ConnectionInterface $connection) {
$connection->close();
}, function () {
// ignore to avoid reporting unhandled rejection
});
$connecting->cancel();
});

View File

@ -168,6 +168,8 @@ class LazyClient extends EventEmitter implements Client
if ($this->promise !== null) {
$this->promise->then(function (Client $redis) {
$redis->close();
}, function () {
// ignore to avoid reporting unhandled rejection
});
if ($this->promise !== null) {
$this->promise->cancel();

View File

@ -28,8 +28,17 @@ class StreamingClient extends EventEmitter implements Client
private $subscribed = 0;
private $psubscribed = 0;
public function __construct(DuplexStreamInterface $stream, ParserInterface $parser = null, SerializerInterface $serializer = null)
/**
* @param DuplexStreamInterface $stream
* @param ?ParserInterface $parser
* @param ?SerializerInterface $serializer
*/
public function __construct(DuplexStreamInterface $stream, $parser = null, $serializer = null)
{
// manual type checks to support legacy PHP < 7.1
assert($parser === null || $parser instanceof ParserInterface);
assert($serializer === null || $serializer instanceof SerializerInterface);
if ($parser === null || $serializer === null) {
$factory = new ProtocolFactory();
if ($parser === null) {

View File

@ -1,5 +1,16 @@
# Changelog
## 1.4.0 (2024-01-30)
* Feature: Full PHP 8.3 compatibility.
(#38 by @yadaiio and #36 by @clue)
* Improve documentation and examples.
(#37 by @yadaiio, #34 by @SimonFrings and #33 by @PaulRotmann)
* Improve test suite and use GitHub actions for continuous integration (CI).
(#32 by @SimonFrings)
## 1.3.0 (2020-11-06)
* Improve test suite and add `.gitattributes` to exclude dev files from export.
@ -8,7 +19,7 @@
## 1.2.0 (2018-07-09)
* Feature: Forward compatiblity with EventLoop v0.5 and upcoming v1.0.
* Feature: Forward compatibility with EventLoop v0.5 and upcoming v1.0.
(#28 by @clue)
* Improve test suite by updating Travis config to test against legacy PHP 5.3 through PHP 7.2.

View File

@ -1,4 +1,7 @@
# clue/reactphp-term [![Build Status](https://travis-ci.org/clue/reactphp-term.svg?branch=master)](https://travis-ci.org/clue/reactphp-term)
# clue/reactphp-term
[![CI status](https://github.com/clue/reactphp-term/actions/workflows/ci.yml/badge.svg)](https://github.com/clue/reactphp-term/actions)
[![installs on Packagist](https://img.shields.io/packagist/dt/clue/term-react?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/clue/term-react)
Streaming terminal emulator, built on top of [ReactPHP](https://reactphp.org/).
@ -33,7 +36,7 @@ It wraps a given `ReadableStreamInterface` and exposes its plain data through
the same interface.
```php
$stdin = new ReadableResourceStream(STDIN, $loop);
$stdin = new React\Stream\ReadableResourceStream(STDIN);
$stream = new ControlCodeParser($stdin);
@ -112,37 +115,36 @@ $stream->on('c0', function ($code) {
## Install
The recommended way to install this library is [through Composer](https://getcomposer.org).
The recommended way to install this library is [through Composer](https://getcomposer.org/).
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
This project follows [SemVer](https://semver.org/).
This will install the latest supported version:
```bash
$ composer require clue/term-react:^1.3
composer require clue/term-react:^1.4
```
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
This project aims to run on any platform and thus does not require any PHP
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
extensions and supports running on legacy PHP 5.3 through current PHP 8+ and
HHVM.
It's *highly recommended to use PHP 7+* for this project.
It's *highly recommended to use the latest supported PHP version* for this project.
## Tests
To run the test suite, you first need to clone this repo and then install all
dependencies [through Composer](https://getcomposer.org):
dependencies [through Composer](https://getcomposer.org/):
```bash
$ composer install
composer install
```
To run the test suite, go to the project root and run:
```bash
$ php vendor/bin/phpunit
vendor/bin/phpunit
```
## License

View File

@ -10,18 +10,22 @@
"email": "christian@clue.engineering"
}
],
"autoload": {
"psr-4": { "Clue\\React\\Term\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Clue\\Tests\\React\\Term\\": "tests/" }
},
"require": {
"php": ">=5.3",
"react/stream": "^1.0 || ^0.7"
"react/stream": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8",
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3"
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/event-loop": "^1.2"
},
"autoload": {
"psr-4": {
"Clue\\React\\Term\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Clue\\Tests\\React\\Term\\": "tests/"
}
}
}

View File

@ -31,7 +31,7 @@ class ControlCodeParser extends EventEmitter implements ReadableStreamInterface
* C1 types in 8 bit are currently not supported, as they require special
* care with regards to whether UTF-8 mode is enabled. So far this has
* turned out to be a non-issue because most terminal emulators *accept*
* boths formats, but usually *send* in 7 bit mode exclusively.
* both formats, but usually *send* in 7 bit mode exclusively.
*/
private $types = array(
'[' => 'csi',

View File

@ -1,5 +1,16 @@
# Changelog
## 1.3.0 (2023-12-06)
* Feature: Full PHP 8.3 compatibility.
(#29 by @yadaiio and #27 by @clue)
* Minor documentation improvements.
(#25 by @PaulRotmann, #26 by @SimonFrings and #28 by @yadaiio)
* Improve test suite, use GitHub actions for continuous integration (CI).
(#24 by @SimonFrings)
## 1.2.0 (2020-11-06)
* Improve test suite and add `.gitattributes` to exclude dev files from export.

View File

@ -1,4 +1,7 @@
# clue/reactphp-utf8 [![Build Status](https://travis-ci.org/clue/reactphp-utf8.svg?branch=master)](https://travis-ci.org/clue/reactphp-utf8)
# clue/reactphp-utf8
[![CI status](https://github.com/clue/reactphp-utf8/actions/workflows/ci.yml/badge.svg)](https://github.com/clue/reactphp-utf8/actions)
[![installs on Packagist](https://img.shields.io/packagist/dt/clue/utf8-react?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/clue/utf8-react)
Streaming UTF-8 parser, built on top of [ReactPHP](https://reactphp.org/).
@ -32,7 +35,11 @@ It wraps a given `ReadableStreamInterface` and exposes its data through the same
interface.
```php
$stdin = new ReadableResourceStream(STDIN, $loop);
<?php
require __DIR__ . '/vendor/autoload.php';
$stdin = new ReadableResourceStream(STDIN);
$stream = new Sequencer($stdin);
@ -49,7 +56,7 @@ This class reassembles these sequences by buffering incomplete ones.
Also, if you're merely consuming a stream and you're not in control of producing and
ensuring valid UTF-8 data, it may as well include invalid UTF-8 byte sequences.
This class replaces any invalid bytes in the sequence with a `?`.
This replacement character can be given as a second paramter to the constructor:
This replacement character can be given as a second parameter to the constructor:
```php
$stream = new Sequencer($stdin, 'X');
@ -64,36 +71,36 @@ This binary data will be left as-is, unless you filter this at a later stage.
## Install
The recommended way to install this library is [through Composer](https://getcomposer.org).
The recommended way to install this library is [through Composer](https://getcomposer.org/).
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
This project follows [SemVer](https://semver.org/).
This will install the latest supported version:
```bash
$ composer require clue/utf8-react:^1.2
composer require clue/utf8-react:^1.3
```
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
This project aims to run on any platform and thus does not require any PHP
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
extensions and supports running on legacy PHP 5.3 through current PHP 8+ and
HHVM.
It's *highly recommended to use PHP 7+* for this project.
It's *highly recommended to use the latest supported PHP version* for this project.
## Tests
To run the test suite, you first need to clone this repo and then install all
dependencies [through Composer](https://getcomposer.org):
dependencies [through Composer](https://getcomposer.org/):
```bash
$ composer install
composer install
```
To run the test suite, go to the project root and run:
```bash
$ php vendor/bin/phpunit
vendor/bin/phpunit
```
## License

View File

@ -10,18 +10,22 @@
"email": "christian@clue.engineering"
}
],
"autoload": {
"psr-4": { "Clue\\React\\Utf8\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Clue\\Tests\\React\\Utf8\\": "tests/" }
},
"require": {
"php": ">=5.3",
"react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3 ||^5.7 || ^4.8",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/stream": "^1.0 || ^0.7"
},
"autoload": {
"psr-4": {
"Clue\\React\\Utf8\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Clue\\Tests\\React\\Utf8\\": "tests/"
}
}
}

View File

@ -4,8 +4,8 @@ namespace Clue\React\Utf8;
use Evenement\EventEmitter;
use React\Stream\ReadableStreamInterface;
use React\Stream\WritableStreamInterface;
use React\Stream\Util;
use React\Stream\WritableStreamInterface;
/**
* forwards only complete UTF-8 sequences

View File

@ -42,37 +42,35 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
@ -80,7 +78,8 @@ class ClassLoader
private $useIncludePath = false;
/**
* @var array<string, string>
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
@ -88,29 +87,29 @@ class ClassLoader
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
/** @var ?string */
private $apcuPrefix;
/**
* @var array<string, self>
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
* @return string[]
*/
public function getPrefixes()
{
@ -122,7 +121,8 @@ class ClassLoader
}
/**
* @return array<string, list<string>>
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
@ -130,7 +130,8 @@ class ClassLoader
}
/**
* @return list<string>
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
@ -138,7 +139,8 @@ class ClassLoader
}
/**
* @return list<string>
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
@ -146,7 +148,8 @@ class ClassLoader
}
/**
* @return array<string, string> Array of classname => path
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
@ -154,7 +157,8 @@ class ClassLoader
}
/**
* @param array<string, string> $classMap Class to filename map
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
@ -171,25 +175,24 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
(array) $paths
);
}
@ -198,19 +201,19 @@ class ClassLoader
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
(array) $paths
);
}
}
@ -219,9 +222,9 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
@ -229,18 +232,17 @@ class ClassLoader
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@ -250,18 +252,18 @@ class ClassLoader
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
(array) $paths
);
}
}
@ -270,8 +272,8 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
@ -288,8 +290,8 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
@ -423,8 +425,7 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
includeFile($file);
return true;
}
@ -475,9 +476,9 @@ class ClassLoader
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return array<string, self>
* @return self[]
*/
public static function getRegisteredLoaders()
{
@ -554,26 +555,18 @@ class ClassLoader
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@ -21,14 +21,12 @@ use Composer\Semver\VersionParser;
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
@ -39,7 +37,7 @@ class InstalledVersions
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
@ -98,7 +96,7 @@ class InstalledVersions
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
@ -119,7 +117,7 @@ class InstalledVersions
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
@ -243,7 +241,7 @@ class InstalledVersions
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
@ -257,7 +255,7 @@ class InstalledVersions
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@ -280,7 +278,7 @@ class InstalledVersions
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
@ -303,7 +301,7 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
@ -313,7 +311,7 @@ class InstalledVersions
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
@ -322,6 +320,7 @@ class InstalledVersions
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
@ -330,9 +329,11 @@ class InstalledVersions
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $required;
$copiedLocalDir = true;
}
}
}
@ -350,7 +351,7 @@ class InstalledVersions
}
}
if (self::$installed !== array()) {
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}

View File

@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(

View File

@ -2,16 +2,16 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
'972fda704d680a3a53c68e34e193cb22' => $vendorDir . '/react/promise-timer/src/functions_include.php',
'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'45b89995831374eefdfc4161161938f6' => $vendorDir . '/jfcherng/php-color-output/src/helpers.php',
'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',

View File

@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
@ -11,6 +11,5 @@ return array(
'Less' => array($vendorDir . '/wikimedia/less.php/lib'),
'JShrink' => array($vendorDir . '/tedivm/jshrink/src'),
'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
'Clue\\Redis\\Protocol' => array($vendorDir . '/clue/redis-protocol/src'),
'AssetLoader' => array($baseDir . '/'),
);

View File

@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
@ -44,10 +44,11 @@ return array(
'Fig\\Http\\Message\\' => array($vendorDir . '/fig/http-message-util/src'),
'Evenement\\' => array($vendorDir . '/evenement/evenement/src'),
'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'),
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/src'),
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections'),
'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
'ConnectionManager\\Extra\\' => array($vendorDir . '/clue/connection-manager-extra/src'),
'Clue\\Redis\\Protocol\\' => array($vendorDir . '/clue/redis-protocol/src'),
'Clue\\React\\Utf8\\' => array($vendorDir . '/clue/utf8-react/src'),
'Clue\\React\\Term\\' => array($vendorDir . '/clue/term-react/src'),
'Clue\\React\\Stdio\\' => array($vendorDir . '/clue/stdio-react/src'),

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit412d4c2afc15769a0fac36e82d8e412c
class ComposerAutoloaderInit6063f1b04a2dad456eda2cdae82a650e
{
private static $loader;
@ -24,31 +24,61 @@ class ComposerAutoloaderInit412d4c2afc15769a0fac36e82d8e412c
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit412d4c2afc15769a0fac36e82d8e412c', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit412d4c2afc15769a0fac36e82d8e412c', 'loadClassLoader'));
spl_autoload_register(array('ComposerAutoloaderInit6063f1b04a2dad456eda2cdae82a650e', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit6063f1b04a2dad456eda2cdae82a650e', 'loadClassLoader'));
$includePaths = require __DIR__ . '/include_paths.php';
$includePaths[] = get_include_path();
set_include_path(implode(PATH_SEPARATOR, $includePaths));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c::getInitializer($loader));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire6063f1b04a2dad456eda2cdae82a650e($fileIdentifier, $file);
}
return $loader;
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire6063f1b04a2dad456eda2cdae82a650e($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

@ -4,15 +4,15 @@
namespace Composer\Autoload;
class ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c
class ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
'972fda704d680a3a53c68e34e193cb22' => __DIR__ . '/..' . '/react/promise-timer/src/functions_include.php',
'ebf8799635f67b5d7248946fe2154f4a' => __DIR__ . '/..' . '/ringcentral/psr7/src/functions_include.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'45b89995831374eefdfc4161161938f6' => __DIR__ . '/..' . '/jfcherng/php-color-output/src/helpers.php',
'ebf8799635f67b5d7248946fe2154f4a' => __DIR__ . '/..' . '/ringcentral/psr7/src/functions_include.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
@ -102,6 +102,7 @@ class ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c
array (
'Cron\\' => 5,
'ConnectionManager\\Extra\\' => 24,
'Clue\\Redis\\Protocol\\' => 20,
'Clue\\React\\Utf8\\' => 16,
'Clue\\React\\Term\\' => 16,
'Clue\\React\\Stdio\\' => 17,
@ -275,7 +276,7 @@ class ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c
),
'Doctrine\\Deprecations\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations',
0 => __DIR__ . '/..' . '/doctrine/deprecations/src',
),
'Doctrine\\Common\\Collections\\' =>
array (
@ -289,6 +290,10 @@ class ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c
array (
0 => __DIR__ . '/..' . '/clue/connection-manager-extra/src',
),
'Clue\\Redis\\Protocol\\' =>
array (
0 => __DIR__ . '/..' . '/clue/redis-protocol/src',
),
'Clue\\React\\Utf8\\' =>
array (
0 => __DIR__ . '/..' . '/clue/utf8-react/src',
@ -363,13 +368,6 @@ class ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c
0 => __DIR__ . '/..' . '/ezyang/htmlpurifier/library',
),
),
'C' =>
array (
'Clue\\Redis\\Protocol' =>
array (
0 => __DIR__ . '/..' . '/clue/redis-protocol/src',
),
),
'A' =>
array (
'AssetLoader' =>
@ -395,10 +393,10 @@ class ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c::$prefixesPsr0;
$loader->classMap = ComposerStaticInit412d4c2afc15769a0fac36e82d8e412c::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e::$prefixesPsr0;
$loader->classMap = ComposerStaticInit6063f1b04a2dad456eda2cdae82a650e::$classMap;
}, null, ClassLoader::class);
}

View File

@ -2,7 +2,7 @@
// include_paths.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(

File diff suppressed because it is too large Load Diff

View File

@ -1,301 +1,301 @@
<?php return array(
'root' => array(
'name' => 'icinga/icinga-php-thirdparty',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => 'f70b33a8ab9ec60c6491b2467935c32b9f390116',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '04c233f4add8d6fcebc00186826c7b709bc26e19',
'name' => 'icinga/icinga-php-thirdparty',
'dev' => true,
),
'versions' => array(
'brick/math' => array(
'pretty_version' => '0.9.3',
'version' => '0.9.3.0',
'reference' => 'ca57d18f028f84f777b2168cd1911b0dee2343ae',
'type' => 'library',
'install_path' => __DIR__ . '/../brick/math',
'aliases' => array(),
'reference' => 'ca57d18f028f84f777b2168cd1911b0dee2343ae',
'dev_requirement' => false,
),
'clue/block-react' => array(
'pretty_version' => 'v1.5.0',
'version' => '1.5.0.0',
'reference' => '718b0571a94aa693c6fffc72182e87257ac900f3',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/block-react',
'aliases' => array(),
'reference' => '718b0571a94aa693c6fffc72182e87257ac900f3',
'dev_requirement' => false,
),
'clue/connection-manager-extra' => array(
'pretty_version' => 'v1.3.0',
'version' => '1.3.0.0',
'reference' => '6ddf7da4cd75bf5aa54be13eea7be9aad9e97ada',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/connection-manager-extra',
'aliases' => array(),
'reference' => '6ddf7da4cd75bf5aa54be13eea7be9aad9e97ada',
'dev_requirement' => false,
),
'clue/http-proxy-react' => array(
'pretty_version' => 'v1.8.0',
'version' => '1.8.0.0',
'reference' => '09366dd3e13b36b90f8e47a6acaf5a2c96b79fb5',
'pretty_version' => 'v1.9.0',
'version' => '1.9.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/http-proxy-react',
'aliases' => array(),
'reference' => 'f3b02835273036a9370ac1c144b55df8e2b98430',
'dev_requirement' => false,
),
'clue/mq-react' => array(
'pretty_version' => 'v1.6.0',
'version' => '1.6.0.0',
'reference' => 'cab0147723017bc2deb3f248c607ad8e3c87e509',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/mq-react',
'aliases' => array(),
'reference' => 'cab0147723017bc2deb3f248c607ad8e3c87e509',
'dev_requirement' => false,
),
'clue/redis-protocol' => array(
'pretty_version' => 'v0.3.1',
'version' => '0.3.1.0',
'reference' => '271b8009887209d930f613ad3b9518f94bd6b51c',
'pretty_version' => 'v0.3.2',
'version' => '0.3.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/redis-protocol',
'aliases' => array(),
'reference' => '6f565332f5531b7722d1e9c445314b91862f6d6c',
'dev_requirement' => false,
),
'clue/redis-react' => array(
'pretty_version' => 'v2.6.0',
'version' => '2.6.0.0',
'reference' => 'f911455f9d7a77dd6f39c22548ddff521544b291',
'pretty_version' => 'v2.8.0',
'version' => '2.8.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/redis-react',
'aliases' => array(),
'reference' => '84569198dfd5564977d2ae6a32de4beb5a24bdca',
'dev_requirement' => false,
),
'clue/soap-react' => array(
'pretty_version' => 'v2.0.0',
'version' => '2.0.0.0',
'reference' => 'db2e0ba9b0380ac6c4a34df3f1021d2191d45d45',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/soap-react',
'aliases' => array(),
'reference' => 'db2e0ba9b0380ac6c4a34df3f1021d2191d45d45',
'dev_requirement' => false,
),
'clue/socket-raw' => array(
'pretty_version' => 'v1.6.0',
'version' => '1.6.0.0',
'reference' => '91e9f619f6769f931454a9882c21ffd7623d06cb',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/socket-raw',
'aliases' => array(),
'reference' => '91e9f619f6769f931454a9882c21ffd7623d06cb',
'dev_requirement' => false,
),
'clue/socks-react' => array(
'pretty_version' => 'v1.4.0',
'version' => '1.4.0.0',
'reference' => 'eb3bd2ca5f43fea4b3bc4ef055179e0e6714730d',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/socks-react',
'aliases' => array(),
'reference' => 'eb3bd2ca5f43fea4b3bc4ef055179e0e6714730d',
'dev_requirement' => false,
),
'clue/stdio-react' => array(
'pretty_version' => 'v2.6.0',
'version' => '2.6.0.0',
'reference' => 'dfa6c378aabdff718202d4e2453f752c38ea3399',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/stdio-react',
'aliases' => array(),
'reference' => 'dfa6c378aabdff718202d4e2453f752c38ea3399',
'dev_requirement' => false,
),
'clue/term-react' => array(
'pretty_version' => 'v1.3.0',
'version' => '1.3.0.0',
'reference' => 'eb6eb063eda04a714ef89f066586a2c49588f7ca',
'pretty_version' => 'v1.4.0',
'version' => '1.4.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/term-react',
'aliases' => array(),
'reference' => '00f297dc597eaee2ebf98af8f27cca5d21d60fa3',
'dev_requirement' => false,
),
'clue/utf8-react' => array(
'pretty_version' => 'v1.2.0',
'version' => '1.2.0.0',
'reference' => '8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96',
'pretty_version' => 'v1.3.0',
'version' => '1.3.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../clue/utf8-react',
'aliases' => array(),
'reference' => 'd5cd04d39cb5457aa5df830b7c4b301d2694217e',
'dev_requirement' => false,
),
'components/jquery' => array(
'pretty_version' => '3.6.0',
'version' => '3.6.0.0',
'reference' => '6cf38ee1fd04b6adf8e7dda161283aa35be818c3',
'type' => 'component',
'install_path' => __DIR__ . '/../components/jquery',
'aliases' => array(),
'reference' => '6cf38ee1fd04b6adf8e7dda161283aa35be818c3',
'dev_requirement' => false,
),
'cweagans/composer-patches' => array(
'pretty_version' => '1.7.3',
'version' => '1.7.3.0',
'reference' => 'e190d4466fe2b103a55467dfa83fc2fecfcaf2db',
'type' => 'composer-plugin',
'install_path' => __DIR__ . '/../cweagans/composer-patches',
'aliases' => array(),
'reference' => 'e190d4466fe2b103a55467dfa83fc2fecfcaf2db',
'dev_requirement' => false,
),
'doctrine/collections' => array(
'pretty_version' => '1.8.0',
'version' => '1.8.0.0',
'reference' => '2b44dd4cbca8b5744327de78bafef5945c7e7b5e',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/collections',
'aliases' => array(),
'reference' => '2b44dd4cbca8b5744327de78bafef5945c7e7b5e',
'dev_requirement' => false,
),
'doctrine/deprecations' => array(
'pretty_version' => '1.1.2',
'version' => '1.1.2.0',
'reference' => '4f2d4f2836e7ec4e7a8625e75c6aa916004db931',
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/deprecations',
'aliases' => array(),
'reference' => '31610dbb31faa98e6b5447b62340826f54fbc4e9',
'dev_requirement' => false,
),
'dompdf/dompdf' => array(
'pretty_version' => 'v2.0.3',
'version' => '2.0.3.0',
'reference' => 'e8d2d5e37e8b0b30f0732a011295ab80680d7e85',
'pretty_version' => 'v2.0.8',
'version' => '2.0.8.0',
'type' => 'library',
'install_path' => __DIR__ . '/../dompdf/dompdf',
'aliases' => array(),
'reference' => 'c20247574601700e1f7c8dab39310fca1964dc52',
'dev_requirement' => false,
),
'dragonmantank/cron-expression' => array(
'pretty_version' => 'v3.3.3',
'version' => '3.3.3.0',
'reference' => 'adfb1f505deb6384dc8b39804c5065dd3c8c8c0a',
'pretty_version' => 'v3.4.0',
'version' => '3.4.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../dragonmantank/cron-expression',
'aliases' => array(),
'reference' => '8c784d071debd117328803d86b2097615b457500',
'dev_requirement' => false,
),
'erusev/parsedown' => array(
'pretty_version' => '1.7.4',
'version' => '1.7.4.0',
'reference' => 'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
'type' => 'library',
'install_path' => __DIR__ . '/../erusev/parsedown',
'aliases' => array(),
'reference' => 'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
'dev_requirement' => false,
),
'evenement/evenement' => array(
'pretty_version' => 'v3.0.2',
'version' => '3.0.2.0',
'reference' => '0a16b0d71ab13284339abb99d9d2bd813640efbc',
'type' => 'library',
'install_path' => __DIR__ . '/../evenement/evenement',
'aliases' => array(),
'reference' => '0a16b0d71ab13284339abb99d9d2bd813640efbc',
'dev_requirement' => false,
),
'ezyang/htmlpurifier' => array(
'pretty_version' => 'v4.17.0',
'version' => '4.17.0.0',
'reference' => 'bbc513d79acf6691fa9cf10f192c90dd2957f18c',
'pretty_version' => 'v4.18.0',
'version' => '4.18.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../ezyang/htmlpurifier',
'aliases' => array(),
'reference' => 'cb56001e54359df7ae76dc522d08845dc741621b',
'dev_requirement' => false,
),
'fig/http-message-util' => array(
'pretty_version' => '1.1.5',
'version' => '1.1.5.0',
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
'type' => 'library',
'install_path' => __DIR__ . '/../fig/http-message-util',
'aliases' => array(),
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
'dev_requirement' => false,
),
'guzzlehttp/guzzle' => array(
'pretty_version' => '7.8.0',
'version' => '7.8.0.0',
'reference' => '1110f66a6530a40fe7aea0378fe608ee2b2248f9',
'pretty_version' => '7.9.2',
'version' => '7.9.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
'aliases' => array(),
'reference' => 'd281ed313b989f213357e3be1a179f02196ac99b',
'dev_requirement' => false,
),
'guzzlehttp/promises' => array(
'pretty_version' => '2.0.1',
'version' => '2.0.1.0',
'reference' => '111166291a0f8130081195ac4556a5587d7f1b5d',
'pretty_version' => '2.0.4',
'version' => '2.0.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/promises',
'aliases' => array(),
'reference' => 'f9c436286ab2892c7db7be8c8da4ef61ccf7b455',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
'pretty_version' => '2.6.1',
'version' => '2.6.1.0',
'reference' => 'be45764272e8873c72dbe3d2edcfdfcc3bc9f727',
'pretty_version' => '2.7.0',
'version' => '2.7.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(),
'reference' => 'a70f5c95fb43bc83f07c9c948baa0dc1829bf201',
'dev_requirement' => false,
),
'icinga/icinga-php-thirdparty' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => 'f70b33a8ab9ec60c6491b2467935c32b9f390116',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '04c233f4add8d6fcebc00186826c7b709bc26e19',
'dev_requirement' => false,
),
'jfcherng/php-color-output' => array(
'pretty_version' => '2.0.2',
'version' => '2.0.2.0',
'reference' => '2673074597eca9682d2fdfaee39a22418d4cc2f6',
'type' => 'library',
'install_path' => __DIR__ . '/../jfcherng/php-color-output',
'aliases' => array(),
'reference' => '2673074597eca9682d2fdfaee39a22418d4cc2f6',
'dev_requirement' => false,
),
'jfcherng/php-diff' => array(
'pretty_version' => '6.10.14',
'version' => '6.10.14.0',
'reference' => '38b6e816badaa97f75541484d6c3d7c3e5dbca78',
'pretty_version' => '6.10.17',
'version' => '6.10.17.0',
'type' => 'library',
'install_path' => __DIR__ . '/../jfcherng/php-diff',
'aliases' => array(),
'reference' => '72f551e6200831dc5a7015d28af285649baee817',
'dev_requirement' => false,
),
'jfcherng/php-mb-string' => array(
'pretty_version' => '1.4.8',
'version' => '1.4.8.0',
'reference' => 'ef8926ff849863bfea234e99ee827947bedd86b0',
'type' => 'library',
'install_path' => __DIR__ . '/../jfcherng/php-mb-string',
'aliases' => array(),
'reference' => 'ef8926ff849863bfea234e99ee827947bedd86b0',
'dev_requirement' => false,
),
'jfcherng/php-sequence-matcher' => array(
'pretty_version' => '3.2.10',
'version' => '3.2.10.0',
'reference' => '650164598be2c6ad6891dbd41de4cb5cc21cc91b',
'type' => 'library',
'install_path' => __DIR__ . '/../jfcherng/php-sequence-matcher',
'aliases' => array(),
'reference' => '650164598be2c6ad6891dbd41de4cb5cc21cc91b',
'dev_requirement' => false,
),
'masterminds/html5' => array(
'pretty_version' => '2.8.1',
'version' => '2.8.1.0',
'reference' => 'f47dcf3c70c584de14f21143c55d9939631bc6cf',
'pretty_version' => '2.9.0',
'version' => '2.9.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../masterminds/html5',
'aliases' => array(),
'reference' => 'f5ac2c0b0a2eefca70b2ce32a5809992227e75a6',
'dev_requirement' => false,
),
'mtdowling/cron-expression' => array(
@ -305,39 +305,39 @@
),
),
'phenx/php-font-lib' => array(
'pretty_version' => '0.5.4',
'version' => '0.5.4.0',
'reference' => 'dd448ad1ce34c63d09baccd05415e361300c35b4',
'pretty_version' => '0.5.6',
'version' => '0.5.6.0',
'type' => 'library',
'install_path' => __DIR__ . '/../phenx/php-font-lib',
'aliases' => array(),
'reference' => 'a1681e9793040740a405ac5b189275059e2a9863',
'dev_requirement' => false,
),
'phenx/php-svg-lib' => array(
'pretty_version' => '0.5.0',
'version' => '0.5.0.0',
'reference' => '76876c6cf3080bcb6f249d7d59705108166a6685',
'pretty_version' => '0.5.4',
'version' => '0.5.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../phenx/php-svg-lib',
'aliases' => array(),
'reference' => '46b25da81613a9cf43c83b2a8c2c1bdab27df691',
'dev_requirement' => false,
),
'predis/predis' => array(
'pretty_version' => 'v2.2.2',
'version' => '2.2.2.0',
'reference' => 'b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1',
'pretty_version' => 'v2.3.0',
'version' => '2.3.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../predis/predis',
'aliases' => array(),
'reference' => 'bac46bfdb78cd6e9c7926c697012aae740cb9ec9',
'dev_requirement' => false,
),
'psr/http-client' => array(
'pretty_version' => '1.0.3',
'version' => '1.0.3.0',
'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-client',
'aliases' => array(),
'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
'dev_requirement' => false,
),
'psr/http-client-implementation' => array(
@ -347,12 +347,12 @@
),
),
'psr/http-factory' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
'pretty_version' => '1.1.0',
'version' => '1.1.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'dev_requirement' => false,
),
'psr/http-factory-implementation' => array(
@ -364,10 +364,10 @@
'psr/http-message' => array(
'pretty_version' => '1.1',
'version' => '1.1.0.0',
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
'dev_requirement' => false,
),
'psr/http-message-implementation' => array(
@ -379,136 +379,136 @@
'psr/log' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
'dev_requirement' => false,
),
'ralouphie/getallheaders' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'type' => 'library',
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
'aliases' => array(),
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'dev_requirement' => false,
),
'ramsey/collection' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'reference' => 'ab2237657ad99667a5143e32ba2683c8029563d4',
'type' => 'library',
'install_path' => __DIR__ . '/../ramsey/collection',
'aliases' => array(),
'reference' => 'ab2237657ad99667a5143e32ba2683c8029563d4',
'dev_requirement' => false,
),
'ramsey/uuid' => array(
'pretty_version' => '4.2.3',
'version' => '4.2.3.0',
'reference' => 'fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df',
'type' => 'library',
'install_path' => __DIR__ . '/../ramsey/uuid',
'aliases' => array(),
'reference' => 'fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df',
'dev_requirement' => false,
),
'react/cache' => array(
'pretty_version' => 'v1.2.0',
'version' => '1.2.0.0',
'reference' => 'd47c472b64aa5608225f47965a484b75c7817d5b',
'type' => 'library',
'install_path' => __DIR__ . '/../react/cache',
'aliases' => array(),
'reference' => 'd47c472b64aa5608225f47965a484b75c7817d5b',
'dev_requirement' => false,
),
'react/child-process' => array(
'pretty_version' => 'v0.6.5',
'version' => '0.6.5.0',
'reference' => 'e71eb1aa55f057c7a4a0d08d06b0b0a484bead43',
'pretty_version' => 'v0.6.6',
'version' => '0.6.6.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/child-process',
'aliases' => array(),
'reference' => '1721e2b93d89b745664353b9cfc8f155ba8a6159',
'dev_requirement' => false,
),
'react/datagram' => array(
'pretty_version' => 'v1.9.0',
'version' => '1.9.0.0',
'reference' => '13c259bb2fd04f238eeb85f3783167eedec46e2c',
'pretty_version' => 'v1.10.0',
'version' => '1.10.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/datagram',
'aliases' => array(),
'reference' => '9236e1f5a67a6029be17d551e9858c487836c301',
'dev_requirement' => false,
),
'react/dns' => array(
'pretty_version' => 'v1.11.0',
'version' => '1.11.0.0',
'reference' => '3be0fc8f1eb37d6875cd6f0c6c7d0be81435de9f',
'pretty_version' => 'v1.13.0',
'version' => '1.13.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/dns',
'aliases' => array(),
'reference' => 'eb8ae001b5a455665c89c1df97f6fb682f8fb0f5',
'dev_requirement' => false,
),
'react/event-loop' => array(
'pretty_version' => 'v1.5.0',
'version' => '1.5.0.0',
'reference' => 'bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354',
'type' => 'library',
'install_path' => __DIR__ . '/../react/event-loop',
'aliases' => array(),
'reference' => 'bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354',
'dev_requirement' => false,
),
'react/http' => array(
'pretty_version' => 'v1.9.0',
'version' => '1.9.0.0',
'reference' => 'bb3154dbaf2dfe3f0467f956a05f614a69d5f1d0',
'pretty_version' => 'v1.11.0',
'version' => '1.11.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/http',
'aliases' => array(),
'reference' => '8db02de41dcca82037367f67a2d4be365b1c4db9',
'dev_requirement' => false,
),
'react/promise' => array(
'pretty_version' => 'v2.11.0',
'version' => '2.11.0.0',
'reference' => '1a8460931ea36dc5c76838fec5734d55c88c6831',
'type' => 'library',
'install_path' => __DIR__ . '/../react/promise',
'aliases' => array(),
'reference' => '1a8460931ea36dc5c76838fec5734d55c88c6831',
'dev_requirement' => false,
),
'react/promise-stream' => array(
'pretty_version' => 'v1.6.0',
'version' => '1.6.0.0',
'reference' => '87d1399a33926fd5ba28a7647346b71482787502',
'pretty_version' => 'v1.7.0',
'version' => '1.7.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/promise-stream',
'aliases' => array(),
'reference' => '5c7ec3450f558deb779742e33967d837e2db7871',
'dev_requirement' => false,
),
'react/promise-timer' => array(
'pretty_version' => 'v1.10.0',
'version' => '1.10.0.0',
'reference' => '4cb85c1c2125390748e3908120bb82feb99fe766',
'pretty_version' => 'v1.11.0',
'version' => '1.11.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/promise-timer',
'aliases' => array(),
'reference' => '4f70306ed66b8b44768941ca7f142092600fafc1',
'dev_requirement' => false,
),
'react/socket' => array(
'pretty_version' => 'v1.14.0',
'version' => '1.14.0.0',
'reference' => '21591111d3ea62e31f2254280ca0656bc2b1bda6',
'pretty_version' => 'v1.16.0',
'version' => '1.16.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/socket',
'aliases' => array(),
'reference' => '23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1',
'dev_requirement' => false,
),
'react/stream' => array(
'pretty_version' => 'v1.3.0',
'version' => '1.3.0.0',
'reference' => '6fbc9672905c7d5a885f2da2fc696f65840f4a66',
'pretty_version' => 'v1.4.0',
'version' => '1.4.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../react/stream',
'aliases' => array(),
'reference' => '1e5b0acb8fe55143b5b426817155190eb6f5b18d',
'dev_requirement' => false,
),
'rhumsaa/uuid' => array(
@ -520,109 +520,109 @@
'ringcentral/psr7' => array(
'pretty_version' => '1.3.0',
'version' => '1.3.0.0',
'reference' => '360faaec4b563958b673fb52bbe94e37f14bc686',
'type' => 'library',
'install_path' => __DIR__ . '/../ringcentral/psr7',
'aliases' => array(),
'reference' => '360faaec4b563958b673fb52bbe94e37f14bc686',
'dev_requirement' => false,
),
'sabberworm/php-css-parser' => array(
'pretty_version' => '8.4.0',
'version' => '8.4.0.0',
'reference' => 'e41d2140031d533348b2192a83f02d8dd8a71d30',
'pretty_version' => 'v8.7.0',
'version' => '8.7.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../sabberworm/php-css-parser',
'aliases' => array(),
'reference' => 'f414ff953002a9b18e3a116f5e462c56f21237cf',
'dev_requirement' => false,
),
'shardj/zf1-future' => array(
'pretty_version' => '1.23.5',
'version' => '1.23.5.0',
'reference' => '0464ee916ca73142ab733d80c83210d89cba3936',
'pretty_version' => '1.24.2',
'version' => '1.24.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../shardj/zf1-future',
'aliases' => array(),
'reference' => '68d83e18c01950547ddf26700a8a99abeb98e936',
'dev_requirement' => false,
),
'simshaun/recurr' => array(
'pretty_version' => 'v5.0.2',
'version' => '5.0.2.0',
'reference' => '1aff62e6e0ee875b3f2487352542605123ee9172',
'pretty_version' => 'v5.0.3',
'version' => '5.0.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../simshaun/recurr',
'aliases' => array(),
'reference' => '7b136768d64f257065e38a804ee6d2f9af6ba6d1',
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
'pretty_version' => 'v2.5.4',
'version' => '2.5.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'reference' => '605389f2a7e5625f273b53960dc46aeaf9c62918',
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb',
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '42292d99c55abe617799667f454222c54c60e229',
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => '85181ba99b2345b0ef10ce42ecac37612d9fd341',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => '60328e362d4c2c802a54fcbf04f9d3fb892b4cf8',
'dev_requirement' => false,
),
'symfony/polyfill-php81' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '7581cd600fa9fd681b797d00b02f068e2f13263b',
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php81',
'aliases' => array(),
'reference' => '4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c',
'dev_requirement' => false,
),
'tedivm/jshrink' => array(
'pretty_version' => 'v1.7.0',
'version' => '1.7.0.0',
'reference' => '7a35f5a4651ca2ce77295eb8a3b4e133ba47e19e',
'type' => 'library',
'install_path' => __DIR__ . '/../tedivm/jshrink',
'aliases' => array(),
'reference' => '7a35f5a4651ca2ce77295eb8a3b4e133ba47e19e',
'dev_requirement' => false,
),
'webmozart/assert' => array(
'pretty_version' => '1.11.0',
'version' => '1.11.0.0',
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
'type' => 'library',
'install_path' => __DIR__ . '/../webmozart/assert',
'aliases' => array(),
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
'dev_requirement' => false,
),
'wikimedia/less.php' => array(
'pretty_version' => 'v3.2.1',
'version' => '3.2.1.0',
'reference' => '0d5b30ba792bdbf8991a646fc9c30561b38a5559',
'type' => 'library',
'install_path' => __DIR__ . '/../wikimedia/less.php',
'aliases' => array(),
'reference' => '0d5b30ba792bdbf8991a646fc9c30561b38a5559',
'dev_requirement' => false,
),
'zendframework/zendframework1' => array(

View File

@ -150,6 +150,67 @@ class MyTest extends TestCase
}
```
## Displaying deprecations after running a PHPUnit test suite
It is possible to integrate this library with PHPUnit to display all
deprecations triggered during the test suite execution.
```xml
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
bootstrap="vendor/autoload.php"
displayDetailsOnTestsThatTriggerDeprecations="true"
failOnDeprecation="true"
>
<!-- one attribute to display the deprecations, the other to fail the test suite -->
<php>
<!-- ensures native PHP deprecations are used -->
<server name="DOCTRINE_DEPRECATIONS" value="trigger"/>
</php>
<!-- ensures the @ operator in @trigger_error is ignored -->
<source ignoreSuppressionOfDeprecations="true">
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
```
Note that you can still trigger Deprecations in your code, provided you use the
`#[WithoutErrorHandler]` attribute to disable PHPUnit's error handler for tests
that call it. Be wary that this will disable all error handling, meaning it
will mask any warnings or errors that would otherwise be caught by PHPUnit.
At the moment, it is not possible to disable deduplication with an environment
variable, but you can use a bootstrap file to achieve that:
```php
// tests/bootstrap.php
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
use Doctrine\Deprecations\Deprecation;
Deprecation::withoutDeduplication();
```
Then, reference that file in your PHPUnit configuration:
```xml
<phpunit
bootstrap="tests/bootstrap.php"
>
</phpunit>
```
## What is a deprecation identifier?
An identifier for deprecations is just a link to any resource, most often a

View File

@ -8,20 +8,18 @@
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"doctrine/coding-standard": "^9 || ^12",
"phpstan/phpstan": "1.4.10 || 2.0.3",
"phpstan/phpstan-phpunit": "^1.0 || ^2",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
"psr/log": "^1 || ^2 || ^3"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"autoload": {
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
"Doctrine\\Deprecations\\": "src"
}
},
"autoload-dev": {

View File

@ -173,9 +173,7 @@ class Deprecation
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
}
/**
* @param list<array{function: string, line?: int, file?: string, class?: class-string, type?: string, args?: mixed[], object?: object}> $backtrace
*/
/** @param list<array{function: string, line?: int, file?: string, class?: class-string, type?: string, args?: mixed[], object?: object}> $backtrace */
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
{
$type = self::$type ?? self::getTypeFromEnv();
@ -226,19 +224,19 @@ class Deprecation
public static function enableTrackingDeprecations(): void
{
self::$type = self::$type ?? 0;
self::$type = self::$type ?? self::getTypeFromEnv();
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
}
public static function enableWithTriggerError(): void
{
self::$type = self::$type ?? 0;
self::$type = self::$type ?? self::getTypeFromEnv();
self::$type |= self::TYPE_TRIGGER_ERROR;
}
public static function enableWithPsrLogger(LoggerInterface $logger): void
{
self::$type = self::$type ?? 0;
self::$type = self::$type ?? self::getTypeFromEnv();
self::$type |= self::TYPE_PSR_LOGGER;
self::$logger = $logger;
}
@ -289,9 +287,7 @@ class Deprecation
return self::$triggeredDeprecations;
}
/**
* @return int-mask-of<self::TYPE_*>
*/
/** @return int-mask-of<self::TYPE_*> */
private static function getTypeFromEnv(): int
{
switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) {

View File

@ -26,17 +26,13 @@ trait VerifyDeprecations
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
}
/**
* @before
*/
/** @before */
public function enableDeprecationTracking(): void
{
Deprecation::enableTrackingDeprecations();
}
/**
* @after
*/
/** @after */
public function verifyDeprecationsAreTriggered(): void
{
foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {

View File

@ -1 +1 @@
2.0.3
2.0.8

View File

@ -29,7 +29,7 @@
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"phenx/php-font-lib": ">=0.5.4 <1.0.0",
"phenx/php-svg-lib": ">=0.3.3 <1.0.0"
"phenx/php-svg-lib": ">=0.5.2 <1.0.0"
},
"require-dev": {
"ext-json": "*",

View File

@ -467,12 +467,17 @@ class GD implements Canvas
imagesetthickness($this->get_image(), $width);
if ($c === IMG_COLOR_STYLED) {
imagepolygon($this->get_image(), [
$points = [
$x1, $y1,
$x1 + $w, $y1,
$x1 + $w, $y1 + $h,
$x1, $y1 + $h
], $c);
];
if (version_compare(PHP_VERSION, "8.1.0", "<")) {
imagepolygon($this->get_image(), $points, count($points)/2, $c);
} else {
imagepolygon($this->get_image(), $points, $c);
}
} else {
imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
@ -570,9 +575,17 @@ class GD implements Canvas
imagesetthickness($this->get_image(), isset($width) ? $width : 0);
if ($fill) {
imagefilledpolygon($this->get_image(), $points, $c);
if (version_compare(PHP_VERSION, "8.1.0", "<")) {
imagefilledpolygon($this->get_image(), $points, count($points)/2, $c);
} else {
imagefilledpolygon($this->get_image(), $points, $c);
}
} else {
imagepolygon($this->get_image(), $points, $c);
if (version_compare(PHP_VERSION, "8.1.0", "<")) {
imagepolygon($this->get_image(), $points, count($points)/2, $c);
} else {
imagepolygon($this->get_image(), $points, $c);
}
}
}

View File

@ -31,6 +31,14 @@ class Cache
*/
protected static $tempImages = [];
/**
* Array of image references from an SVG document.
* Used to detect circular references across SVG documents.
*
* @var array
*/
protected static $svgRefs = [];
/**
* The url to the "broken image" used when images can't be loaded
*
@ -134,20 +142,28 @@ class Cache
$parser,
function ($parser, $name, $attributes) use ($options, $parsed_url, $full_url) {
if (strtolower($name) === "image") {
if (!\array_key_exists($full_url, self::$svgRefs)) {
self::$svgRefs[$full_url] = [];
}
$attributes = array_change_key_case($attributes, CASE_LOWER);
$urls = [];
$urls[] = $attributes["xlink:href"] ?? "";
$urls[] = $attributes["href"] ?? "";
foreach ($urls as $url) {
if (!empty($url)) {
$inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
if ($inner_full_url === $full_url) {
throw new ImageException("SVG self-reference is not allowed", E_WARNING);
}
[$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
if (!empty($message)) {
throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING);
}
if (empty($url)) {
continue;
}
$inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
if (empty($inner_full_url)) {
continue;
}
self::detectCircularRef($full_url, $inner_full_url);
self::$svgRefs[$full_url][] = $inner_full_url;
[$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
if (!empty($message)) {
throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING);
}
}
}
@ -178,6 +194,19 @@ class Cache
return [$resolved_url, $type, $message];
}
static function detectCircularRef(string $src, string $target)
{
if (!\array_key_exists($target, self::$svgRefs)) {
return;
}
foreach (self::$svgRefs[$target] as $ref) {
if ($ref === $src) {
throw new ImageException("Circular external SVG image reference detected.", E_WARNING);
}
self::detectCircularRef($src, $ref);
}
}
/**
* Register a temp file for the given original image file.
*
@ -239,6 +268,7 @@ class Cache
self::$_cache = [];
self::$tempImages = [];
self::$svgRefs = [];
}
static function detect_type($file, $context = null)

View File

@ -83,6 +83,13 @@ class Options
"https://" => ["rules" => []]
];
/**
* Operational artifact (log files, temporary files) path validation
*
* @var callable
*/
private $artifactPathValidation = null;
/**
* @var string
*/
@ -335,6 +342,8 @@ class Options
$this->setAllowedProtocols(["file://", "http://", "https://"]);
$this->setArtifactPathValidation([$this, "validateArtifactPath"]);
if (null !== $attributes) {
$this->set($attributes);
}
@ -361,6 +370,8 @@ class Options
$this->setChroot($value);
} elseif ($key === 'allowedProtocols') {
$this->setAllowedProtocols($value);
} elseif ($key === 'artifactPathValidation') {
$this->setArtifactPathValidation($value);
} elseif ($key === 'logOutputFile' || $key === 'log_output_file') {
$this->setLogOutputFile($value);
} elseif ($key === 'defaultMediaType' || $key === 'default_media_type') {
@ -428,6 +439,8 @@ class Options
return $this->getChroot();
} elseif ($key === 'allowedProtocols') {
return $this->getAllowedProtocols();
} elseif ($key === 'artifactPathValidation') {
return $this->getArtifactPathValidation();
} elseif ($key === 'logOutputFile' || $key === 'log_output_file') {
return $this->getLogOutputFile();
} elseif ($key === 'defaultMediaType' || $key === 'default_media_type') {
@ -592,6 +605,24 @@ class Options
return $this;
}
/**
* @return array
*/
public function getArtifactPathValidation()
{
return $this->artifactPathValidation;
}
/**
* @param callable $validator
* @return $this
*/
public function setArtifactPathValidation($validator)
{
$this->artifactPathValidation = $validator;
return $this;
}
/**
* @return array
*/
@ -848,7 +879,9 @@ class Options
*/
public function setFontCache($fontCache)
{
$this->fontCache = $fontCache;
if (!is_callable($this->artifactPathValidation) || ($this->artifactPathValidation)($fontCache, "fontCache") === true) {
$this->fontCache = $fontCache;
}
return $this;
}
@ -866,7 +899,9 @@ class Options
*/
public function setFontDir($fontDir)
{
$this->fontDir = $fontDir;
if (!is_callable($this->artifactPathValidation) || ($this->artifactPathValidation)($fontDir, "fontDir") === true) {
$this->fontDir = $fontDir;
}
return $this;
}
@ -1035,7 +1070,9 @@ class Options
*/
public function setLogOutputFile($logOutputFile)
{
$this->logOutputFile = $logOutputFile;
if (!is_callable($this->artifactPathValidation) || ($this->artifactPathValidation)($logOutputFile, "logOutputFile") === true) {
$this->logOutputFile = $logOutputFile;
}
return $this;
}
@ -1053,7 +1090,9 @@ class Options
*/
public function setTempDir($tempDir)
{
$this->tempDir = $tempDir;
if (!is_callable($this->artifactPathValidation) || ($this->artifactPathValidation)($tempDir, "tempDir") === true) {
$this->tempDir = $tempDir;
}
return $this;
}
@ -1071,7 +1110,9 @@ class Options
*/
public function setRootDir($rootDir)
{
$this->rootDir = $rootDir;
if (!is_callable($this->artifactPathValidation) || ($this->artifactPathValidation)($rootDir, "rootDir") === true) {
$this->rootDir = $rootDir;
}
return $this;
}
@ -1105,6 +1146,19 @@ class Options
return $this->httpContext;
}
public function validateArtifactPath(?string $path, string $option)
{
if ($path === null) {
return true;
}
$parsed_uri = parse_url($path);
if ($parsed_uri === false || (array_key_exists("scheme", $parsed_uri) && strtolower($parsed_uri["scheme"]) === "phar")) {
return false;
}
return true;
}
public function validateLocalUri(string $uri)
{
if ($uri === null || strlen($uri) === 0) {

View File

@ -1,7 +1,7 @@
PHP Cron Expression Parser
==========================
[![Latest Stable Version](https://poser.pugx.org/dragonmantank/cron-expression/v/stable.png)](https://packagist.org/packages/dragonmantank/cron-expression) [![Total Downloads](https://poser.pugx.org/dragonmantank/cron-expression/downloads.png)](https://packagist.org/packages/dragonmantank/cron-expression) [![Build Status](https://secure.travis-ci.org/dragonmantank/cron-expression.png)](http://travis-ci.org/dragonmantank/cron-expression) [![StyleCI](https://github.styleci.io/repos/103715337/shield?branch=master)](https://github.styleci.io/repos/103715337)
[![Latest Stable Version](https://poser.pugx.org/dragonmantank/cron-expression/v/stable.png)](https://packagist.org/packages/dragonmantank/cron-expression) [![Total Downloads](https://poser.pugx.org/dragonmantank/cron-expression/downloads.png)](https://packagist.org/packages/dragonmantank/cron-expression) [![Tests](https://github.com/dragonmantank/cron-expression/actions/workflows/tests.yml/badge.svg)](https://github.com/dragonmantank/cron-expression/actions/workflows/tests.yml) [![StyleCI](https://github.styleci.io/repos/103715337/shield?branch=master)](https://github.styleci.io/repos/103715337)
The PHP cron expression parser can parse a CRON expression, determine if it is
due to run, calculate the next run date of the expression, and calculate the previous
@ -55,23 +55,65 @@ CRON Expressions
A CRON expression is a string representing the schedule for a particular command to execute. The parts of a CRON schedule are as follows:
* * * * *
- - - - -
| | | | |
| | | | |
| | | | +----- day of week (0 - 7) (Sunday=0 or 7)
| | | +---------- month (1 - 12)
| | +--------------- day of month (1 - 31)
| +-------------------- hour (0 - 23)
+------------------------- min (0 - 59)
```
* * * * *
- - - - -
| | | | |
| | | | |
| | | | +----- day of week (0-7) (Sunday = 0 or 7) (or SUN-SAT)
| | | +--------- month (1-12) (or JAN-DEC)
| | +------------- day of month (1-31)
| +----------------- hour (0-23)
+--------------------- minute (0-59)
```
This library also supports a few macros:
Each part of expression can also use wildcard, lists, ranges and steps:
* `@yearly`, `@annually` - Run once a year, midnight, Jan. 1 - `0 0 1 1 *`
* `@monthly` - Run once a month, midnight, first of month - `0 0 1 * *`
* `@weekly` - Run once a week, midnight on Sun - `0 0 * * 0`
* `@daily`, `@midnight` - Run once a day, midnight - `0 0 * * *`
* `@hourly` - Run once an hour, first minute - `0 * * * *`
- wildcard - match always
- `* * * * *` - At every minute.
- day of week and day of month also support `?`, an alias to `*`
- lists - match list of values, ranges and steps
- e.g. `15,30 * * * *` - At minute 15 and 30.
- ranges - match values in range
- e.g. `1-9 * * * *` - At every minute from 1 through 9.
- steps - match every nth value in range
- e.g. `*/5 * * * *` - At every 5th minute.
- e.g. `0-30/5 * * * *` - At every 5th minute from 0 through 30.
- combinations
- e.g. `0-14,30-44 * * * *` - At every minute from 0 through 14 and every minute from 30 through 44.
You can also use macro instead of an expression:
- `@yearly`, `@annually` - At 00:00 on 1st of January. (same as `0 0 1 1 *`)
- `@monthly` - At 00:00 on day-of-month 1. (same as `0 0 1 * *`)
- `@weekly` - At 00:00 on Sunday. (same as `0 0 * * 0`)
- `@daily`, `@midnight` - At 00:00. (same as `0 0 * * *`)
- `@hourly` - At minute 0. (same as `0 * * * *`)
Day of month extra features:
- nearest weekday - weekday (Monday-Friday) nearest to the given day
- e.g. `* * 15W * *` - At every minute on a weekday nearest to the 15th.
- If you were to specify `15W` as the value, the meaning is: "the nearest weekday to the 15th of the month"
So if the 15th is a Saturday, the trigger will fire on Friday the 14th.
If the 15th is a Sunday, the trigger will fire on Monday the 16th.
If the 15th is a Tuesday, then it will fire on Tuesday the 15th.
- However, if you specify `1W` as the value for day-of-month,
and the 1st is a Saturday, the trigger will fire on Monday the 3rd,
as it will not 'jump' over the boundary of a month's days.
- last day of the month
- e.g. `* * L * *` - At every minute on a last day-of-month.
- last weekday of the month
- e.g. `* * LW * *` - At every minute on a last weekday.
Day of week extra features:
- nth day
- e.g. `* * * * 7#4` - At every minute on 4th Sunday.
- 1-5
- Every day of week repeats 4-5 times a month. To target the last one, use "last day" feature instead.
- last day
- e.g. `* * * * 7L` - At every minute on the last Sunday.
Requirements
============
@ -85,3 +127,5 @@ Projects that Use cron-expression
* Part of the [Laravel Framework](https://github.com/laravel/framework/)
* Available as a [Symfony Bundle - setono/cron-expression-bundle](https://github.com/Setono/CronExpressionBundle)
* Framework agnostic, PHP-based job scheduler - [Crunz](https://github.com/crunzphp/crunz)
* Framework agnostic job scheduler - with locks, parallelism, per-second scheduling and more - [orisai/scheduler](https://github.com/orisai/scheduler)
* Explain expression in English (and other languages) with [orisai/cron-expression-explainer](https://github.com/orisai/cron-expression-explainer)

View File

@ -18,7 +18,6 @@
"require-dev": {
"phpstan/phpstan": "^1.0",
"phpunit/phpunit": "^7.0|^8.0|^9.0",
"phpstan/phpstan-webmozart-assert": "^1.0",
"phpstan/extension-installer": "^1.0"
},
"autoload": {
@ -38,6 +37,11 @@
"phpstan": "./vendor/bin/phpstan analyze",
"test": "phpunit"
},
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"config": {
"allow-plugins": {
"ocramius/package-versions": true,

View File

@ -12,7 +12,6 @@ use Exception;
use InvalidArgumentException;
use LogicException;
use RuntimeException;
use Webmozart\Assert\Assert;
/**
* CRON expression parser that can determine whether or not a CRON expression is
@ -148,7 +147,7 @@ class CronExpression
/**
* @deprecated since version 3.0.2, use __construct instead.
*/
public static function factory(string $expression, FieldFactoryInterface $fieldFactory = null): CronExpression
public static function factory(string $expression, ?FieldFactoryInterface $fieldFactory = null): CronExpression
{
/** @phpstan-ignore-next-line */
return new static($expression, $fieldFactory);
@ -179,7 +178,7 @@ class CronExpression
* @param null|FieldFactoryInterface $fieldFactory Factory to create cron fields
* @throws InvalidArgumentException
*/
public function __construct(string $expression, FieldFactoryInterface $fieldFactory = null)
public function __construct(string $expression, ?FieldFactoryInterface $fieldFactory = null)
{
$shortcut = strtolower($expression);
$expression = self::$registeredAliases[$shortcut] ?? $expression;
@ -200,7 +199,12 @@ class CronExpression
public function setExpression(string $value): CronExpression
{
$split = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
Assert::isArray($split);
if (!\is_array($split)) {
throw new InvalidArgumentException(
$value . ' is not a valid CRON expression'
);
}
$notEnoughParts = \count($split) < 5;
@ -334,7 +338,10 @@ class CronExpression
$currentTime = new DateTime($currentTime);
}
Assert::isInstanceOf($currentTime, DateTime::class);
if (!$currentTime instanceof DateTime) {
throw new InvalidArgumentException('invalid current time');
}
$currentTime->setTimezone(new DateTimeZone($timeZone));
$matches = [];
@ -420,7 +427,10 @@ class CronExpression
$currentTime = new DateTime($currentTime);
}
Assert::isInstanceOf($currentTime, DateTime::class);
if (!$currentTime instanceof DateTime) {
throw new InvalidArgumentException('invalid current time');
}
$currentTime->setTimezone(new DateTimeZone($timeZone));
// drop the seconds to 0
@ -462,7 +472,10 @@ class CronExpression
$currentDate = new DateTime('now');
}
Assert::isInstanceOf($currentDate, DateTime::class);
if (!$currentDate instanceof DateTime) {
throw new InvalidArgumentException('invalid current date');
}
$currentDate->setTimezone(new DateTimeZone($timeZone));
// Workaround for setTime causing an offset change: https://bugs.php.net/bug.php?id=81074
$currentDate = DateTime::createFromFormat("!Y-m-d H:iO", $currentDate->format("Y-m-d H:iP"), $currentDate->getTimezone());

View File

@ -1 +1 @@
4.17.0
4.18.0

View File

@ -13,7 +13,7 @@
}
],
"require": {
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"cerdic/css-tidy": "^1.7 || ^2.0",

View File

@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run.
*
* @version 4.17.0
* @version 4.18.0
*
* @warning
* You must *not* include any other HTML Purifier files before this file,
@ -101,6 +101,7 @@ require 'HTMLPurifier/AttrDef/CSS/Length.php';
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require 'HTMLPurifier/AttrDef/CSS/Ratio.php';
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require 'HTMLPurifier/AttrDef/CSS/URI.php';
require 'HTMLPurifier/AttrDef/HTML/Bool.php';

View File

@ -19,7 +19,7 @@
*/
/*
HTML Purifier 4.17.0 - Standards Compliant HTML Filtering
HTML Purifier 4.18.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or
@ -58,12 +58,12 @@ class HTMLPurifier
* Version of HTML Purifier.
* @type string
*/
public $version = '4.17.0';
public $version = '4.18.0';
/**
* Constant with version of HTML Purifier.
*/
const VERSION = '4.17.0';
const VERSION = '4.18.0';
/**
* Global configuration object.

View File

@ -95,6 +95,7 @@ require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Ratio.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';

View File

@ -27,6 +27,13 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$definition = $config->getCSSDefinition();
$allow_duplicates = $config->get("CSS.AllowDuplicates");
$universal_attrdef = new HTMLPurifier_AttrDef_Enum(
array(
'initial',
'inherit',
'unset',
)
);
// According to the CSS2.1 spec, the places where a
// non-delimiting semicolon can appear are in strings
@ -96,16 +103,13 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
if (!$ok) {
continue;
}
// inefficient call, since the validator will do this again
if (strtolower(trim($value)) !== 'inherit') {
// inherit works for everything (but only on the base property)
$result = $universal_attrdef->validate($value, $config, $context);
if ($result === false) {
$result = $definition->info[$property]->validate(
$value,
$config,
$context
);
} else {
$result = 'inherit';
}
if ($result === false) {
continue;

View File

@ -0,0 +1,46 @@
<?php
/**
* Validates a ratio as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_CSS_Ratio extends HTMLPurifier_AttrDef
{
/**
* @param string $ratio Ratio to validate
* @param HTMLPurifier_Config $config Configuration options
* @param HTMLPurifier_Context $context Context
*
* @return string|boolean
*
* @warning Some contexts do not pass $config, $context. These
* variables should not be used without checking HTMLPurifier_Length
*/
public function validate($ratio, $config, $context)
{
$ratio = $this->parseCDATA($ratio);
$parts = explode('/', $ratio, 2);
$length = count($parts);
if ($length < 1 || $length > 2) {
return false;
}
$num = new \HTMLPurifier_AttrDef_CSS_Number();
if ($length === 1) {
return $num->validate($parts[0], $config, $context);
}
$num1 = $num->validate($parts[0], $config, $context);
$num2 = $num->validate($parts[1], $config, $context);
if ($num1 === false || $num2 === false) {
return false;
}
return $num1 . '/' . $num2;
}
}
// vim: et sw=4 sts=4

View File

@ -63,24 +63,18 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
// This doesn't match I18N domain names, but we don't have proper IRI support,
// so force users to insert Punycode.
// There is not a good sense in which underscores should be
// allowed, since it's technically not! (And if you go as
// far to allow everything as specified by the DNS spec...
// well, that's literally everything, modulo some space limits
// for the components and the overall name (which, by the way,
// we are NOT checking!). So we (arbitrarily) decide this:
// let's allow underscores wherever we would have allowed
// hyphens, if they are enabled. This is a pretty good match
// for browser behavior, for example, a large number of browsers
// cannot handle foo_.example.com, but foo_bar.example.com is
// fairly well supported.
// Underscores defined as Unreserved Characters in RFC 3986 are
// allowed in a URI. There are cases where we want to consider a
// URI containing "_" such as "_dmarc.example.com".
// Underscores are not allowed in the default. If you want to
// allow it, set Core.AllowHostnameUnderscore to true.
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
// Based off of RFC 1738, but amended so that
// as per RFC 3696, the top label need only not be all numeric.
// The productions describing this are:
$a = '[a-z]'; // alpha
$an = '[a-z0-9]'; // alphanum
$an = "[a-z0-9$underscore]"; // alphanum
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
$domainlabel = "$an(?:$and*$an)?";

View File

@ -116,8 +116,6 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
'auto',
'cover',
'contain',
'initial',
'inherit',
]
),
new HTMLPurifier_AttrDef_CSS_Percentage(),
@ -236,21 +234,20 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
[
new HTMLPurifier_AttrDef_CSS_Length('0'),
new HTMLPurifier_AttrDef_CSS_Percentage(true),
new HTMLPurifier_AttrDef_Enum(['auto', 'initial', 'inherit'])
new HTMLPurifier_AttrDef_Enum(['auto'])
]
);
$trusted_min_wh = new HTMLPurifier_AttrDef_CSS_Composite(
[
new HTMLPurifier_AttrDef_CSS_Length('0'),
new HTMLPurifier_AttrDef_CSS_Percentage(true),
new HTMLPurifier_AttrDef_Enum(['initial', 'inherit'])
]
);
$trusted_max_wh = new HTMLPurifier_AttrDef_CSS_Composite(
[
new HTMLPurifier_AttrDef_CSS_Length('0'),
new HTMLPurifier_AttrDef_CSS_Percentage(true),
new HTMLPurifier_AttrDef_Enum(['none', 'initial', 'inherit'])
new HTMLPurifier_AttrDef_Enum(['none'])
]
);
$max = $config->get('CSS.MaxImgLength');
@ -278,12 +275,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
new HTMLPurifier_AttrDef_Switch(
'img',
// For img tags:
new HTMLPurifier_AttrDef_CSS_Composite(
[
new HTMLPurifier_AttrDef_CSS_Length('0', $max),
new HTMLPurifier_AttrDef_Enum(['initial', 'inherit'])
]
),
new HTMLPurifier_AttrDef_CSS_Length('0', $max),
// For everyone else:
$trusted_min_wh
);
@ -297,22 +289,29 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
new HTMLPurifier_AttrDef_CSS_Composite(
[
new HTMLPurifier_AttrDef_CSS_Length('0', $max),
new HTMLPurifier_AttrDef_Enum(['none', 'initial', 'inherit'])
new HTMLPurifier_AttrDef_Enum(['none'])
]
),
// For everyone else:
$trusted_max_wh
);
$this->info['aspect-ratio'] = new HTMLPurifier_AttrDef_CSS_Multiple(
new HTMLPurifier_AttrDef_CSS_Composite([
new HTMLPurifier_AttrDef_CSS_Ratio(),
new HTMLPurifier_AttrDef_Enum(['auto']),
])
);
// text-decoration and related shorthands
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
$this->info['text-decoration-line'] = new HTMLPurifier_AttrDef_Enum(
['none', 'underline', 'overline', 'line-through', 'initial', 'inherit']
['none', 'underline', 'overline', 'line-through']
);
$this->info['text-decoration-style'] = new HTMLPurifier_AttrDef_Enum(
['solid', 'double', 'dotted', 'dashed', 'wavy', 'initial', 'inherit']
['solid', 'double', 'dotted', 'dashed', 'wavy']
);
$this->info['text-decoration-color'] = new HTMLPurifier_AttrDef_CSS_Color();
@ -320,7 +319,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
$this->info['text-decoration-thickness'] = new HTMLPurifier_AttrDef_CSS_Composite([
new HTMLPurifier_AttrDef_CSS_Length(),
new HTMLPurifier_AttrDef_CSS_Percentage(),
new HTMLPurifier_AttrDef_Enum(['auto', 'from-font', 'initial', 'inherit'])
new HTMLPurifier_AttrDef_Enum(['auto', 'from-font'])
]);
$this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();

View File

@ -190,6 +190,9 @@ class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
$current_tr_tbody = null;
foreach($content as $node) {
if (!isset($node->name)) {
continue;
}
switch ($node->name) {
case 'tbody':
$current_tr_tbody = null;

View File

@ -21,7 +21,7 @@ class HTMLPurifier_Config
* HTML Purifier's version
* @type string
*/
public $version = '4.17.0';
public $version = '4.18.0';
/**
* Whether or not to automatically finalize

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
Core.RemoveBlanks
TYPE: bool
DEFAULT: false
VERSION: 4.18
--DESCRIPTION--
<p>
If set to true, blank nodes will be removed. This can be useful for maintaining
backwards compatibility when upgrading from previous versions of PHP.
</p>
--# vim: et sw=4 sts=4

View File

@ -116,8 +116,8 @@ class HTMLPurifier_EntityParser
protected function entityCallback($matches)
{
$entity = $matches[0];
$hex_part = @$matches[1];
$dec_part = @$matches[2];
$hex_part = isset($matches[1]) ? $matches[1] : null;
$dec_part = isset($matches[2]) ? $matches[2] : null;
$named_part = empty($matches[3]) ? (empty($matches[4]) ? "" : $matches[4]) : $matches[3];
if ($hex_part !== NULL && $hex_part !== "") {
return HTMLPurifier_Encoder::unichr(hexdec($hex_part));

View File

@ -54,6 +54,11 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
*/
private $_enum_attrdef;
/**
* @type HTMLPurifier_AttrDef_Enum
*/
private $_universal_attrdef;
public function __construct()
{
$this->_tidy = new csstidy();
@ -70,6 +75,13 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
'focus'
)
);
$this->_universal_attrdef = new HTMLPurifier_AttrDef_Enum(
array(
'initial',
'inherit',
'unset',
)
);
}
/**
@ -307,6 +319,11 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
unset($style[$name]);
continue;
}
$uni_ret = $this->_universal_attrdef->validate($value, $config, $context);
if ($uni_ret !== false) {
$style[$name] = $uni_ret;
continue;
}
$def = $css_definition->info[$name];
$ret = $def->validate($value, $config, $context);
if ($ret === false) {

View File

@ -28,22 +28,28 @@ class HTMLPurifier_HTMLModule_Iframe extends HTMLPurifier_HTMLModule
if ($config->get('HTML.SafeIframe')) {
$this->safe = true;
}
$attrs = array(
'src' => 'URI#embedded',
'width' => 'Length',
'height' => 'Length',
'name' => 'ID',
'scrolling' => 'Enum#yes,no,auto',
'frameborder' => 'Enum#0,1',
'longdesc' => 'URI',
'marginheight' => 'Pixels',
'marginwidth' => 'Pixels',
);
if ($config->get('HTML.Trusted')) {
$attrs['allowfullscreen'] = 'Bool#allowfullscreen';
}
$this->addElement(
'iframe',
'Inline',
'Flow',
'Common',
array(
'src' => 'URI#embedded',
'width' => 'Length',
'height' => 'Length',
'name' => 'ID',
'scrolling' => 'Enum#yes,no,auto',
'frameborder' => 'Enum#0,1',
'longdesc' => 'URI',
'marginheight' => 'Pixels',
'marginwidth' => 'Pixels',
)
$attrs
);
}
}

View File

@ -269,20 +269,6 @@ class HTMLPurifier_Lexer
);
}
/**
* Special Internet Explorer conditional comments should be removed.
* @param string $string HTML string to process.
* @return string HTML with conditional comments removed.
*/
protected static function removeIEConditional($string)
{
return preg_replace(
'#<!--\[if [^>]+\]>.*?<!\[endif\]-->#si', // probably should generalize for all strings
'',
$string
);
}
/**
* Callback function for escapeCDATA() that does the work.
*
@ -323,8 +309,6 @@ class HTMLPurifier_Lexer
// escape CDATA
$html = $this->escapeCDATA($html);
$html = $this->removeIEConditional($html);
// extract body from document if applicable
if ($config->get('Core.ConvertDocumentToFragment')) {
$e = false;

Some files were not shown because too many files have changed in this diff Show More