Add files via upload
This commit is contained in:
+97
@@ -0,0 +1,97 @@
|
||||
# Changelog
|
||||
|
||||
## 1.7.0 (2023-12-20)
|
||||
|
||||
* Feature: Full PHP 8.3 and PHP 8.2 compatibility.
|
||||
(#51 by @yadaiio and #50 by @clue)
|
||||
|
||||
* Feature / Fix: Improve error reporting when custom error handler is used.
|
||||
(#47 by @SimonFrings)
|
||||
|
||||
* Improve test suite and ensure 100% code coverage.
|
||||
(#46 by @SimonFrings, #48 and #50 by @clue and #51 by @yadaiio)
|
||||
|
||||
## 1.6.0 (2022-02-21)
|
||||
|
||||
* Feature: Support PHP 8.1 release.
|
||||
(#45 by @clue)
|
||||
|
||||
* Improve documentation to use fully-qualified function names.
|
||||
(#43 by @SimonFrings and #42 by @PaulRotmann)
|
||||
|
||||
* Improve test suite and use GitHub actions for continuous integration (CI).
|
||||
(#39 and #40 by @SimonFrings)
|
||||
|
||||
## 1.5.0 (2020-10-02)
|
||||
|
||||
* Feature: Improve performance by using global imports.
|
||||
(#38 by @clue)
|
||||
|
||||
* Improve API documentation and add support / sponsorship info.
|
||||
(#30 by @clue and #35 by @SimonFrings)
|
||||
|
||||
* Improve test suite and add `.gitattributes` to exclude dev files from exports.
|
||||
Prepare PHP 8 support, update to PHPUnit 9 and simplify test matrix.
|
||||
(#32 and #37 by @clue and #34 and #36 by @SimonFrings)
|
||||
|
||||
## 1.4.1 (2019-04-09)
|
||||
|
||||
* Fix: Check if the function is declared before declaring it.
|
||||
(#23 by @Niko9911)
|
||||
|
||||
* Improve test suite to also test against PHP 7.2 and
|
||||
add test for base64 encoding and decoding filters.
|
||||
(#22 by @arubacao and #25 by @Nyholm and @clue)
|
||||
|
||||
## 1.4.0 (2017-08-18)
|
||||
|
||||
* Feature / Fix: The `fun()` function does not pass filter parameter `null`
|
||||
to underlying `stream_filter_append()` by default
|
||||
(#15 by @Nyholm)
|
||||
|
||||
Certain filters (such as `convert.quoted-printable-encode`) do not accept
|
||||
a filter parameter at all. If no explicit filter parameter is given, we no
|
||||
longer pass a default `null` value.
|
||||
|
||||
```php
|
||||
$encode = Filter\fun('convert.quoted-printable-encode');
|
||||
assert('t=C3=A4st' === $encode('täst'));
|
||||
```
|
||||
|
||||
* Add examples and improve documentation
|
||||
(#13 and #20 by @clue and #18 by @Nyholm)
|
||||
|
||||
* Improve test suite by adding PHPUnit to require-dev,
|
||||
fix HHVM build for now again and ignore future HHVM build errors,
|
||||
lock Travis distro so new future defaults will not break the build
|
||||
and test on PHP 7.1
|
||||
(#12, #14 and #19 by @clue and #16 by @Nyholm)
|
||||
|
||||
## 1.3.0 (2015-11-08)
|
||||
|
||||
* Feature: Support accessing built-in filters as callbacks
|
||||
(#5 by @clue)
|
||||
|
||||
```php
|
||||
$fun = Filter\fun('zlib.deflate');
|
||||
|
||||
$ret = $fun('hello') . $fun('world') . $fun();
|
||||
assert('helloworld' === gzinflate($ret));
|
||||
```
|
||||
|
||||
## 1.2.0 (2015-10-23)
|
||||
|
||||
* Feature: Invoke close event when closing filter (flush buffer)
|
||||
(#9 by @clue)
|
||||
|
||||
## 1.1.0 (2015-10-22)
|
||||
|
||||
* Feature: Abort filter operation when catching an Exception
|
||||
(#10 by @clue)
|
||||
|
||||
* Feature: Additional safeguards to prevent filter state corruption
|
||||
(#7 by @clue)
|
||||
|
||||
## 1.0.0 (2015-10-18)
|
||||
|
||||
* First tagged release
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 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.
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
# clue/stream-filter
|
||||
|
||||
[](https://github.com/clue/stream-filter/actions)
|
||||
[](https://packagist.org/packages/clue/stream-filter)
|
||||
|
||||
A simple and modern approach to stream filtering in PHP
|
||||
|
||||
**Table of contents**
|
||||
|
||||
* [Why?](#why)
|
||||
* [Support us](#support-us)
|
||||
* [Usage](#usage)
|
||||
* [append()](#append)
|
||||
* [prepend()](#prepend)
|
||||
* [fun()](#fun)
|
||||
* [remove()](#remove)
|
||||
* [Install](#install)
|
||||
* [Tests](#tests)
|
||||
* [License](#license)
|
||||
|
||||
## Why?
|
||||
|
||||
PHP's stream filtering system is great!
|
||||
|
||||
It offers very powerful stream filtering options and comes with a useful set of built-in filters.
|
||||
These filters can be used to easily and efficiently perform various transformations on-the-fly, such as:
|
||||
|
||||
* read from a gzip'ed input file,
|
||||
* transcode from ISO-8859-1 (Latin1) to UTF-8,
|
||||
* write to a bzip output file
|
||||
* and much more.
|
||||
|
||||
But let's face it:
|
||||
Its API is [*difficult to work with*](https://www.php.net/manual/en/php-user-filter.filter.php)
|
||||
and its documentation is [*subpar*](https://stackoverflow.com/questions/27103269/what-is-a-bucket-brigade).
|
||||
This combined means its powerful features are often neglected.
|
||||
|
||||
This project aims to make these features more accessible to a broader audience.
|
||||
* **Lightweight, SOLID design** -
|
||||
Provides a thin abstraction that is [*just good enough*](https://en.wikipedia.org/wiki/Principle_of_good_enough)
|
||||
and does not get in your way.
|
||||
Custom filters require trivial effort.
|
||||
* **Good test coverage** -
|
||||
Comes with an automated tests suite and is regularly tested in the *real world*
|
||||
|
||||
## 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! 🚀
|
||||
|
||||
## Usage
|
||||
|
||||
This lightweight library consists only of a few simple functions.
|
||||
All functions reside under the `Clue\StreamFilter` namespace.
|
||||
|
||||
The below examples refer to all functions with their fully-qualified names like this:
|
||||
|
||||
```php
|
||||
Clue\StreamFilter\append(…);
|
||||
```
|
||||
|
||||
As of PHP 5.6+ you can also import each required function into your code like this:
|
||||
|
||||
```php
|
||||
use function Clue\StreamFilter\append;
|
||||
|
||||
append(…);
|
||||
```
|
||||
|
||||
Alternatively, you can also use an import statement similar to this:
|
||||
|
||||
```php
|
||||
use Clue\StreamFilter as Filter;
|
||||
|
||||
Filter\append(…);
|
||||
```
|
||||
|
||||
### append()
|
||||
|
||||
The `append(resource<stream> $stream, callable $callback, int $read_write = STREAM_FILTER_ALL): resource<stream filter>` function can be used to
|
||||
append a filter callback to the given stream.
|
||||
|
||||
Each stream can have a list of filters attached.
|
||||
This function appends a filter to the end of this list.
|
||||
|
||||
If the given filter can not be added, it throws an `Exception`.
|
||||
|
||||
The `$stream` can be any valid stream resource, such as:
|
||||
|
||||
```php
|
||||
$stream = fopen('demo.txt', 'w+');
|
||||
```
|
||||
|
||||
The `$callback` should be a valid callable function which accepts
|
||||
an individual chunk of data and should return the updated chunk:
|
||||
|
||||
```php
|
||||
$filter = Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
// will be called each time you read or write a $chunk to/from the stream
|
||||
return $chunk;
|
||||
});
|
||||
```
|
||||
|
||||
As such, you can also use native PHP functions or any other `callable`:
|
||||
|
||||
```php
|
||||
Clue\StreamFilter\append($stream, 'strtoupper');
|
||||
|
||||
// will write "HELLO" to the underlying stream
|
||||
fwrite($stream, 'hello');
|
||||
```
|
||||
|
||||
If the `$callback` accepts invocation without parameters,
|
||||
then this signature will be invoked once ending (flushing) the filter:
|
||||
|
||||
```php
|
||||
Clue\StreamFilter\append($stream, function ($chunk = null) {
|
||||
if ($chunk === null) {
|
||||
// will be called once ending the filter
|
||||
return 'end';
|
||||
}
|
||||
// will be called each time you read or write a $chunk to/from the stream
|
||||
return $chunk;
|
||||
});
|
||||
|
||||
fclose($stream);
|
||||
```
|
||||
|
||||
> Note: Legacy PHP versions (PHP < 5.4) do not support passing additional data
|
||||
from the end signal handler if the stream is being closed.
|
||||
|
||||
If your callback throws an `Exception`, then the filter process will be aborted.
|
||||
In order to play nice with PHP's stream handling,
|
||||
the `Exception` will be transformed to a PHP warning instead:
|
||||
|
||||
```php
|
||||
Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
throw new \RuntimeException('Unexpected chunk');
|
||||
});
|
||||
|
||||
// raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
|
||||
fwrite($stream, 'hello');
|
||||
```
|
||||
|
||||
The optional `$read_write` parameter can be used to only invoke the `$callback`
|
||||
when either writing to the stream or only when reading from the stream:
|
||||
|
||||
```php
|
||||
Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
// will be called each time you write to the stream
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
// will be called each time you read from the stream
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_READ);
|
||||
```
|
||||
|
||||
This function returns a filter resource which can be passed to [`remove()`](#remove).
|
||||
|
||||
> Note that once a filter has been added to stream, the stream can no longer be passed to
|
||||
> [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
|
||||
> (and family).
|
||||
>
|
||||
> > Warning: stream_select(): cannot cast a filtered stream on this system in {file} on line {line}
|
||||
>
|
||||
> This is due to limitations of PHP's stream filter support, as it can no longer reliably
|
||||
> tell when the underlying stream resource is actually ready.
|
||||
> As an alternative, consider calling `stream_select()` on the unfiltered stream and
|
||||
> then pass the unfiltered data through the [`fun()`](#fun) function.
|
||||
|
||||
### prepend()
|
||||
|
||||
The `prepend(resource<stream> $stream, callable $callback, int $read_write = STREAM_FILTER_ALL): resource<stream filter>` function can be used to
|
||||
prepend a filter callback to the given stream.
|
||||
|
||||
Each stream can have a list of filters attached.
|
||||
This function prepends a filter to the start of this list.
|
||||
|
||||
If the given filter can not be added, it throws an `Exception`.
|
||||
|
||||
```php
|
||||
$filter = Clue\StreamFilter\prepend($stream, function ($chunk) {
|
||||
// will be called each time you read or write a $chunk to/from the stream
|
||||
return $chunk;
|
||||
});
|
||||
```
|
||||
|
||||
This function returns a filter resource which can be passed to [`remove()`](#remove).
|
||||
|
||||
Except for the position in the list of filters, this function behaves exactly
|
||||
like the [`append()`](#append) function.
|
||||
For more details about its behavior, see also the [`append()`](#append) function.
|
||||
|
||||
### fun()
|
||||
|
||||
The `fun(string $filter, mixed $parameters = null): callable` function can be used to
|
||||
create a filter function which uses the given built-in `$filter`.
|
||||
|
||||
PHP comes with a useful set of [built-in filters](https://www.php.net/manual/en/filters.php).
|
||||
Using `fun()` makes accessing these as easy as passing an input string to filter
|
||||
and getting the filtered output string.
|
||||
|
||||
```php
|
||||
$fun = Clue\StreamFilter\fun('string.rot13');
|
||||
|
||||
assert('grfg' === $fun('test'));
|
||||
assert('test' === $fun($fun('test'));
|
||||
```
|
||||
|
||||
Please note that not all filter functions may be available depending
|
||||
on installed PHP extensions and the PHP version in use.
|
||||
In particular, [HHVM](https://hhvm.com/) may not offer the same filter functions
|
||||
or parameters as Zend PHP.
|
||||
Accessing an unknown filter function will result in a `RuntimeException`:
|
||||
|
||||
```php
|
||||
Clue\StreamFilter\fun('unknown'); // throws RuntimeException
|
||||
```
|
||||
|
||||
Some filters may accept or require additional filter parameters – most
|
||||
filters do not require filter parameters.
|
||||
If given, the optional `$parameters` argument will be passed to the
|
||||
underlying filter handler as-is.
|
||||
In particular, note how *not passing* this parameter at all differs from
|
||||
explicitly passing a `null` value (which many filters do not accept).
|
||||
Please refer to the individual filter definition for more details.
|
||||
For example, the `string.strip_tags` filter can be invoked like this:
|
||||
|
||||
```php
|
||||
$fun = Clue\StreamFilter\fun('string.strip_tags', '<a><b>');
|
||||
|
||||
$ret = $fun('<b>h<br>i</b>');
|
||||
assert('<b>hi</b>' === $ret);
|
||||
```
|
||||
|
||||
Under the hood, this function allocates a temporary memory stream, so it's
|
||||
recommended to clean up the filter function after use.
|
||||
Also, some filter functions (in particular the
|
||||
[zlib compression filters](https://www.php.net/manual/en/filters.compression.php))
|
||||
may use internal buffers and may emit a final data chunk on close.
|
||||
The filter function can be closed by invoking without any arguments:
|
||||
|
||||
```php
|
||||
$fun = Clue\StreamFilter\fun('zlib.deflate');
|
||||
|
||||
$ret = $fun('hello') . $fun('world') . $fun();
|
||||
assert('helloworld' === gzinflate($ret));
|
||||
```
|
||||
|
||||
The filter function must not be used anymore after it has been closed.
|
||||
Doing so will result in a `RuntimeException`:
|
||||
|
||||
```php
|
||||
$fun = Clue\StreamFilter\fun('string.rot13');
|
||||
$fun();
|
||||
|
||||
$fun('test'); // throws RuntimeException
|
||||
```
|
||||
|
||||
> Note: If you're using the zlib compression filters, then you should be wary
|
||||
about engine inconsistencies between different PHP versions and HHVM.
|
||||
These inconsistencies exist in the underlying PHP engines and there's little we
|
||||
can do about this in this library.
|
||||
[Our test suite](tests/) contains several test cases that exhibit these issues.
|
||||
If you feel some test case is missing or outdated, we're happy to accept PRs! :)
|
||||
|
||||
### remove()
|
||||
|
||||
The `remove(resource<stream filter> $filter): bool` function can be used to
|
||||
remove a filter previously added via [`append()`](#append) or [`prepend()`](#prepend).
|
||||
|
||||
```php
|
||||
$filter = Clue\StreamFilter\append($stream, function () {
|
||||
// …
|
||||
});
|
||||
Clue\StreamFilter\remove($filter);
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
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/stream-filter:^1.7
|
||||
```
|
||||
|
||||
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+ and
|
||||
HHVM.
|
||||
It's *highly recommended to use the latest supported PHP version* for this project.
|
||||
Older PHP versions may suffer from a number of inconsistencies documented above.
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
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.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "clue/stream-filter",
|
||||
"description": "A simple and modern approach to stream filtering in PHP",
|
||||
"keywords": ["stream", "callback", "filter", "php_user_filter", "stream_filter_append", "stream_filter_register", "bucket brigade"],
|
||||
"homepage": "https://github.com/clue/stream-filter",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"email": "christian@clue.engineering"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Clue\\StreamFilter\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Clue\\Tests\\StreamFilter\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\StreamFilter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @see append()
|
||||
* @see prepend()
|
||||
*/
|
||||
class CallbackFilter extends \php_user_filter
|
||||
{
|
||||
private $callback;
|
||||
private $closed = true;
|
||||
private $supportsClose = false;
|
||||
|
||||
/** @return bool */
|
||||
#[\ReturnTypeWillChange]
|
||||
public function onCreate()
|
||||
{
|
||||
$this->closed = false;
|
||||
|
||||
if (!\is_callable($this->params)) {
|
||||
throw new \InvalidArgumentException('No valid callback parameter given to stream_filter_(append|prepend)');
|
||||
}
|
||||
$this->callback = $this->params;
|
||||
|
||||
// callback supports end event if it accepts invocation without arguments
|
||||
$ref = new \ReflectionFunction($this->callback);
|
||||
$this->supportsClose = ($ref->getNumberOfRequiredParameters() === 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
#[\ReturnTypeWillChange]
|
||||
public function onClose()
|
||||
{
|
||||
$this->closed = true;
|
||||
|
||||
// callback supports closing and is not already closed
|
||||
if ($this->supportsClose) {
|
||||
$this->supportsClose = false;
|
||||
// invoke without argument to signal end and discard resulting buffer
|
||||
try {
|
||||
\call_user_func($this->callback);
|
||||
} catch (\Exception $ignored) {
|
||||
// this might be called during engine shutdown, so it's not safe
|
||||
// to raise any errors or exceptions here
|
||||
// trigger_error('Error closing filter: ' . $ignored->getMessage(), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
$this->callback = null;
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
#[\ReturnTypeWillChange]
|
||||
public function filter($in, $out, &$consumed, $closing)
|
||||
{
|
||||
// concatenate whole buffer from input brigade
|
||||
$data = '';
|
||||
while ($bucket = \stream_bucket_make_writeable($in)) {
|
||||
$consumed += $bucket->datalen;
|
||||
$data .= $bucket->data;
|
||||
}
|
||||
|
||||
// skip processing callback that already ended
|
||||
if ($this->closed) {
|
||||
return \PSFS_FEED_ME;
|
||||
}
|
||||
|
||||
// only invoke filter function if buffer is not empty
|
||||
// this may skip flushing a closing filter
|
||||
if ($data !== '') {
|
||||
try {
|
||||
$data = \call_user_func($this->callback, $data);
|
||||
} catch (\Exception $e) {
|
||||
// exception should mark filter as closed
|
||||
$this->onClose();
|
||||
\trigger_error('Error invoking filter: ' . $e->getMessage(), \E_USER_WARNING);
|
||||
|
||||
return \PSFS_ERR_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
// mark filter as closed after processing closing chunk
|
||||
if ($closing) {
|
||||
$this->closed = true;
|
||||
|
||||
// callback supports closing and is not already closed
|
||||
if ($this->supportsClose) {
|
||||
$this->supportsClose = false;
|
||||
|
||||
// invoke without argument to signal end and append resulting buffer
|
||||
try {
|
||||
$data .= \call_user_func($this->callback);
|
||||
} catch (\Exception $e) {
|
||||
\trigger_error('Error ending filter: ' . $e->getMessage(), \E_USER_WARNING);
|
||||
|
||||
return \PSFS_ERR_FATAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($data !== '') {
|
||||
// create a new bucket for writing the resulting buffer to the output brigade
|
||||
// reusing an existing bucket turned out to be bugged in some environments (ancient PHP versions and HHVM)
|
||||
$bucket = @\stream_bucket_new($this->stream, $data);
|
||||
|
||||
// legacy PHP versions (PHP < 5.4) do not support passing data from the event signal handler
|
||||
// because closing the stream invalidates the stream and its stream bucket brigade before
|
||||
// invoking the filter close handler.
|
||||
if ($bucket !== false) {
|
||||
\stream_bucket_append($out, $bucket);
|
||||
}
|
||||
}
|
||||
|
||||
return \PSFS_PASS_ON;
|
||||
}
|
||||
}
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\StreamFilter;
|
||||
|
||||
/**
|
||||
* Append a filter callback to the given stream.
|
||||
*
|
||||
* Each stream can have a list of filters attached.
|
||||
* This function appends a filter to the end of this list.
|
||||
*
|
||||
* If the given filter can not be added, it throws an `Exception`.
|
||||
*
|
||||
* The `$stream` can be any valid stream resource, such as:
|
||||
*
|
||||
* ```php
|
||||
* $stream = fopen('demo.txt', 'w+');
|
||||
* ```
|
||||
*
|
||||
* The `$callback` should be a valid callable function which accepts
|
||||
* an individual chunk of data and should return the updated chunk:
|
||||
*
|
||||
* ```php
|
||||
* $filter = Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
* // will be called each time you read or write a $chunk to/from the stream
|
||||
* return $chunk;
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* As such, you can also use native PHP functions or any other `callable`:
|
||||
*
|
||||
* ```php
|
||||
* Clue\StreamFilter\append($stream, 'strtoupper');
|
||||
*
|
||||
* // will write "HELLO" to the underlying stream
|
||||
* fwrite($stream, 'hello');
|
||||
* ```
|
||||
*
|
||||
* If the `$callback` accepts invocation without parameters,
|
||||
* then this signature will be invoked once ending (flushing) the filter:
|
||||
*
|
||||
* ```php
|
||||
* Clue\StreamFilter\append($stream, function ($chunk = null) {
|
||||
* if ($chunk === null) {
|
||||
* // will be called once ending the filter
|
||||
* return 'end';
|
||||
* }
|
||||
* // will be called each time you read or write a $chunk to/from the stream
|
||||
* return $chunk;
|
||||
* });
|
||||
*
|
||||
* fclose($stream);
|
||||
* ```
|
||||
*
|
||||
* > Note: Legacy PHP versions (PHP < 5.4) do not support passing additional data
|
||||
* from the end signal handler if the stream is being closed.
|
||||
*
|
||||
* If your callback throws an `Exception`, then the filter process will be aborted.
|
||||
* In order to play nice with PHP's stream handling,
|
||||
* the `Exception` will be transformed to a PHP warning instead:
|
||||
*
|
||||
* ```php
|
||||
* Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
* throw new \RuntimeException('Unexpected chunk');
|
||||
* });
|
||||
*
|
||||
* // raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
|
||||
* fwrite($stream, 'hello');
|
||||
* ```
|
||||
*
|
||||
* The optional `$read_write` parameter can be used to only invoke the `$callback`
|
||||
* when either writing to the stream or only when reading from the stream:
|
||||
*
|
||||
* ```php
|
||||
* Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
* // will be called each time you write to the stream
|
||||
* return $chunk;
|
||||
* }, STREAM_FILTER_WRITE);
|
||||
*
|
||||
* Clue\StreamFilter\append($stream, function ($chunk) {
|
||||
* // will be called each time you read from the stream
|
||||
* return $chunk;
|
||||
* }, STREAM_FILTER_READ);
|
||||
* ```
|
||||
*
|
||||
* This function returns a filter resource which can be passed to [`remove()`](#remove).
|
||||
*
|
||||
* > Note that once a filter has been added to stream, the stream can no longer be passed to
|
||||
* > [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
|
||||
* > (and family).
|
||||
* >
|
||||
* > > Warning: stream_select(): cannot cast a filtered stream on this system in {file} on line {line}
|
||||
* >
|
||||
* > This is due to limitations of PHP's stream filter support, as it can no longer reliably
|
||||
* > tell when the underlying stream resource is actually ready.
|
||||
* > As an alternative, consider calling `stream_select()` on the unfiltered stream and
|
||||
* > then pass the unfiltered data through the [`fun()`](#fun) function.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param callable $callback
|
||||
* @param int $read_write
|
||||
* @return resource filter resource which can be used for `remove()`
|
||||
* @throws \Exception on error
|
||||
* @uses stream_filter_append()
|
||||
*/
|
||||
function append($stream, $callback, $read_write = STREAM_FILTER_ALL)
|
||||
{
|
||||
$errstr = '';
|
||||
\set_error_handler(function ($_, $error) use (&$errstr) {
|
||||
// Match errstr from PHP's warning message.
|
||||
// stream_filter_append() expects parameter 1 to be resource,...
|
||||
$errstr = $error; // @codeCoverageIgnore
|
||||
});
|
||||
|
||||
try {
|
||||
$ret = \stream_filter_append($stream, register(), $read_write, $callback);
|
||||
} catch (\TypeError $e) { // @codeCoverageIgnoreStart
|
||||
// Throws TypeError on PHP 8.0+
|
||||
\restore_error_handler();
|
||||
throw $e;
|
||||
} // @codeCoverageIgnoreEnd
|
||||
|
||||
\restore_error_handler();
|
||||
|
||||
// PHP 8 throws above on type errors, older PHP and memory issues can throw here
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($ret === false) {
|
||||
throw new \RuntimeException('Unable to append filter: ' . $errstr);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a filter callback to the given stream.
|
||||
*
|
||||
* Each stream can have a list of filters attached.
|
||||
* This function prepends a filter to the start of this list.
|
||||
*
|
||||
* If the given filter can not be added, it throws an `Exception`.
|
||||
*
|
||||
* ```php
|
||||
* $filter = Clue\StreamFilter\prepend($stream, function ($chunk) {
|
||||
* // will be called each time you read or write a $chunk to/from the stream
|
||||
* return $chunk;
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* This function returns a filter resource which can be passed to [`remove()`](#remove).
|
||||
*
|
||||
* Except for the position in the list of filters, this function behaves exactly
|
||||
* like the [`append()`](#append) function.
|
||||
* For more details about its behavior, see also the [`append()`](#append) function.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param callable $callback
|
||||
* @param int $read_write
|
||||
* @return resource filter resource which can be used for `remove()`
|
||||
* @throws \Exception on error
|
||||
* @uses stream_filter_prepend()
|
||||
*/
|
||||
function prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)
|
||||
{
|
||||
$errstr = '';
|
||||
\set_error_handler(function ($_, $error) use (&$errstr) {
|
||||
// Match errstr from PHP's warning message.
|
||||
// stream_filter_prepend() expects parameter 1 to be resource,...
|
||||
$errstr = $error; // @codeCoverageIgnore
|
||||
});
|
||||
|
||||
try {
|
||||
$ret = \stream_filter_prepend($stream, register(), $read_write, $callback);
|
||||
} catch (\TypeError $e) { // @codeCoverageIgnoreStart
|
||||
// Throws TypeError on PHP 8.0+
|
||||
\restore_error_handler();
|
||||
throw $e;
|
||||
} // @codeCoverageIgnoreEnd
|
||||
|
||||
\restore_error_handler();
|
||||
|
||||
// PHP 8 throws above on type errors, older PHP and memory issues can throw here
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($ret === false) {
|
||||
throw new \RuntimeException('Unable to prepend filter: ' . $errstr);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a filter function which uses the given built-in `$filter`.
|
||||
*
|
||||
* PHP comes with a useful set of [built-in filters](https://www.php.net/manual/en/filters.php).
|
||||
* Using `fun()` makes accessing these as easy as passing an input string to filter
|
||||
* and getting the filtered output string.
|
||||
*
|
||||
* ```php
|
||||
* $fun = Clue\StreamFilter\fun('string.rot13');
|
||||
*
|
||||
* assert('grfg' === $fun('test'));
|
||||
* assert('test' === $fun($fun('test'));
|
||||
* ```
|
||||
*
|
||||
* Please note that not all filter functions may be available depending
|
||||
* on installed PHP extensions and the PHP version in use.
|
||||
* In particular, [HHVM](https://hhvm.com/) may not offer the same filter functions
|
||||
* or parameters as Zend PHP.
|
||||
* Accessing an unknown filter function will result in a `RuntimeException`:
|
||||
*
|
||||
* ```php
|
||||
* Clue\StreamFilter\fun('unknown'); // throws RuntimeException
|
||||
* ```
|
||||
*
|
||||
* Some filters may accept or require additional filter parameters – most
|
||||
* filters do not require filter parameters.
|
||||
* If given, the optional `$parameters` argument will be passed to the
|
||||
* underlying filter handler as-is.
|
||||
* In particular, note how *not passing* this parameter at all differs from
|
||||
* explicitly passing a `null` value (which many filters do not accept).
|
||||
* Please refer to the individual filter definition for more details.
|
||||
* For example, the `string.strip_tags` filter can be invoked like this:
|
||||
*
|
||||
* ```php
|
||||
* $fun = Clue\StreamFilter\fun('string.strip_tags', '<a><b>');
|
||||
*
|
||||
* $ret = $fun('<b>h<br>i</b>');
|
||||
* assert('<b>hi</b>' === $ret);
|
||||
* ```
|
||||
*
|
||||
* Under the hood, this function allocates a temporary memory stream, so it's
|
||||
* recommended to clean up the filter function after use.
|
||||
* Also, some filter functions (in particular the
|
||||
* [zlib compression filters](https://www.php.net/manual/en/filters.compression.php))
|
||||
* may use internal buffers and may emit a final data chunk on close.
|
||||
* The filter function can be closed by invoking without any arguments:
|
||||
*
|
||||
* ```php
|
||||
* $fun = Clue\StreamFilter\fun('zlib.deflate');
|
||||
*
|
||||
* $ret = $fun('hello') . $fun('world') . $fun();
|
||||
* assert('helloworld' === gzinflate($ret));
|
||||
* ```
|
||||
*
|
||||
* The filter function must not be used anymore after it has been closed.
|
||||
* Doing so will result in a `RuntimeException`:
|
||||
*
|
||||
* ```php
|
||||
* $fun = Clue\StreamFilter\fun('string.rot13');
|
||||
* $fun();
|
||||
*
|
||||
* $fun('test'); // throws RuntimeException
|
||||
* ```
|
||||
*
|
||||
* > Note: If you're using the zlib compression filters, then you should be wary
|
||||
* about engine inconsistencies between different PHP versions and HHVM.
|
||||
* These inconsistencies exist in the underlying PHP engines and there's little we
|
||||
* can do about this in this library.
|
||||
* [Our test suite](tests/) contains several test cases that exhibit these issues.
|
||||
* If you feel some test case is missing or outdated, we're happy to accept PRs! :)
|
||||
*
|
||||
* @param string $filter built-in filter name. See stream_get_filters() or http://php.net/manual/en/filters.php
|
||||
* @param mixed $parameters (optional) parameters to pass to the built-in filter as-is
|
||||
* @return callable a filter callback which can be append()'ed or prepend()'ed
|
||||
* @throws \RuntimeException on error
|
||||
* @link http://php.net/manual/en/filters.php
|
||||
* @see stream_get_filters()
|
||||
* @see append()
|
||||
*/
|
||||
function fun($filter, $parameters = null)
|
||||
{
|
||||
$fp = \fopen('php://memory', 'w');
|
||||
|
||||
$errstr = '';
|
||||
\set_error_handler(function ($_, $error) use (&$errstr) {
|
||||
// Match errstr from PHP's warning message.
|
||||
// stream_filter_append() expects parameter 1 to be resource,...
|
||||
$errstr = $error;
|
||||
});
|
||||
|
||||
if (\func_num_args() === 1) {
|
||||
$filter = \stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE);
|
||||
} else {
|
||||
$filter = \stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE, $parameters);
|
||||
}
|
||||
|
||||
\restore_error_handler();
|
||||
|
||||
if ($filter === false) {
|
||||
\fclose($fp);
|
||||
throw new \RuntimeException('Unable to access built-in filter: ' . $errstr);
|
||||
}
|
||||
|
||||
// append filter function which buffers internally
|
||||
$buffer = '';
|
||||
append($fp, function ($chunk) use (&$buffer) {
|
||||
$buffer .= $chunk;
|
||||
|
||||
// always return empty string in order to skip actually writing to stream resource
|
||||
return '';
|
||||
}, \STREAM_FILTER_WRITE);
|
||||
|
||||
$closed = false;
|
||||
|
||||
return function ($chunk = null) use ($fp, $filter, &$buffer, &$closed) {
|
||||
if ($closed) {
|
||||
throw new \RuntimeException('Unable to perform operation on closed stream');
|
||||
}
|
||||
if ($chunk === null) {
|
||||
$closed = true;
|
||||
$buffer = '';
|
||||
\fclose($fp);
|
||||
return $buffer;
|
||||
}
|
||||
// initialize buffer and invoke filters by attempting to write to stream
|
||||
$buffer = '';
|
||||
\fwrite($fp, $chunk);
|
||||
|
||||
// buffer now contains everything the filter function returned
|
||||
return $buffer;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a filter previously added via `append()` or `prepend()`.
|
||||
*
|
||||
* ```php
|
||||
* $filter = Clue\StreamFilter\append($stream, function () {
|
||||
* // …
|
||||
* });
|
||||
* Clue\StreamFilter\remove($filter);
|
||||
* ```
|
||||
*
|
||||
* @param resource $filter
|
||||
* @return bool true on success or false on error
|
||||
* @throws \RuntimeException on error
|
||||
* @uses stream_filter_remove()
|
||||
*/
|
||||
function remove($filter)
|
||||
{
|
||||
$errstr = '';
|
||||
\set_error_handler(function ($_, $error) use (&$errstr) {
|
||||
// Match errstr from PHP's warning message.
|
||||
// stream_filter_remove() expects parameter 1 to be resource,...
|
||||
$errstr = $error;
|
||||
});
|
||||
|
||||
try {
|
||||
$ret = \stream_filter_remove($filter);
|
||||
} catch (\TypeError $e) { // @codeCoverageIgnoreStart
|
||||
// Throws TypeError on PHP 8.0+
|
||||
\restore_error_handler();
|
||||
throw $e;
|
||||
} // @codeCoverageIgnoreEnd
|
||||
|
||||
\restore_error_handler();
|
||||
|
||||
if ($ret === false) {
|
||||
// PHP 8 throws above on type errors, older PHP and memory issues can throw here
|
||||
throw new \RuntimeException('Unable to remove filter: ' . $errstr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the callback filter and returns the resulting filter name
|
||||
*
|
||||
* There should be little reason to call this function manually.
|
||||
*
|
||||
* @return string filter name
|
||||
* @uses CallbackFilter
|
||||
*/
|
||||
function register()
|
||||
{
|
||||
static $registered = null;
|
||||
if ($registered === null) {
|
||||
$registered = 'stream-callback';
|
||||
\stream_filter_register($registered, __NAMESPACE__ . '\CallbackFilter');
|
||||
}
|
||||
return $registered;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\StreamFilter;
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!\function_exists(__NAMESPACE__ . '\\append')) {
|
||||
require __DIR__ . '/functions.php';
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
Vendored
+579
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<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>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
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;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @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[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
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 false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @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[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @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 __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array()) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
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.
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'9c67151ae59aff4788964ce8eb2a0f43' => $vendorDir . '/clue/stream-filter/src/functions_include.php',
|
||||
'8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||
'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
|
||||
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
|
||||
'InfluxDB2\\' => array($vendorDir . '/influxdata/influxdb-client-php/src/InfluxDB2'),
|
||||
'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
|
||||
'Http\\Message\\' => array($vendorDir . '/php-http/message/src'),
|
||||
'Http\\Discovery\\' => array($vendorDir . '/php-http/discovery/src'),
|
||||
'Http\\Client\\Common\\' => array($vendorDir . '/php-http/client-common/src'),
|
||||
'Http\\Client\\' => array($vendorDir . '/php-http/httplug/src'),
|
||||
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
|
||||
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
|
||||
'Clue\\StreamFilter\\' => array($vendorDir . '/clue/stream-filter/src'),
|
||||
);
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitc6b2a118695e0873d12f3e5a49047b2e
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitc6b2a118695e0873d12f3e5a49047b2e', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitc6b2a118695e0873d12f3e5a49047b2e', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitc6b2a118695e0873d12f3e5a49047b2e::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInitc6b2a118695e0873d12f3e5a49047b2e::$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);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitc6b2a118695e0873d12f3e5a49047b2e
|
||||
{
|
||||
public static $files = array (
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'9c67151ae59aff4788964ce8eb2a0f43' => __DIR__ . '/..' . '/clue/stream-filter/src/functions_include.php',
|
||||
'8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||
'Symfony\\Component\\OptionsResolver\\' => 34,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
'Psr\\Http\\Client\\' => 16,
|
||||
),
|
||||
'I' =>
|
||||
array (
|
||||
'InfluxDB2\\' => 10,
|
||||
),
|
||||
'H' =>
|
||||
array (
|
||||
'Http\\Promise\\' => 13,
|
||||
'Http\\Message\\' => 13,
|
||||
'Http\\Discovery\\' => 15,
|
||||
'Http\\Client\\Common\\' => 19,
|
||||
'Http\\Client\\' => 12,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Psr7\\' => 16,
|
||||
'GuzzleHttp\\Promise\\' => 19,
|
||||
'GuzzleHttp\\' => 11,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'Clue\\StreamFilter\\' => 18,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Symfony\\Polyfill\\Php80\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||
),
|
||||
'Symfony\\Component\\OptionsResolver\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/options-resolver',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
1 => __DIR__ . '/..' . '/psr/http-factory/src',
|
||||
),
|
||||
'Psr\\Http\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-client/src',
|
||||
),
|
||||
'InfluxDB2\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/influxdata/influxdb-client-php/src/InfluxDB2',
|
||||
),
|
||||
'Http\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/promise/src',
|
||||
),
|
||||
'Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/message/src',
|
||||
),
|
||||
'Http\\Discovery\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/discovery/src',
|
||||
),
|
||||
'Http\\Client\\Common\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/client-common/src',
|
||||
),
|
||||
'Http\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/httplug/src',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||
),
|
||||
'GuzzleHttp\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
|
||||
),
|
||||
'GuzzleHttp\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
|
||||
),
|
||||
'Clue\\StreamFilter\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/stream-filter/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitc6b2a118695e0873d12f3e5a49047b2e::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitc6b2a118695e0873d12f3e5a49047b2e::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitc6b2a118695e0873d12f3e5a49047b2e::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Vendored
+1244
File diff suppressed because it is too large
Load Diff
Vendored
+215
@@ -0,0 +1,215 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'clue/stream-filter' => array(
|
||||
'pretty_version' => 'v1.7.0',
|
||||
'version' => '1.7.0.0',
|
||||
'reference' => '049509fef80032cb3f051595029ab75b49a3c2f7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../clue/stream-filter',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/guzzle' => array(
|
||||
'pretty_version' => '7.8.1',
|
||||
'version' => '7.8.1.0',
|
||||
'reference' => '41042bc7ab002487b876a0683fc8dce04ddce104',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/promises' => array(
|
||||
'pretty_version' => '2.0.2',
|
||||
'version' => '2.0.2.0',
|
||||
'reference' => 'bbff78d96034045e58e13dedd6ad91b5d1253223',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/psr7' => array(
|
||||
'pretty_version' => '2.6.2',
|
||||
'version' => '2.6.2.0',
|
||||
'reference' => '45b30f99ac27b5ca93cb4831afe16285f57b8221',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'influxdata/influxdb-client-php' => array(
|
||||
'pretty_version' => '3.4.0',
|
||||
'version' => '3.4.0.0',
|
||||
'reference' => '9db0d74595cbca08d9e2ce59814fc967c1b36531',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../influxdata/influxdb-client-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-http/async-client-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'php-http/client-common' => array(
|
||||
'pretty_version' => '2.7.1',
|
||||
'version' => '2.7.1.0',
|
||||
'reference' => '1e19c059b0e4d5f717bf5d524d616165aeab0612',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-http/client-common',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-http/client-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'php-http/discovery' => array(
|
||||
'pretty_version' => '1.19.2',
|
||||
'version' => '1.19.2.0',
|
||||
'reference' => '61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb',
|
||||
'type' => 'composer-plugin',
|
||||
'install_path' => __DIR__ . '/../php-http/discovery',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-http/httplug' => array(
|
||||
'pretty_version' => '2.4.0',
|
||||
'version' => '2.4.0.0',
|
||||
'reference' => '625ad742c360c8ac580fcc647a1541d29e257f67',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-http/httplug',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-http/message' => array(
|
||||
'pretty_version' => '1.16.0',
|
||||
'version' => '1.16.0.0',
|
||||
'reference' => '47a14338bf4ebd67d317bf1144253d7db4ab55fd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-http/message',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-http/message-factory-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
),
|
||||
),
|
||||
'php-http/promise' => array(
|
||||
'pretty_version' => '1.3.0',
|
||||
'version' => '1.3.0.0',
|
||||
'reference' => '2916a606d3b390f4e9e8e2b8dd68581508be0f07',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-http/promise',
|
||||
'aliases' => array(),
|
||||
'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(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-client-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '*',
|
||||
1 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/http-factory' => array(
|
||||
'pretty_version' => '1.0.2',
|
||||
'version' => '1.0.2.0',
|
||||
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-factory',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-factory-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '*',
|
||||
1 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/http-message' => array(
|
||||
'pretty_version' => '2.0',
|
||||
'version' => '2.0.0.0',
|
||||
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-message',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-message-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '*',
|
||||
1 => '1.0',
|
||||
),
|
||||
),
|
||||
'ralouphie/getallheaders' => array(
|
||||
'pretty_version' => '3.0.3',
|
||||
'version' => '3.0.3.0',
|
||||
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v3.4.0',
|
||||
'version' => '3.4.0.0',
|
||||
'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/options-resolver' => array(
|
||||
'pretty_version' => 'v7.0.0',
|
||||
'version' => '7.0.0.0',
|
||||
'reference' => '700ff4096e346f54cb628ea650767c8130f1001f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/options-resolver',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.28.0',
|
||||
'version' => '1.28.0.0',
|
||||
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 80200)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
The changelog is maintained for all Symfony contracts at the following URL:
|
||||
https://github.com/symfony/contracts/blob/main/CHANGELOG.md
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020-present Fabien Potencier
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,26 @@
|
||||
Symfony Deprecation Contracts
|
||||
=============================
|
||||
|
||||
A generic function and convention to trigger deprecation notices.
|
||||
|
||||
This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices.
|
||||
|
||||
By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component,
|
||||
the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments.
|
||||
|
||||
The function requires at least 3 arguments:
|
||||
- the name of the Composer package that is triggering the deprecation
|
||||
- the version of the package that introduced the deprecation
|
||||
- the message of the deprecation
|
||||
- more arguments can be provided: they will be inserted in the message using `printf()` formatting
|
||||
|
||||
Example:
|
||||
```php
|
||||
trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin');
|
||||
```
|
||||
|
||||
This will generate the following message:
|
||||
`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.`
|
||||
|
||||
While not recommended, the deprecation notices can be completely ignored by declaring an empty
|
||||
`function trigger_deprecation() {}` in your application.
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"type": "library",
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"function.php"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.4-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (!function_exists('trigger_deprecation')) {
|
||||
/**
|
||||
* Triggers a silenced deprecation notice.
|
||||
*
|
||||
* @param string $package The name of the Composer package that is triggering the deprecation
|
||||
* @param string $version The version of the package that introduced the deprecation
|
||||
* @param string $message The message of the deprecation
|
||||
* @param mixed ...$args Values to insert in the message using printf() formatting
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void
|
||||
{
|
||||
@trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Improve message with full path on invalid type in nested option
|
||||
|
||||
6.3
|
||||
---
|
||||
|
||||
* Add `OptionsResolver::setIgnoreUndefined()` and `OptionConfigurator::ignoreUndefined()` to ignore not defined options while resolving
|
||||
|
||||
6.0
|
||||
---
|
||||
|
||||
* Remove `OptionsResolverIntrospector::getDeprecationMessage()`
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* Add prototype definition for nested options
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* added fluent configuration of options using `OptionResolver::define()`
|
||||
* added `setInfo()` and `getInfo()` methods
|
||||
* updated the signature of method `OptionsResolver::setDeprecated()` to `OptionsResolver::setDeprecation(string $option, string $package, string $version, $message)`
|
||||
* deprecated `OptionsResolverIntrospector::getDeprecationMessage()`, use `OptionsResolverIntrospector::getDeprecation()` instead
|
||||
|
||||
5.0.0
|
||||
-----
|
||||
|
||||
* added argument `$triggerDeprecation` to `OptionsResolver::offsetGet()`
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* added `OptionsResolver::addNormalizer` method
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added support for nested options definition
|
||||
* added `setDeprecated` and `isDeprecated` methods
|
||||
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* added `OptionsResolverIntrospector` to inspect options definitions inside an `OptionsResolver` instance
|
||||
* added array of types support in allowed types (e.g int[])
|
||||
|
||||
2.6.0
|
||||
-----
|
||||
|
||||
* deprecated OptionsResolverInterface
|
||||
* [BC BREAK] removed "array" type hint from OptionsResolverInterface methods
|
||||
setRequired(), setAllowedValues(), addAllowedValues(), setAllowedTypes() and
|
||||
addAllowedTypes()
|
||||
* added OptionsResolver::setDefault()
|
||||
* added OptionsResolver::hasDefault()
|
||||
* added OptionsResolver::setNormalizer()
|
||||
* added OptionsResolver::isRequired()
|
||||
* added OptionsResolver::getRequiredOptions()
|
||||
* added OptionsResolver::isMissing()
|
||||
* added OptionsResolver::getMissingOptions()
|
||||
* added OptionsResolver::setDefined()
|
||||
* added OptionsResolver::isDefined()
|
||||
* added OptionsResolver::getDefinedOptions()
|
||||
* added OptionsResolver::remove()
|
||||
* added OptionsResolver::clear()
|
||||
* deprecated OptionsResolver::replaceDefaults()
|
||||
* deprecated OptionsResolver::setOptional() in favor of setDefined()
|
||||
* deprecated OptionsResolver::isKnown() in favor of isDefined()
|
||||
* [BC BREAK] OptionsResolver::isRequired() returns true now if a required
|
||||
option has a default value set
|
||||
* [BC BREAK] merged Options into OptionsResolver and turned Options into an
|
||||
interface
|
||||
* deprecated Options::overload() (now in OptionsResolver)
|
||||
* deprecated Options::set() (now in OptionsResolver)
|
||||
* deprecated Options::get() (now in OptionsResolver)
|
||||
* deprecated Options::has() (now in OptionsResolver)
|
||||
* deprecated Options::replace() (now in OptionsResolver)
|
||||
* [BC BREAK] Options::get() (now in OptionsResolver) can only be used within
|
||||
lazy option/normalizer closures now
|
||||
* [BC BREAK] removed Traversable interface from Options since using within
|
||||
lazy option/normalizer closures resulted in exceptions
|
||||
* [BC BREAK] removed Options::all() since using within lazy option/normalizer
|
||||
closures resulted in exceptions
|
||||
* [BC BREAK] OptionDefinitionException now extends LogicException instead of
|
||||
RuntimeException
|
||||
* [BC BREAK] normalizers are not executed anymore for unset options
|
||||
* normalizers are executed after validating the options now
|
||||
* [BC BREAK] an UndefinedOptionsException is now thrown instead of an
|
||||
InvalidOptionsException when non-existing options are passed
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Debug;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
|
||||
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class OptionsResolverIntrospector
|
||||
{
|
||||
private \Closure $get;
|
||||
|
||||
public function __construct(OptionsResolver $optionsResolver)
|
||||
{
|
||||
$this->get = \Closure::bind(function ($property, $option, $message) {
|
||||
/** @var OptionsResolver $this */
|
||||
if (!$this->isDefined($option)) {
|
||||
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option));
|
||||
}
|
||||
|
||||
if (!\array_key_exists($option, $this->{$property})) {
|
||||
throw new NoConfigurationException($message);
|
||||
}
|
||||
|
||||
return $this->{$property}[$option];
|
||||
}, $optionsResolver, $optionsResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoConfigurationException on no configured value
|
||||
*/
|
||||
public function getDefault(string $option): mixed
|
||||
{
|
||||
return ($this->get)('defaults', $option, sprintf('No default value was set for the "%s" option.', $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Closure[]
|
||||
*
|
||||
* @throws NoConfigurationException on no configured closures
|
||||
*/
|
||||
public function getLazyClosures(string $option): array
|
||||
{
|
||||
return ($this->get)('lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*
|
||||
* @throws NoConfigurationException on no configured types
|
||||
*/
|
||||
public function getAllowedTypes(string $option): array
|
||||
{
|
||||
return ($this->get)('allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*
|
||||
* @throws NoConfigurationException on no configured values
|
||||
*/
|
||||
public function getAllowedValues(string $option): array
|
||||
{
|
||||
return ($this->get)('allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoConfigurationException on no configured normalizer
|
||||
*/
|
||||
public function getNormalizer(string $option): \Closure
|
||||
{
|
||||
return current($this->getNormalizers($option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoConfigurationException when no normalizer is configured
|
||||
*/
|
||||
public function getNormalizers(string $option): array
|
||||
{
|
||||
return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoConfigurationException on no configured deprecation
|
||||
*/
|
||||
public function getDeprecation(string $option): array
|
||||
{
|
||||
return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when trying to read an option outside of or write it inside of
|
||||
* {@link \Symfony\Component\OptionsResolver\Options::resolve()}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class AccessException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Marker interface for all exceptions thrown by the OptionsResolver component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when an argument is invalid.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when the value of an option does not match its validation rules.
|
||||
*
|
||||
* You should make sure a valid value is passed to the option.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidOptionsException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a required option is missing.
|
||||
*
|
||||
* Add the option to the passed options array.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class MissingOptionsException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
|
||||
|
||||
/**
|
||||
* Thrown when trying to introspect an option definition property
|
||||
* for which no value was configured inside the OptionsResolver instance.
|
||||
*
|
||||
* @see OptionsResolverIntrospector
|
||||
*
|
||||
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
|
||||
*/
|
||||
class NoConfigurationException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when trying to read an option that has no value set.
|
||||
*
|
||||
* When accessing optional options from within a lazy option or normalizer you should first
|
||||
* check whether the optional option is set. You can do this with `isset($options['optional'])`.
|
||||
* In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can
|
||||
* occur when evaluating lazy options.
|
||||
*
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when two lazy options have a cyclic dependency.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class OptionDefinitionException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when an undefined option is passed.
|
||||
*
|
||||
* You should remove the options in question from your code or define them
|
||||
* beforehand.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class UndefinedOptionsException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-present Fabien Potencier
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Exception\AccessException;
|
||||
|
||||
final class OptionConfigurator
|
||||
{
|
||||
private string $name;
|
||||
private OptionsResolver $resolver;
|
||||
|
||||
public function __construct(string $name, OptionsResolver $resolver)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->resolver = $resolver;
|
||||
$this->resolver->setDefined($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds allowed types for this option.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws AccessException If called from a lazy option or normalizer
|
||||
*/
|
||||
public function allowedTypes(string ...$types): static
|
||||
{
|
||||
$this->resolver->setAllowedTypes($this->name, $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets allowed values for this option.
|
||||
*
|
||||
* @param mixed ...$values One or more acceptable values/closures
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws AccessException If called from a lazy option or normalizer
|
||||
*/
|
||||
public function allowedValues(mixed ...$values): static
|
||||
{
|
||||
$this->resolver->setAllowedValues($this->name, $values);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value for this option.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws AccessException If called from a lazy option or normalizer
|
||||
*/
|
||||
public function default(mixed $value): static
|
||||
{
|
||||
$this->resolver->setDefault($this->name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an option configurator with the given name.
|
||||
*/
|
||||
public function define(string $option): self
|
||||
{
|
||||
return $this->resolver->define($option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this option as deprecated.
|
||||
*
|
||||
* @param string $package The name of the composer package that is triggering the deprecation
|
||||
* @param string $version The version of the package that introduced the deprecation
|
||||
* @param string|\Closure $message The deprecation message to use
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function deprecated(string $package, string $version, string|\Closure $message = 'The option "%name%" is deprecated.'): static
|
||||
{
|
||||
$this->resolver->setDeprecated($this->name, $package, $version, $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the normalizer for this option.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws AccessException If called from a lazy option or normalizer
|
||||
*/
|
||||
public function normalize(\Closure $normalizer): static
|
||||
{
|
||||
$this->resolver->setNormalizer($this->name, $normalizer);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this option as required.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws AccessException If called from a lazy option or normalizer
|
||||
*/
|
||||
public function required(): static
|
||||
{
|
||||
$this->resolver->setRequired($this->name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an info message for an option.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws AccessException If called from a lazy option or normalizer
|
||||
*/
|
||||
public function info(string $info): static
|
||||
{
|
||||
$this->resolver->setInfo($this->name, $info);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether ignore undefined options.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function ignoreUndefined(bool $ignore = true): static
|
||||
{
|
||||
$this->resolver->setIgnoreUndefined($ignore);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Contains resolved option values.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
interface Options extends \ArrayAccess, \Countable
|
||||
{
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+15
@@ -0,0 +1,15 @@
|
||||
OptionsResolver Component
|
||||
=========================
|
||||
|
||||
The OptionsResolver component is `array_replace` on steroids. It allows you to
|
||||
create an options system with required options, defaults, validation (type,
|
||||
value), normalization and more.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/options_resolver.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"type": "library",
|
||||
"description": "Provides an improved replacement for the array_replace PHP function",
|
||||
"keywords": ["options", "config", "configuration"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\OptionsResolver\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020-present Fabien Potencier
|
||||
|
||||
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.
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Polyfill\Php80;
|
||||
|
||||
/**
|
||||
* @author Ion Bazan <ion.bazan@gmail.com>
|
||||
* @author Nico Oelgart <nicoswd@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Php80
|
||||
{
|
||||
public static function fdiv(float $dividend, float $divisor): float
|
||||
{
|
||||
return @($dividend / $divisor);
|
||||
}
|
||||
|
||||
public static function get_debug_type($value): string
|
||||
{
|
||||
switch (true) {
|
||||
case null === $value: return 'null';
|
||||
case \is_bool($value): return 'bool';
|
||||
case \is_string($value): return 'string';
|
||||
case \is_array($value): return 'array';
|
||||
case \is_int($value): return 'int';
|
||||
case \is_float($value): return 'float';
|
||||
case \is_object($value): break;
|
||||
case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class';
|
||||
default:
|
||||
if (null === $type = @get_resource_type($value)) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
if ('Unknown' === $type) {
|
||||
$type = 'closed';
|
||||
}
|
||||
|
||||
return "resource ($type)";
|
||||
}
|
||||
|
||||
$class = \get_class($value);
|
||||
|
||||
if (false === strpos($class, '@')) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous';
|
||||
}
|
||||
|
||||
public static function get_resource_id($res): int
|
||||
{
|
||||
if (!\is_resource($res) && null === @get_resource_type($res)) {
|
||||
throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
|
||||
}
|
||||
|
||||
return (int) $res;
|
||||
}
|
||||
|
||||
public static function preg_last_error_msg(): string
|
||||
{
|
||||
switch (preg_last_error()) {
|
||||
case \PREG_INTERNAL_ERROR:
|
||||
return 'Internal error';
|
||||
case \PREG_BAD_UTF8_ERROR:
|
||||
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
case \PREG_BAD_UTF8_OFFSET_ERROR:
|
||||
return 'The offset did not correspond to the beginning of a valid UTF-8 code point';
|
||||
case \PREG_BACKTRACK_LIMIT_ERROR:
|
||||
return 'Backtrack limit exhausted';
|
||||
case \PREG_RECURSION_LIMIT_ERROR:
|
||||
return 'Recursion limit exhausted';
|
||||
case \PREG_JIT_STACKLIMIT_ERROR:
|
||||
return 'JIT stack limit exhausted';
|
||||
case \PREG_NO_ERROR:
|
||||
return 'No error';
|
||||
default:
|
||||
return 'Unknown error';
|
||||
}
|
||||
}
|
||||
|
||||
public static function str_contains(string $haystack, string $needle): bool
|
||||
{
|
||||
return '' === $needle || false !== strpos($haystack, $needle);
|
||||
}
|
||||
|
||||
public static function str_starts_with(string $haystack, string $needle): bool
|
||||
{
|
||||
return 0 === strncmp($haystack, $needle, \strlen($needle));
|
||||
}
|
||||
|
||||
public static function str_ends_with(string $haystack, string $needle): bool
|
||||
{
|
||||
if ('' === $needle || $needle === $haystack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ('' === $haystack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$needleLength = \strlen($needle);
|
||||
|
||||
return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength);
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Polyfill\Php80;
|
||||
|
||||
/**
|
||||
* @author Fedonyuk Anton <info@ensostudio.ru>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PhpToken implements \Stringable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $line;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $pos;
|
||||
|
||||
public function __construct(int $id, string $text, int $line = -1, int $position = -1)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->text = $text;
|
||||
$this->line = $line;
|
||||
$this->pos = $position;
|
||||
}
|
||||
|
||||
public function getTokenName(): ?string
|
||||
{
|
||||
if ('UNKNOWN' === $name = token_name($this->id)) {
|
||||
$name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string|array $kind
|
||||
*/
|
||||
public function is($kind): bool
|
||||
{
|
||||
foreach ((array) $kind as $value) {
|
||||
if (\in_array($value, [$this->id, $this->text], true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isIgnorable(): bool
|
||||
{
|
||||
return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string) $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static[]
|
||||
*/
|
||||
public static function tokenize(string $code, int $flags = 0): array
|
||||
{
|
||||
$line = 1;
|
||||
$position = 0;
|
||||
$tokens = token_get_all($code, $flags);
|
||||
foreach ($tokens as $index => $token) {
|
||||
if (\is_string($token)) {
|
||||
$id = \ord($token);
|
||||
$text = $token;
|
||||
} else {
|
||||
[$id, $text, $line] = $token;
|
||||
}
|
||||
$tokens[$index] = new static($id, $text, $line, $position);
|
||||
$position += \strlen($text);
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
Symfony Polyfill / Php80
|
||||
========================
|
||||
|
||||
This component provides features added to PHP 8.0 core:
|
||||
|
||||
- [`Stringable`](https://php.net/stringable) interface
|
||||
- [`fdiv`](https://php.net/fdiv)
|
||||
- [`ValueError`](https://php.net/valueerror) class
|
||||
- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class
|
||||
- `FILTER_VALIDATE_BOOL` constant
|
||||
- [`get_debug_type`](https://php.net/get_debug_type)
|
||||
- [`PhpToken`](https://php.net/phptoken) class
|
||||
- [`preg_last_error_msg`](https://php.net/preg_last_error_msg)
|
||||
- [`str_contains`](https://php.net/str_contains)
|
||||
- [`str_starts_with`](https://php.net/str_starts_with)
|
||||
- [`str_ends_with`](https://php.net/str_ends_with)
|
||||
- [`get_resource_id`](https://php.net/get_resource_id)
|
||||
|
||||
More information can be found in the
|
||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
This library is released under the [MIT license](LICENSE).
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class Attribute
|
||||
{
|
||||
public const TARGET_CLASS = 1;
|
||||
public const TARGET_FUNCTION = 2;
|
||||
public const TARGET_METHOD = 4;
|
||||
public const TARGET_PROPERTY = 8;
|
||||
public const TARGET_CLASS_CONSTANT = 16;
|
||||
public const TARGET_PARAMETER = 32;
|
||||
public const TARGET_ALL = 63;
|
||||
public const IS_REPEATABLE = 64;
|
||||
|
||||
/** @var int */
|
||||
public $flags;
|
||||
|
||||
public function __construct(int $flags = self::TARGET_ALL)
|
||||
{
|
||||
$this->flags = $flags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) {
|
||||
class PhpToken extends Symfony\Polyfill\Php80\PhpToken
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
interface Stringable
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
class UnhandledMatchError extends Error
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
class ValueError extends Error
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Symfony\Polyfill\Php80 as p;
|
||||
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) {
|
||||
define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
if (!function_exists('fdiv')) {
|
||||
function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); }
|
||||
}
|
||||
if (!function_exists('preg_last_error_msg')) {
|
||||
function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); }
|
||||
}
|
||||
if (!function_exists('str_contains')) {
|
||||
function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); }
|
||||
}
|
||||
if (!function_exists('str_starts_with')) {
|
||||
function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); }
|
||||
}
|
||||
if (!function_exists('str_ends_with')) {
|
||||
function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); }
|
||||
}
|
||||
if (!function_exists('get_debug_type')) {
|
||||
function get_debug_type($value): string { return p\Php80::get_debug_type($value); }
|
||||
}
|
||||
if (!function_exists('get_resource_id')) {
|
||||
function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"type": "library",
|
||||
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||
"keywords": ["polyfill", "shim", "compatibility", "portable"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ion Bazan",
|
||||
"email": "ion.bazan@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Polyfill\\Php80\\": "" },
|
||||
"files": [ "bootstrap.php" ],
|
||||
"classmap": [ "Resources/stubs" ]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user