One Hat Cyber Team
Your IP:
216.73.216.30
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
self
/
root
/
proc
/
thread-self
/
cwd
/
View File Name :
stream-filter.tar
src/CallbackFilter.php 0000644 00000007556 15111262743 0010725 0 ustar 00 <?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; } } src/functions_include.php 0000644 00000000204 15111262743 0011555 0 ustar 00 <?php // @codeCoverageIgnoreStart if (!\function_exists('Clue\\StreamFilter\\append')) { require __DIR__ . '/functions.php'; } src/functions.php 0000644 00000025735 15111262743 0010072 0 ustar 00 <?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) { $ret = @\stream_filter_append($stream, register(), $read_write, $callback); // PHP 8 throws above on type errors, older PHP and memory issues can throw here // @codeCoverageIgnoreStart if ($ret === false) { $error = \error_get_last() + array('message' => ''); throw new \RuntimeException('Unable to append filter: ' . $error['message']); } // @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) { $ret = @\stream_filter_prepend($stream, register(), $read_write, $callback); // PHP 8 throws above on type errors, older PHP and memory issues can throw here // @codeCoverageIgnoreStart if ($ret === false) { $error = \error_get_last() + array('message' => ''); throw new \RuntimeException('Unable to prepend filter: ' . $error['message']); } // @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'); 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); } if ($filter === false) { \fclose($fp); $error = \error_get_last() + array('message' => ''); throw new \RuntimeException('Unable to access built-in filter: ' . $error['message']); } // 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) { if (@\stream_filter_remove($filter) === false) { // PHP 8 throws above on type errors, older PHP and memory issues can throw here $error = \error_get_last(); throw new \RuntimeException('Unable to remove filter: ' . $error['message']); } } /** * 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; } README.md 0000644 00000025253 15111262743 0006034 0 ustar 00 # 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.6 ``` 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. LICENSE 0000644 00000002072 15111262743 0005554 0 ustar 00 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. composer.json 0000644 00000001455 15111262743 0007275 0 ustar 00 { "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/php-stream-filter", "license": "MIT", "authors": [ { "name": "Christian Lück", "email": "christian@clue.engineering" } ], "require": { "php": ">=5.3" }, "require-dev": { "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" }, "autoload": { "psr-4": { "Clue\\StreamFilter\\": "src/" }, "files": [ "src/functions_include.php" ] }, "autoload-dev": { "psr-4": { "Clue\\Tests\\StreamFilter\\": "tests/" } } } CHANGELOG.md 0000644 00000004672 15111262743 0006370 0 ustar 00 # Changelog ## 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 .github/FUNDING.yml 0000644 00000000066 15111262743 0007725 0 ustar 00 github: clue custom: https://clue.engineering/support