Merge branch 'ent-10833-15134-bug-en-chromium-111-0-5563-64-1' into 'develop'
update composer See merge request artica/pandorafms!5674
This commit is contained in:
commit
d5450853f5
|
@ -9,14 +9,14 @@
|
|||
],
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "8.0.0"
|
||||
"php": "8.0.2"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"mpdf/mpdf": "^8.0.15",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"amphp/parallel-functions": "^1.0",
|
||||
"chrome-php/chrome": "^1.7.1",
|
||||
"chrome-php/chrome": "^1.8.1",
|
||||
"artica/phpchartjs": "^1.0",
|
||||
"tinymce/tinymce": "^6.4"
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,92 @@
|
|||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '7.4'
|
||||
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '8.0'
|
||||
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '8.1'
|
||||
|
||||
- operating-system: 'windows-latest'
|
||||
php-version: '8.1'
|
||||
job-description: 'on Windows'
|
||||
|
||||
- operating-system: 'macos-latest'
|
||||
php-version: '8.1'
|
||||
job-description: 'on macOS'
|
||||
|
||||
name: PHP ${{ matrix.php-version }} ${{ matrix.job-description }}
|
||||
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
|
||||
steps:
|
||||
- name: Set git to use LF
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions: fiber-amphp/ext-fiber@master
|
||||
|
||||
- name: Get Composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('**/composer.*') }}-${{ matrix.composer-flags }}
|
||||
restore-keys: |
|
||||
composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('**/composer.*') }}-
|
||||
composer-${{ runner.os }}-${{ matrix.php-version }}-
|
||||
composer-${{ runner.os }}-
|
||||
composer-
|
||||
|
||||
- name: Install dependencies
|
||||
uses: nick-invision/retry@v2
|
||||
with:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 30
|
||||
command: |
|
||||
composer update --optimize-autoloader --no-interaction --no-progress ${{ matrix.composer-flags }}
|
||||
composer info -D
|
||||
|
||||
- name: Run tests
|
||||
run: vendor/bin/phpunit ${{ matrix.phpunit-flags }}
|
||||
|
||||
- name: Run examples
|
||||
run: (for f in examples/*.php; do echo $f && if ! php $f; then echo '!! failed !!' && exit 1; fi && echo "-------"; done)
|
||||
if: runner.os != 'Windows'
|
||||
|
||||
- name: Run style fixer
|
||||
env:
|
||||
PHP_CS_FIXER_IGNORE_ENV: 1
|
||||
run: vendor/bin/php-cs-fixer --diff --dry-run -v fix
|
||||
if: runner.os != 'Windows'
|
||||
|
||||
- name: Install composer-require-checker
|
||||
run: php -r 'file_put_contents("composer-require-checker.phar", file_get_contents("https://github.com/maglnet/ComposerRequireChecker/releases/download/3.7.0/composer-require-checker.phar"));'
|
||||
if: runner.os != 'Windows' && matrix.composer-require-checker-version != 'none'
|
||||
|
||||
- name: Run composer-require-checker
|
||||
run: php composer-require-checker.phar check composer.json --config-file $PWD/composer-require-check.json
|
||||
if: runner.os != 'Windows' && matrix.composer-require-checker-version != 'none'
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 amphp
|
||||
Copyright (c) 2017-2022 amphp (Niklas Keller, and contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"symbol-whitelist": [
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"static",
|
||||
"self",
|
||||
"parent",
|
||||
"array",
|
||||
"string",
|
||||
"int",
|
||||
"float",
|
||||
"bool",
|
||||
"iterable",
|
||||
"callable",
|
||||
"mixed",
|
||||
"void",
|
||||
"object",
|
||||
"parallel"
|
||||
],
|
||||
"php-core-extensions": [
|
||||
"Core",
|
||||
"date",
|
||||
"pcre",
|
||||
"Phar",
|
||||
"Reflection",
|
||||
"SPL",
|
||||
"standard",
|
||||
"hash"
|
||||
]
|
||||
}
|
|
@ -23,19 +23,20 @@
|
|||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7",
|
||||
"amphp/parallel": "^1.1",
|
||||
"php": ">=7.4",
|
||||
"amphp/amp": "^2.0.3",
|
||||
"opis/closure": "^3.0.7"
|
||||
"amphp/parallel": "^1.4",
|
||||
"amphp/serialization": "^1.0",
|
||||
"laravel/serializable-closure": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"amphp/phpunit-util": "^1.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.9",
|
||||
"phpunit/phpunit": "^6.5"
|
||||
"amphp/php-cs-fixer-config": "v2.x-dev",
|
||||
"amphp/phpunit-util": "^2.0",
|
||||
"phpunit/phpunit": "^9.5.11"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.0.0"
|
||||
"php": "7.4"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -6,7 +6,8 @@ use Amp\Parallel\Worker\Environment;
|
|||
use Amp\Parallel\Worker\Task;
|
||||
|
||||
/** @internal */
|
||||
class SerializedCallableTask implements Task {
|
||||
class SerializedCallableTask implements Task
|
||||
{
|
||||
/** @var string */
|
||||
private $function;
|
||||
|
||||
|
@ -17,12 +18,14 @@ class SerializedCallableTask implements Task {
|
|||
* @param string $function Serialized function.
|
||||
* @param array $args Arguments to pass to the function. Must be serializable.
|
||||
*/
|
||||
public function __construct(string $function, array $args) {
|
||||
public function __construct(string $function, array $args)
|
||||
{
|
||||
$this->function = $function;
|
||||
$this->args = $args;
|
||||
}
|
||||
|
||||
public function run(Environment $environment) {
|
||||
public function run(Environment $environment)
|
||||
{
|
||||
$callable = \unserialize($this->function, ['allowed_classes' => true]);
|
||||
|
||||
if ($callable instanceof \__PHP_Incomplete_Class) {
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
namespace Amp\ParallelFunctions;
|
||||
|
||||
use Amp\MultiReasonException;
|
||||
use Amp\Parallel\Sync\SerializationException;
|
||||
use Amp\Parallel\Worker\Pool;
|
||||
use Amp\Promise;
|
||||
use Opis\Closure\SerializableClosure;
|
||||
use Amp\Serialization\SerializationException;
|
||||
use Laravel\SerializableClosure\SerializableClosure;
|
||||
use function Amp\call;
|
||||
use function Amp\Parallel\Worker\enqueue;
|
||||
use function Amp\Promise\any;
|
||||
|
@ -20,7 +20,8 @@ use function Amp\Promise\any;
|
|||
* @return callable Callable executing in another thread / process.
|
||||
* @throws SerializationException If the passed callable is not safely serializable.
|
||||
*/
|
||||
function parallel(callable $callable, Pool $pool = null): callable {
|
||||
function parallel(callable $callable, Pool $pool = null): callable
|
||||
{
|
||||
if ($callable instanceof \Closure) {
|
||||
$callable = new SerializableClosure($callable);
|
||||
}
|
||||
|
@ -47,11 +48,12 @@ function parallel(callable $callable, Pool $pool = null): callable {
|
|||
* @return Promise Resolves to the result once the operation finished.
|
||||
* @throws \Error If the passed callable is not safely serializable.
|
||||
*/
|
||||
function parallelMap(array $array, callable $callable, Pool $pool = null): Promise {
|
||||
function parallelMap(array $array, callable $callable, Pool $pool = null): Promise
|
||||
{
|
||||
return call(function () use ($array, $callable, $pool) {
|
||||
// Amp\Promise\any() guarantees that all operations finished prior to resolving. Amp\Promise\all() doesn't.
|
||||
// Additionally, we return all errors as a MultiReasonException instead of throwing on the first error.
|
||||
list($errors, $results) = yield any(\array_map(parallel($callable, $pool), $array));
|
||||
[$errors, $results] = yield any(\array_map(parallel($callable, $pool), $array));
|
||||
|
||||
if ($errors) {
|
||||
throw new MultiReasonException($errors);
|
||||
|
@ -72,7 +74,8 @@ function parallelMap(array $array, callable $callable, Pool $pool = null): Promi
|
|||
* @return Promise
|
||||
* @throws \Error If the passed callable is not safely serializable.
|
||||
*/
|
||||
function parallelFilter(array $array, callable $callable = null, int $flag = 0, Pool $pool = null): Promise {
|
||||
function parallelFilter(array $array, callable $callable = null, int $flag = 0, Pool $pool = null): Promise
|
||||
{
|
||||
return call(function () use ($array, $callable, $flag, $pool) {
|
||||
if ($callable === null) {
|
||||
if ($flag === \ARRAY_FILTER_USE_BOTH || $flag === \ARRAY_FILTER_USE_KEY) {
|
||||
|
@ -87,11 +90,11 @@ function parallelFilter(array $array, callable $callable = null, int $flag = 0,
|
|||
// Amp\Promise\any() guarantees that all operations finished prior to resolving. Amp\Promise\all() doesn't.
|
||||
// Additionally, we return all errors as a MultiReasonException instead of throwing on the first error.
|
||||
if ($flag === \ARRAY_FILTER_USE_BOTH) {
|
||||
list($errors, $results) = yield any(\array_map(parallel($callable, $pool), $array, \array_keys($array)));
|
||||
[$errors, $results] = yield any(\array_map(parallel($callable, $pool), $array, \array_keys($array)));
|
||||
} elseif ($flag === \ARRAY_FILTER_USE_KEY) {
|
||||
list($errors, $results) = yield any(\array_map(parallel($callable, $pool), \array_keys($array)));
|
||||
[$errors, $results] = yield any(\array_map(parallel($callable, $pool), \array_keys($array)));
|
||||
} else {
|
||||
list($errors, $results) = yield any(\array_map(parallel($callable, $pool), $array));
|
||||
[$errors, $results] = yield any(\array_map(parallel($callable, $pool), $array));
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
|
|
|
@ -80,9 +80,13 @@ final class Process implements Context
|
|||
"log_errors" => "1",
|
||||
];
|
||||
|
||||
$otherOpts = [];
|
||||
if ($binary === null) {
|
||||
if (\PHP_SAPI === "cli") {
|
||||
$binary = \PHP_BINARY;
|
||||
} else if (\PHP_SAPI === "phpdbg") {
|
||||
$binary = \PHP_BINARY;
|
||||
$otherOpts []= '-qrr';
|
||||
} else {
|
||||
$binary = self::$binaryPath ?? self::locateBinary();
|
||||
}
|
||||
|
@ -136,7 +140,7 @@ final class Process implements Context
|
|||
|
||||
$command = \implode(" ", [
|
||||
\escapeshellarg($binary),
|
||||
$this->formatOptions($options),
|
||||
$this->formatOptions($options, $otherOpts),
|
||||
\escapeshellarg($scriptPath),
|
||||
$this->hub->getUri(),
|
||||
$script,
|
||||
|
@ -163,9 +167,9 @@ final class Process implements Context
|
|||
throw new \Error("Could not locate PHP executable binary");
|
||||
}
|
||||
|
||||
private function formatOptions(array $options): string
|
||||
private function formatOptions(array $options, array $otherOpts): string
|
||||
{
|
||||
$result = [];
|
||||
$result = $otherOpts;
|
||||
|
||||
foreach ($options as $option => $value) {
|
||||
$result[] = \sprintf("-d%s=%s", $option, $value);
|
||||
|
|
|
@ -4,8 +4,16 @@ namespace Amp\Parallel\Sync;
|
|||
|
||||
use Amp\Serialization\SerializationException as SerializerException;
|
||||
|
||||
// Alias must be defined in an always-loaded file as catch blocks do not trigger the autoloader.
|
||||
\class_alias(SerializerException::class, SerializationException::class);
|
||||
if (!\class_exists(SerializationException::class)) {
|
||||
// Wrap definition in error handler to avoid apparently PHP bug with preloading, see amphp/parallel#159
|
||||
set_error_handler(function (): bool { return true; });
|
||||
try {
|
||||
// Alias must be defined in an always-loaded file as catch blocks do not trigger the autoloader.
|
||||
\class_alias(SerializerException::class, SerializationException::class);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $exception
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '7.4'
|
||||
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '8.0'
|
||||
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '8.1'
|
||||
|
||||
- operating-system: 'windows-latest'
|
||||
php-version: '8.1'
|
||||
job-description: 'on Windows'
|
||||
|
||||
- operating-system: 'macos-latest'
|
||||
php-version: '8.1'
|
||||
job-description: 'on macOS'
|
||||
|
||||
- operating-system: 'ubuntu-latest'
|
||||
php-version: '8.2'
|
||||
|
||||
name: PHP ${{ matrix.php-version }} ${{ matrix.job-description }}
|
||||
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
|
||||
steps:
|
||||
- name: Set git to use LF
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
|
||||
- name: Get Composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('**/composer.*') }}-${{ matrix.composer-flags }}
|
||||
restore-keys: |
|
||||
composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('**/composer.*') }}-
|
||||
composer-${{ runner.os }}-${{ matrix.php-version }}-
|
||||
composer-${{ runner.os }}-
|
||||
composer-
|
||||
|
||||
- name: Install dependencies
|
||||
uses: nick-invision/retry@v2
|
||||
with:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 30
|
||||
command: |
|
||||
composer update --optimize-autoloader --no-interaction --no-progress ${{ matrix.composer-flags }}
|
||||
composer info -D
|
||||
|
||||
- name: Run tests
|
||||
run: vendor/bin/phpunit ${{ matrix.phpunit-flags }}
|
||||
|
||||
- name: Run static analysis
|
||||
run: vendor/bin/psalm.phar
|
||||
|
||||
- name: Run style fixer
|
||||
env:
|
||||
PHP_CS_FIXER_IGNORE_ENV: 1
|
||||
run: vendor/bin/php-cs-fixer --diff --dry-run -v fix
|
||||
if: runner.os != 'Windows'
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "docs/.shared"]
|
||||
path = docs/.shared
|
||||
url = https://github.com/amphp/website-shared
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
$config = new Amp\CodeStyle\Config();
|
||||
$config->getFinder()
|
||||
->in(__DIR__ . '/examples')
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/test');
|
||||
|
||||
$config->setCacheFile(__DIR__ . '/.php_cs.cache');
|
||||
|
||||
return $config;
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
"@PSR1" => true,
|
||||
"@PSR2" => true,
|
||||
"braces" => [
|
||||
"allow_single_line_closure" => true,
|
||||
"position_after_functions_and_oop_constructs" => "same",
|
||||
],
|
||||
"array_syntax" => ["syntax" => "short"],
|
||||
"cast_spaces" => true,
|
||||
"combine_consecutive_unsets" => true,
|
||||
"function_to_constant" => true,
|
||||
"no_multiline_whitespace_before_semicolons" => true,
|
||||
"no_unused_imports" => true,
|
||||
"no_useless_else" => true,
|
||||
"no_useless_return" => true,
|
||||
"no_whitespace_before_comma_in_array" => true,
|
||||
"no_whitespace_in_blank_line" => true,
|
||||
"non_printable_character" => true,
|
||||
"normalize_index_brace" => true,
|
||||
"ordered_imports" => true,
|
||||
"php_unit_construct" => true,
|
||||
"php_unit_dedicate_assert" => true,
|
||||
"php_unit_fqcn_annotation" => true,
|
||||
"phpdoc_summary" => true,
|
||||
"phpdoc_types" => true,
|
||||
"psr4" => true,
|
||||
"return_type_declaration" => ["space_before" => "none"],
|
||||
"short_scalar_cast" => true,
|
||||
"single_blank_line_before_namespace" => true,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . "/lib")
|
||||
->in(__DIR__ . "/test")
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 amphp
|
||||
Copyright (c) 2017-2022 amphp (Niklas Keller, Aaron Piotrowski, and contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
PHP_BIN := php
|
||||
COMPOSER_BIN := composer
|
||||
|
||||
COVERAGE = coverage
|
||||
SRCS = lib test
|
||||
|
||||
find_php_files = $(shell find $(1) -type f -name "*.php")
|
||||
src = $(foreach d,$(SRCS),$(call find_php_files,$(d)))
|
||||
|
||||
.PHONY: test
|
||||
test: setup phpunit code-style
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-coverage clean-vendor
|
||||
|
||||
.PHONY: clean-coverage
|
||||
clean-coverage:
|
||||
test ! -e coverage || rm -r coverage
|
||||
|
||||
.PHONY: clean-vendor
|
||||
clean-vendor:
|
||||
test ! -e vendor || rm -r vendor
|
||||
|
||||
.PHONY: setup
|
||||
setup: vendor/autoload.php
|
||||
|
||||
.PHONY: deps-update
|
||||
deps-update:
|
||||
$(COMPOSER_BIN) update
|
||||
|
||||
.PHONY: phpunit
|
||||
phpunit: setup
|
||||
$(PHP_BIN) vendor/bin/phpunit
|
||||
|
||||
.PHONY: code-style
|
||||
code-style: setup
|
||||
PHP_CS_FIXER_IGNORE_ENV=1 $(PHP_BIN) vendor/bin/php-cs-fixer --diff -v fix
|
||||
|
||||
composer.lock: composer.json
|
||||
$(COMPOSER_BIN) install
|
||||
touch $@
|
||||
|
||||
vendor/autoload.php: composer.lock
|
||||
$(COMPOSER_BIN) install
|
||||
touch $@
|
|
@ -1,10 +1,7 @@
|
|||
# parser
|
||||
# amphp/parser
|
||||
|
||||
[](https://travis-ci.org/amphp/parser)
|
||||
[](https://coveralls.io/github/amphp/parser?branch=master)
|
||||

|
||||
|
||||
`amphp/parser` is a streaming generator parser.
|
||||
AMPHP is a collection of event-driven libraries for PHP designed with fibers and concurrency in mind.
|
||||
`amphp/parser` allows easily building streaming generator parsers.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -16,11 +13,42 @@ composer require amphp/parser
|
|||
|
||||
## Requirements
|
||||
|
||||
- PHP 7.0+
|
||||
- PHP 7.4+
|
||||
|
||||
## Documentation
|
||||
## Usage
|
||||
|
||||
Documentation is bundled within this repository in the [`./docs`](./docs) directory.
|
||||
PHP's generators are a great way for building incremental parsers.
|
||||
|
||||
## Example
|
||||
|
||||
This simple parser parses a line delimited protocol and prints a message for each line. Instead of printing a message, you could also invoke a data callback.
|
||||
|
||||
```php
|
||||
$parser = new Parser((function () {
|
||||
while (true) {
|
||||
$line = yield "\r\n";
|
||||
|
||||
if (trim($line) === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
print "New item: {$line}" . PHP_EOL;
|
||||
}
|
||||
})());
|
||||
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$parser->push("bar\r");
|
||||
$parser->push("\nfoo");
|
||||
}
|
||||
```
|
||||
|
||||
Furthere examples can be found in other AMPHP packages which this library to build streaming parsers.
|
||||
- [`ChannelParser`](https://github.com/amphp/byte-stream/blob/5c7eb399b746a582e9598935b26483b214250c34/src/Internal/ChannelParser.php#L28) in [`amphp/byte-stream`](https://github.com/amphp/byte-stream)
|
||||
- [`RespParser`](https://github.com/amphp/redis/blob/649cff6d5e6b4c579dcab1a20511a437cbe3d62a/src/Connection/RespParser.php#L31) in [`amphp/redis`](https://github.com/amphp/redis)
|
||||
|
||||
## Yield Behavior
|
||||
|
||||
You can either `yield` a `string` that's used as delimiter, an `integer` that's used as length, or `null` to flush any remaining buffer in the parser (if any) or await the next call to `Parser::push()`.
|
||||
|
||||
## Versioning
|
||||
|
||||
|
|
|
@ -23,15 +23,16 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7"
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6",
|
||||
"friendsofphp/php-cs-fixer": "^2.3"
|
||||
"phpunit/phpunit": "^9",
|
||||
"amphp/php-cs-fixer-config": "^2",
|
||||
"psalm/phar": "^5.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Amp\\Parser\\": "lib"
|
||||
"Amp\\Parser\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
source "https://rubygems.org"
|
||||
gem "github-pages"
|
||||
gem "kramdown"
|
||||
gem "jekyll-github-metadata"
|
||||
gem "jekyll-relative-links"
|
|
@ -1,202 +0,0 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (4.2.8)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.1)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
colorator (1.1.0)
|
||||
ethon (0.10.1)
|
||||
ffi (>= 1.3.0)
|
||||
execjs (2.7.0)
|
||||
faraday (0.12.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.18)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.0)
|
||||
github-pages (139)
|
||||
activesupport (= 4.2.8)
|
||||
github-pages-health-check (= 1.3.3)
|
||||
jekyll (= 3.4.3)
|
||||
jekyll-avatar (= 0.4.2)
|
||||
jekyll-coffeescript (= 1.0.1)
|
||||
jekyll-default-layout (= 0.1.4)
|
||||
jekyll-feed (= 0.9.2)
|
||||
jekyll-gist (= 1.4.0)
|
||||
jekyll-github-metadata (= 2.3.1)
|
||||
jekyll-mentions (= 1.2.0)
|
||||
jekyll-optional-front-matter (= 0.1.2)
|
||||
jekyll-paginate (= 1.1.0)
|
||||
jekyll-readme-index (= 0.1.0)
|
||||
jekyll-redirect-from (= 0.12.1)
|
||||
jekyll-relative-links (= 0.4.0)
|
||||
jekyll-sass-converter (= 1.5.0)
|
||||
jekyll-seo-tag (= 2.2.3)
|
||||
jekyll-sitemap (= 1.0.0)
|
||||
jekyll-swiss (= 0.4.0)
|
||||
jekyll-theme-architect (= 0.0.4)
|
||||
jekyll-theme-cayman (= 0.0.4)
|
||||
jekyll-theme-dinky (= 0.0.4)
|
||||
jekyll-theme-hacker (= 0.0.4)
|
||||
jekyll-theme-leap-day (= 0.0.4)
|
||||
jekyll-theme-merlot (= 0.0.4)
|
||||
jekyll-theme-midnight (= 0.0.4)
|
||||
jekyll-theme-minimal (= 0.0.4)
|
||||
jekyll-theme-modernist (= 0.0.4)
|
||||
jekyll-theme-primer (= 0.2.1)
|
||||
jekyll-theme-slate (= 0.0.4)
|
||||
jekyll-theme-tactile (= 0.0.4)
|
||||
jekyll-theme-time-machine (= 0.0.4)
|
||||
jekyll-titles-from-headings (= 0.1.5)
|
||||
jemoji (= 0.8.0)
|
||||
kramdown (= 1.13.2)
|
||||
liquid (= 3.0.6)
|
||||
listen (= 3.0.6)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.1.1)
|
||||
rouge (= 1.11.1)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.3.3)
|
||||
addressable (~> 2.3)
|
||||
net-dns (~> 0.8)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (~> 2.0)
|
||||
typhoeus (~> 0.7)
|
||||
html-pipeline (2.6.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
i18n (0.8.4)
|
||||
jekyll (3.4.3)
|
||||
addressable (~> 2.4)
|
||||
colorator (~> 1.0)
|
||||
jekyll-sass-converter (~> 1.0)
|
||||
jekyll-watch (~> 1.1)
|
||||
kramdown (~> 1.3)
|
||||
liquid (~> 3.0)
|
||||
mercenary (~> 0.3.3)
|
||||
pathutil (~> 0.9)
|
||||
rouge (~> 1.7)
|
||||
safe_yaml (~> 1.0)
|
||||
jekyll-avatar (0.4.2)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-coffeescript (1.0.1)
|
||||
coffee-script (~> 2.2)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-feed (0.9.2)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-gist (1.4.0)
|
||||
octokit (~> 4.2)
|
||||
jekyll-github-metadata (2.3.1)
|
||||
jekyll (~> 3.1)
|
||||
octokit (~> 4.0, != 4.4.0)
|
||||
jekyll-mentions (1.2.0)
|
||||
activesupport (~> 4.0)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-optional-front-matter (0.1.2)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-paginate (1.1.0)
|
||||
jekyll-readme-index (0.1.0)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-redirect-from (0.12.1)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-relative-links (0.4.0)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-sass-converter (1.5.0)
|
||||
sass (~> 3.4)
|
||||
jekyll-seo-tag (2.2.3)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-sitemap (1.0.0)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-swiss (0.4.0)
|
||||
jekyll-theme-architect (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-cayman (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-dinky (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-hacker (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-leap-day (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-merlot (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-midnight (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-minimal (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-modernist (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-primer (0.2.1)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-slate (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-tactile (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-theme-time-machine (0.0.4)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-titles-from-headings (0.1.5)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-watch (1.5.0)
|
||||
listen (~> 3.0, < 3.1)
|
||||
jemoji (0.8.0)
|
||||
activesupport (~> 4.0)
|
||||
gemoji (~> 3.0)
|
||||
html-pipeline (~> 2.2)
|
||||
jekyll (>= 3.0)
|
||||
kramdown (1.13.2)
|
||||
liquid (3.0.6)
|
||||
listen (3.0.6)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9.7)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.2.0)
|
||||
minima (2.1.1)
|
||||
jekyll (~> 3.3)
|
||||
minitest (5.10.2)
|
||||
multipart-post (2.0.0)
|
||||
net-dns (0.8.0)
|
||||
nokogiri (1.8.0)
|
||||
mini_portile2 (~> 2.2.0)
|
||||
octokit (4.7.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.14.0)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (2.0.5)
|
||||
rb-fsevent (0.9.8)
|
||||
rb-inotify (0.9.8)
|
||||
ffi (>= 0.5.0)
|
||||
rouge (1.11.1)
|
||||
safe_yaml (1.0.4)
|
||||
sass (3.4.24)
|
||||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
typhoeus (0.8.0)
|
||||
ethon (>= 0.8.0)
|
||||
tzinfo (1.2.3)
|
||||
thread_safe (~> 0.1)
|
||||
unicode-display_width (1.2.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
github-pages
|
||||
jekyll-github-metadata
|
||||
jekyll-relative-links
|
||||
kramdown
|
||||
|
||||
BUNDLED WITH
|
||||
1.15.0
|
|
@ -1,23 +0,0 @@
|
|||
kramdown:
|
||||
input: GFM
|
||||
toc_levels: 2..3
|
||||
|
||||
baseurl: "/parser"
|
||||
layouts_dir: ".shared/layout"
|
||||
|
||||
exclude: ["Gemfile", "Gemfile.lock", "README.md", "vendor"]
|
||||
include: [".shared"]
|
||||
|
||||
repository: amphp/parser
|
||||
gems:
|
||||
- "jekyll-github-metadata"
|
||||
- "jekyll-relative-links"
|
||||
|
||||
defaults:
|
||||
- scope:
|
||||
path: ""
|
||||
type: "pages"
|
||||
values:
|
||||
layout: "docs"
|
||||
|
||||
asset_path: "/parser/.shared/asset"
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
title: Parser
|
||||
permalink: /
|
||||
---
|
||||
PHP's generators are a great way for building incremental parsers.
|
||||
|
||||
## Example
|
||||
|
||||
This simple parser parses a line delimited protocol and prints a message for each line. Instead of printing a message, you could also invoke a data callback.
|
||||
|
||||
```php
|
||||
$parser = new Parser((function () {
|
||||
while (true) {
|
||||
$line = yield "\r\n";
|
||||
|
||||
if (trim($line) === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
print "New item: {$line}" . PHP_EOL;
|
||||
}
|
||||
})());
|
||||
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$parser->push("bar\r");
|
||||
$parser->push("\nfoo");
|
||||
}
|
||||
```
|
||||
|
||||
## Yield Behavior
|
||||
|
||||
You can either `yield` a `string` that's used as delimiter, an `integer` that's used as length, or `null` for consuming everything that's available.
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Amp\Parser;
|
||||
|
||||
class Parser {
|
||||
/** @var \Generator */
|
||||
private $generator;
|
||||
|
||||
/** @var string */
|
||||
private $buffer = '';
|
||||
|
||||
/** @var int|string|null */
|
||||
private $delimiter;
|
||||
|
||||
/**
|
||||
* @param \Generator $generator
|
||||
*
|
||||
* @throws InvalidDelimiterError If the generator yields an invalid delimiter.
|
||||
* @throws \Throwable If the generator throws.
|
||||
*/
|
||||
public function __construct(\Generator $generator) {
|
||||
$this->generator = $generator;
|
||||
|
||||
$this->delimiter = $this->generator->current();
|
||||
|
||||
if (!$this->generator->valid()) {
|
||||
$this->generator = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->delimiter !== null
|
||||
&& (!\is_int($this->delimiter) || $this->delimiter <= 0)
|
||||
&& (!\is_string($this->delimiter) || !\strlen($this->delimiter))
|
||||
) {
|
||||
throw new InvalidDelimiterError(
|
||||
$generator,
|
||||
\sprintf(
|
||||
"Invalid value yielded: Expected NULL, an int greater than 0, or a non-empty string; %s given",
|
||||
\is_object($this->delimiter) ? \sprintf("instance of %s", \get_class($this->delimiter)) : \gettype($this->delimiter)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the generator parser and returns any remaining data in the internal buffer. Writing data after calling
|
||||
* this method will result in an error.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public function cancel(): string {
|
||||
$this->generator = null;
|
||||
return $this->buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the parser can still receive more data to parse, false if it has ended and calling push
|
||||
* will throw an exception.
|
||||
*/
|
||||
final public function isValid(): bool {
|
||||
return $this->generator !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data to the internal buffer and tries to continue parsing.
|
||||
*
|
||||
* @param string $data Data to append to the internal buffer.
|
||||
*
|
||||
* @throws InvalidDelimiterError If the generator yields an invalid delimiter.
|
||||
* @throws \Error If parsing has already been cancelled.
|
||||
* @throws \Throwable If the generator throws.
|
||||
*/
|
||||
final public function push(string $data) {
|
||||
if ($this->generator === null) {
|
||||
throw new \Error("The parser is no longer writable");
|
||||
}
|
||||
|
||||
$this->buffer .= $data;
|
||||
$end = false;
|
||||
|
||||
try {
|
||||
while ($this->buffer !== "") {
|
||||
if (\is_int($this->delimiter)) {
|
||||
if (\strlen($this->buffer) < $this->delimiter) {
|
||||
break; // Too few bytes in buffer.
|
||||
}
|
||||
|
||||
$send = \substr($this->buffer, 0, $this->delimiter);
|
||||
$this->buffer = \substr($this->buffer, $this->delimiter);
|
||||
} elseif (\is_string($this->delimiter)) {
|
||||
if (($position = \strpos($this->buffer, $this->delimiter)) === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$send = \substr($this->buffer, 0, $position);
|
||||
$this->buffer = \substr($this->buffer, $position + \strlen($this->delimiter));
|
||||
} else {
|
||||
$send = $this->buffer;
|
||||
$this->buffer = "";
|
||||
}
|
||||
|
||||
$this->delimiter = $this->generator->send($send);
|
||||
|
||||
if (!$this->generator->valid()) {
|
||||
$end = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->delimiter !== null
|
||||
&& (!\is_int($this->delimiter) || $this->delimiter <= 0)
|
||||
&& (!\is_string($this->delimiter) || !\strlen($this->delimiter))
|
||||
) {
|
||||
throw new InvalidDelimiterError(
|
||||
$this->generator,
|
||||
\sprintf(
|
||||
"Invalid value yielded: Expected NULL, an int greater than 0, or a non-empty string; %s given",
|
||||
\is_object($this->delimiter) ? \sprintf("instance of %s", \get_class($this->delimiter)) : \gettype($this->delimiter)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
$end = true;
|
||||
throw $exception;
|
||||
} finally {
|
||||
if ($end) {
|
||||
$this->generator = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="2"
|
||||
phpVersion="8.1"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config/vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="examples" />
|
||||
<directory name="src" />
|
||||
</projectFiles>
|
||||
|
||||
<issueHandlers>
|
||||
<ForbiddenCode>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="examples"/>
|
||||
</errorLevel>
|
||||
</ForbiddenCode>
|
||||
|
||||
<DocblockTypeContradiction>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src" />
|
||||
</errorLevel>
|
||||
</DocblockTypeContradiction>
|
||||
|
||||
<MissingClosureReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src" />
|
||||
</errorLevel>
|
||||
</MissingClosureReturnType>
|
||||
</issueHandlers>
|
||||
</psalm>
|
|
@ -1,14 +1,11 @@
|
|||
<?php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Amp\Parser;
|
||||
|
||||
class InvalidDelimiterError extends \Error {
|
||||
/**
|
||||
* @param \Generator $generator
|
||||
* @param string $prefix
|
||||
* @param \Throwable|null $previous
|
||||
*/
|
||||
public function __construct(\Generator $generator, string $prefix, \Throwable $previous = null) {
|
||||
class InvalidDelimiterError extends \Error
|
||||
{
|
||||
public function __construct(\Generator $generator, string $prefix, \Throwable $previous = null)
|
||||
{
|
||||
$yielded = $generator->current();
|
||||
$prefix .= \sprintf(
|
||||
"; %s yielded at key %s",
|
|
@ -0,0 +1,153 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Amp\Parser;
|
||||
|
||||
class Parser
|
||||
{
|
||||
/** @var \Generator<array-key, int|string|null, string, void>|null */
|
||||
private ?\Generator $generator;
|
||||
|
||||
/** @var list<string> */
|
||||
private array $buffers = [];
|
||||
|
||||
private int $bufferLength = 0;
|
||||
|
||||
/** @var int|string|null */
|
||||
private $delimiter;
|
||||
|
||||
/**
|
||||
* @param \Generator<array-key, int|string|null, string, void> $generator
|
||||
*
|
||||
* @throws InvalidDelimiterError If the generator yields an invalid delimiter.
|
||||
* @throws \Throwable If the generator throws.
|
||||
*/
|
||||
public function __construct(\Generator $generator)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
$this->delimiter = $this->filterDelimiter($this->generator->current());
|
||||
|
||||
if (!$this->generator->valid()) {
|
||||
$this->generator = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the generator parser and returns any remaining data in the internal buffer. Writing data after calling
|
||||
* this method will result in an error.
|
||||
*/
|
||||
final public function cancel(): string
|
||||
{
|
||||
$buffer = \implode($this->buffers);
|
||||
|
||||
$this->buffers = [];
|
||||
$this->generator = null;
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the parser can still receive more data to parse, false if it has ended and calling push
|
||||
* will throw an exception.
|
||||
*/
|
||||
final public function isValid(): bool
|
||||
{
|
||||
return $this->generator !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data to the internal buffer and tries to continue parsing.
|
||||
*
|
||||
* @param string $data Data to append to the internal buffer.
|
||||
*
|
||||
* @throws InvalidDelimiterError If the generator yields an invalid delimiter.
|
||||
* @throws \Error If parsing has already been cancelled.
|
||||
* @throws \Throwable If the generator throws.
|
||||
*/
|
||||
final public function push(string $data): void
|
||||
{
|
||||
if ($this->generator === null) {
|
||||
throw new \Error("The parser is no longer writable");
|
||||
}
|
||||
|
||||
$length = \strlen($data);
|
||||
if ($length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->bufferLength += $length;
|
||||
|
||||
try {
|
||||
do {
|
||||
if (\is_int($this->delimiter) && $this->bufferLength < $this->delimiter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($this->buffers)) {
|
||||
$this->buffers[] = $data;
|
||||
$data = \implode($this->buffers);
|
||||
$this->buffers = [];
|
||||
}
|
||||
|
||||
if (\is_int($this->delimiter)) {
|
||||
$cutAt = $retainFrom = $this->delimiter;
|
||||
} elseif (\is_string($this->delimiter)) {
|
||||
if (($cutAt = \strpos($data, $this->delimiter)) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$retainFrom = $cutAt + \strlen($this->delimiter);
|
||||
} else {
|
||||
$cutAt = $retainFrom = $this->bufferLength;
|
||||
}
|
||||
|
||||
if ($this->bufferLength > $cutAt) {
|
||||
$send = \substr($data, 0, $cutAt);
|
||||
$data = \substr($data, $retainFrom);
|
||||
} else {
|
||||
$send = $data;
|
||||
$data = '';
|
||||
}
|
||||
|
||||
$this->bufferLength -= $retainFrom;
|
||||
|
||||
$this->delimiter = $this->filterDelimiter($this->generator->send($send));
|
||||
|
||||
if (!$this->generator->valid()) {
|
||||
$this->generator = null;
|
||||
return;
|
||||
}
|
||||
} while ($this->bufferLength);
|
||||
} catch (\Throwable $exception) {
|
||||
$this->generator = null;
|
||||
throw $exception;
|
||||
} finally {
|
||||
if (\strlen($data)) {
|
||||
$this->buffers[] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $delimiter Value yielded from Generator.
|
||||
* @return int|string|null
|
||||
*/
|
||||
private function filterDelimiter($delimiter)
|
||||
{
|
||||
\assert($this->generator instanceof \Generator, "Invalid parser state");
|
||||
|
||||
if ($delimiter !== null
|
||||
&& (!\is_int($delimiter) || $delimiter <= 0)
|
||||
&& (!\is_string($delimiter) || !\strlen($delimiter))
|
||||
) {
|
||||
throw new InvalidDelimiterError(
|
||||
$this->generator,
|
||||
\sprintf(
|
||||
"Invalid value yielded: Expected NULL, an int greater than 0, or a non-empty string; %s given",
|
||||
\is_object($delimiter) ? \sprintf("instance of %s", \get_class($delimiter)) : \gettype($delimiter),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $delimiter;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Amp\Process\Internal\Posix;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Loop;
|
||||
use Amp\Process\Internal\ProcessHandle;
|
||||
|
||||
/** @internal */
|
||||
|
@ -32,4 +33,28 @@ final class Handle extends ProcessHandle
|
|||
|
||||
/** @var int */
|
||||
public $originalParentPid;
|
||||
|
||||
/** @var int */
|
||||
public $shellPid;
|
||||
|
||||
public function wait()
|
||||
{
|
||||
if ($this->shellPid === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pid = $this->shellPid;
|
||||
$this->shellPid = 0;
|
||||
|
||||
Loop::unreference(Loop::repeat(100, static function (string $watcherId) use ($pid) {
|
||||
if (!\extension_loaded('pcntl') || \pcntl_waitpid($pid, $status, \WNOHANG) !== 0) {
|
||||
Loop::cancel($watcherId);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->wait();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ final class Runner implements ProcessRunner
|
|||
} else {
|
||||
$handle->joinDeferred->resolve((int) \rtrim(@\stream_get_contents($stream)));
|
||||
}
|
||||
|
||||
$handle->wait();
|
||||
}
|
||||
|
||||
public static function onProcessStartExtraDataPipeReadable($watcher, $stream, $data)
|
||||
|
@ -102,6 +104,8 @@ final class Runner implements ProcessRunner
|
|||
throw new ProcessException("Could not get process status");
|
||||
}
|
||||
|
||||
$handle->shellPid = \proc_get_status($handle->proc)['pid'];
|
||||
|
||||
$stdinDeferred = new Deferred;
|
||||
$handle->stdin = new ProcessOutputStream($stdinDeferred->promise());
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2020 Soufiane Ghzal <sghzal@gmail.com>
|
||||
Copyright (c) 2020-2022 Graham Campbell <hello@gjcampbell.co.uk>
|
||||
Copyright (c) 2020-2022 Enrico Dias <enricodias@gmail.com>
|
||||
Copyright (c) 2020-2023 Graham Campbell <hello@gjcampbell.co.uk>
|
||||
Copyright (c) 2020-2023 Enrico Dias <enricodias@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"chrome-php/wrench": "^1.3",
|
||||
"php": "^7.4.15 || ^8.0.2",
|
||||
"chrome-php/wrench": "^1.5",
|
||||
"evenement/evenement": "^3.0.1",
|
||||
"monolog/monolog": "^1.27.1 || ^2.8 || ^3.2",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
|
@ -26,8 +26,8 @@
|
|||
"symfony/process": "^4.4 || ^5.0 || ^6.0"
|
||||
},
|
||||
"require-dev":{
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"phpunit/phpunit": "^9.5.23",
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^9.6.3 || ^10.0.12",
|
||||
"symfony/var-dumper": "^4.4 || ^5.0 || ^6.0"
|
||||
},
|
||||
"autoload":{
|
||||
|
|
|
@ -120,12 +120,18 @@ class Browser
|
|||
*/
|
||||
final public function sendCloseMessage(): void
|
||||
{
|
||||
if (!$this->connection->isConnected()) {
|
||||
$this->connection->getLogger()->debug('process: chrome already stopped, ignoring');
|
||||
|
||||
return;
|
||||
}
|
||||
$r = $this->connection->sendMessageSync(new Message('Browser.close'));
|
||||
if (!$r->isSuccessful()) {
|
||||
// log
|
||||
$this->connection->getLogger()->debug('process: ✗ could not close gracefully');
|
||||
throw new \Exception('cannot close, Browser.close not supported');
|
||||
}
|
||||
$this->connection->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,6 +187,23 @@ class Browser
|
|||
return \array_values($this->targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a target matching the type and title.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $title
|
||||
*/
|
||||
public function findTarget(string $type, string $title): ?Target
|
||||
{
|
||||
foreach ($this->targets as $target) {
|
||||
if ($target->getTargetInfo('type') === $type && $target->getTargetInfo('title') === $title) {
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $targetId
|
||||
*
|
||||
|
|
|
@ -294,6 +294,9 @@ class BrowserProcess implements LoggerAwareInterface
|
|||
// auto debug port
|
||||
'--remote-debugging-port=0',
|
||||
|
||||
// allow remote access
|
||||
'--remote-allow-origins=*',
|
||||
|
||||
// disable undesired features
|
||||
'--disable-background-networking',
|
||||
'--disable-background-timer-throttling',
|
||||
|
@ -344,6 +347,11 @@ class BrowserProcess implements LoggerAwareInterface
|
|||
$args[] = '--window-size='.\implode(',', $options['windowSize']);
|
||||
}
|
||||
|
||||
if (\array_key_exists('userCrashDumpsDir', $options)) {
|
||||
$args[] = '--enable-crash-reporter';
|
||||
$args[] = '--crash-dumps-dir='.$options['userCrashDumpsDir'];
|
||||
}
|
||||
|
||||
// sandbox mode - useful if you want to use chrome headless inside docker
|
||||
if (\array_key_exists('noSandbox', $options) && $options['noSandbox']) {
|
||||
$args[] = '--no-sandbox';
|
||||
|
@ -432,6 +440,9 @@ class BrowserProcess implements LoggerAwareInterface
|
|||
$this->logger->debug('process: ✓ accepted output');
|
||||
|
||||
return $matches[1];
|
||||
} elseif (\preg_match('/Cannot start http server for devtools\./', $output, $matches)) {
|
||||
$process->stop();
|
||||
throw new \RuntimeException('Devtools could not start');
|
||||
} else {
|
||||
// log
|
||||
$this->logger->debug('process: ignoring output:'.\trim($output));
|
||||
|
@ -446,6 +457,7 @@ class BrowserProcess implements LoggerAwareInterface
|
|||
|
||||
return Utils::tryWithTimeout($timeout, $generator($process));
|
||||
} catch (OperationTimedOut $e) {
|
||||
$process->stop();
|
||||
throw new \RuntimeException('Cannot start browser', 0, $e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ class BrowserFactory
|
|||
* - startupTimeout: Maximum time in seconds to wait for chrome to start (default: 30 sec)
|
||||
* - userAgent: User agent to use for the whole browser
|
||||
* - userDataDir: Chrome user data dir (default: a new empty dir is generated temporarily)
|
||||
* - userCrashDumpsDir: The directory crashpad should store dumps in (crash reporter will be enabled automatically)
|
||||
* - windowSize: Size of the window. ex: `[1920, 1080]` (default: none)
|
||||
*/
|
||||
protected $options = [];
|
||||
|
|
|
@ -267,7 +267,7 @@ class Connection extends EventEmitter implements LoggerAwareInterface
|
|||
public function sendMessageSync(Message $message, int $timeout = null): Response
|
||||
{
|
||||
$responseReader = $this->sendMessage($message);
|
||||
$response = $responseReader->waitForResponse($timeout ?? $this->sendSyncDefaultTimeout);
|
||||
$response = $responseReader->waitForResponse($timeout);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
|
|
@ -109,8 +109,7 @@ class ResponseReader
|
|||
return $this->getResponse();
|
||||
}
|
||||
|
||||
// default 2000ms
|
||||
$timeout = $timeout ?? 2000;
|
||||
$timeout = $timeout ?? $this->connection->getSendSyncDefaultTimeout();
|
||||
|
||||
return Utils::tryWithTimeout($timeout * 1000, $this->waitForResponseGenerator());
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class Session extends EventEmitter
|
|||
{
|
||||
$responseReader = $this->sendMessage($message);
|
||||
|
||||
$response = $responseReader->waitForResponse($timeout ?? $this->getConnection()->getSendSyncDefaultTimeout());
|
||||
$response = $responseReader->waitForResponse($timeout);
|
||||
|
||||
if (!$response) {
|
||||
throw new NoResponseAvailable('No response was sent in the given timeout');
|
||||
|
|
|
@ -12,14 +12,16 @@ class Dom extends Node
|
|||
public function __construct(Page $page)
|
||||
{
|
||||
$message = new Message('DOM.getDocument');
|
||||
$stream = $page->getSession()->sendMessage($message);
|
||||
$response = $stream->waitForResponse(1000);
|
||||
$response = $page->getSession()->sendMessageSync($message);
|
||||
|
||||
$rootNodeId = $response->getResultData('root')['nodeId'];
|
||||
|
||||
parent::__construct($page, $rootNodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[]
|
||||
*/
|
||||
public function search(string $selector): array
|
||||
{
|
||||
$message = new Message('DOM.performSearch', [
|
||||
|
|
|
@ -857,8 +857,10 @@ class Page
|
|||
*
|
||||
* @throws CommunicationException
|
||||
*/
|
||||
public function setHtml(string $html, int $timeout = 3000): void
|
||||
public function setHtml(string $html, int $timeout = 3000, string $eventName = self::LOAD): void
|
||||
{
|
||||
$time = \hrtime(true) / 1000 / 1000;
|
||||
|
||||
$this->getSession()->sendMessageSync(
|
||||
new Message(
|
||||
'Page.setDocumentContent',
|
||||
|
@ -866,10 +868,13 @@ class Page
|
|||
'frameId' => $this->getFrameManager()->getMainFrame()->getFrameId(),
|
||||
'html' => $html,
|
||||
]
|
||||
)
|
||||
),
|
||||
$timeout
|
||||
);
|
||||
|
||||
$this->waitForReload(self::LOAD, $timeout, '');
|
||||
$timeout -= (int) \floor((\hrtime(true) / 1000 / 1000) - $time);
|
||||
|
||||
$this->waitForReload($eventName, \max(0, $timeout), '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"php": "^7.4.15 || ^8.0.2",
|
||||
"ext-sockets": "*",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
"symfony/polyfill-php80": "^1.26"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"phpunit/phpunit": "^9.5.23"
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^9.6.3 || ^10.0.12"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -187,14 +187,7 @@ abstract class Protocol
|
|||
*/
|
||||
public function generateKey(): string
|
||||
{
|
||||
if (\extension_loaded('openssl')) {
|
||||
$key = \openssl_random_pseudo_bytes(16);
|
||||
} else {
|
||||
// SHA1 is 128 bit (= 16 bytes)
|
||||
$key = \sha1(\spl_object_hash($this).\mt_rand(0, \PHP_INT_MAX).\uniqid('', true), true);
|
||||
}
|
||||
|
||||
return \base64_encode($key);
|
||||
return \base64_encode(\random_bytes(16));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -382,7 +375,7 @@ abstract class Protocol
|
|||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getSuccessResponseHeaders($key)
|
||||
protected function getSuccessResponseHeaders(string $key): array
|
||||
{
|
||||
return [
|
||||
self::HEADER_UPGRADE => self::UPGRADE_VALUE,
|
||||
|
@ -398,13 +391,13 @@ abstract class Protocol
|
|||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc6455#section-4.2.2
|
||||
*
|
||||
* @param string $encoded_key
|
||||
* @param string $key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAcceptValue($encoded_key)
|
||||
protected function getAcceptValue(string $key): string
|
||||
{
|
||||
return \base64_encode(\sha1($encoded_key.self::MAGIC_GUID, true));
|
||||
return \base64_encode(\sha1($key.self::MAGIC_GUID, true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -437,7 +430,7 @@ abstract class Protocol
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseError($e, array $headers = [])
|
||||
public function getResponseError($e, array $headers = []): string
|
||||
{
|
||||
$code = false;
|
||||
|
||||
|
@ -463,24 +456,37 @@ abstract class Protocol
|
|||
return false;
|
||||
}
|
||||
|
||||
$headers = $this->getHeaders($response);
|
||||
$statusCode = $this->getStatusCode($response);
|
||||
|
||||
if (!isset($headers[self::HEADER_ACCEPT])) {
|
||||
throw new HandshakeException('No accept header receieved on handshake response');
|
||||
if (self::HTTP_SWITCHING_PROTOCOLS !== $statusCode) {
|
||||
$errorMessage = \explode("\n", \trim($this->getBody($response)), 2)[0];
|
||||
|
||||
throw new HandshakeException(\trim(\sprintf('Expected handshake response status code %d, but received %d. %s', self::HTTP_SWITCHING_PROTOCOLS, $statusCode, $errorMessage)));
|
||||
}
|
||||
|
||||
$accept = $headers[self::HEADER_ACCEPT];
|
||||
$acceptHeaderValue = $this->getHeaders($response)[self::HEADER_ACCEPT] ?? '';
|
||||
|
||||
if (!$accept) {
|
||||
throw new HandshakeException('Invalid accept header');
|
||||
if ('' === $acceptHeaderValue) {
|
||||
throw new HandshakeException('No accept header received on handshake response');
|
||||
}
|
||||
|
||||
$expected = $this->getAcceptValue($key);
|
||||
return $this->getEncodedHash($key) === $acceptHeaderValue;
|
||||
}
|
||||
|
||||
\preg_match('#Sec-WebSocket-Accept:\s(.*)$#imU', $response, $matches);
|
||||
$keyAccept = \trim($matches[1]);
|
||||
/**
|
||||
* Gets the status code from a full response.
|
||||
*
|
||||
* If there is no status line, we return 0.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getStatusCode(string $response): int
|
||||
{
|
||||
[$statusLine] = \explode("\r\n", $response, 2);
|
||||
|
||||
return $keyAccept === $this->getEncodedHash($key);
|
||||
[$protocol, $statusCode] = \explode(' ', $response, 2);
|
||||
|
||||
return (int) $statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -488,7 +494,7 @@ abstract class Protocol
|
|||
*
|
||||
* @return array<string, array>
|
||||
*/
|
||||
protected function getHeaders(string $response)
|
||||
protected function getHeaders(string $response): array
|
||||
{
|
||||
$parts = \explode("\r\n\r\n", $response, 2);
|
||||
|
||||
|
@ -500,16 +506,16 @@ abstract class Protocol
|
|||
|
||||
$return = [];
|
||||
foreach (\explode("\r\n", $headers) as $header) {
|
||||
$parts = \explode(': ', $header, 2);
|
||||
$parts = \explode(':', $header, 2);
|
||||
if (2 == \count($parts)) {
|
||||
[$name, $value] = $parts;
|
||||
if (!isset($return[$name])) {
|
||||
$return[$name] = $value;
|
||||
$return[$name] = \trim($value);
|
||||
} else {
|
||||
if (\is_array($return[$name])) {
|
||||
$return[$name][] = $value;
|
||||
$return[$name][] = \trim($value);
|
||||
} else {
|
||||
$return[$name] = [$return[$name], $value];
|
||||
$return[$name] = [$return[$name], \trim($value)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -518,6 +524,16 @@ abstract class Protocol
|
|||
return \array_change_key_case($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the body from a full response.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBody(string $response): string
|
||||
{
|
||||
return \explode("\r\n\r\n", $response, 2)[1] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an encoded hash for a key.
|
||||
*
|
||||
|
@ -525,7 +541,7 @@ abstract class Protocol
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncodedHash($key)
|
||||
public function getEncodedHash(string $key): string
|
||||
{
|
||||
return \base64_encode(\pack('H*', \sha1($key.self::MAGIC_GUID)));
|
||||
}
|
||||
|
@ -629,7 +645,7 @@ abstract class Protocol
|
|||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function getRequestHeaders(string $response)
|
||||
protected function getRequestHeaders(string $response): array
|
||||
{
|
||||
$eol = \stripos($response, "\r\n");
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"require": {
|
||||
"php": "^7.4",
|
||||
"phpstan/phpstan": "1.8.2"
|
||||
"phpstan/phpstan": "1.10.3"
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
|
|
|
@ -42,6 +42,9 @@ namespace Composer\Autoload;
|
|||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var ?string */
|
||||
private $vendorDir;
|
||||
|
||||
|
@ -106,6 +109,7 @@ class ClassLoader
|
|||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -425,7 +429,8 @@ class ClassLoader
|
|||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -555,18 +560,26 @@ class ClassLoader
|
|||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @private
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,21 +9,20 @@ return array(
|
|||
'e8aa6e4b5a1db2f56ae794f1505391a8' => $vendorDir . '/amphp/amp/lib/functions.php',
|
||||
'76cd0796156622033397994f25b0d8fc' => $vendorDir . '/amphp/amp/lib/Internal/functions.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'6cd5651c4fef5ed6b63e8d8b8ffbf3cc' => $vendorDir . '/amphp/byte-stream/lib/functions.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'3da389f428d8ee50333e4391c3f45046' => $vendorDir . '/amphp/serialization/src/functions.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'8dc56fe697ca93c4b40d876df1c94584' => $vendorDir . '/amphp/process/lib/functions.php',
|
||||
'3da389f428d8ee50333e4391c3f45046' => $vendorDir . '/amphp/serialization/src/functions.php',
|
||||
'bcb7d4fc55f4b1a7e10f5806723e9892' => $vendorDir . '/amphp/sync/src/functions.php',
|
||||
'e187e371b30897d6dc51cac6a8c94ff6' => $vendorDir . '/amphp/sync/src/ConcurrentIterator/functions.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'430de19db8b7ee88fdbe5c545d82d33d' => $vendorDir . '/amphp/parallel/lib/Context/functions.php',
|
||||
'888e1afeed2e8d13ef5a662692091e6e' => $vendorDir . '/amphp/parallel/lib/Sync/functions.php',
|
||||
'384cf4f2eb4d2f896db72315a76066ad' => $vendorDir . '/amphp/parallel/lib/Worker/functions.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php',
|
||||
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'861372841bb4b8ba9fdd215894666f40' => $vendorDir . '/amphp/parallel-functions/src/functions.php',
|
||||
|
|
|
@ -22,24 +22,27 @@ return array(
|
|||
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
|
||||
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||
'PandoraFMS\\Enterprise\\' => array($baseDir . '/enterprise/include/lib'),
|
||||
'PandoraFMS\\' => array($baseDir . '/include/lib'),
|
||||
'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
|
||||
'Mpdf\\' => array($vendorDir . '/mpdf/mpdf/src'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'Models\\' => array($baseDir . '/include/rest-api/models'),
|
||||
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
|
||||
'Laminas\\Json\\' => array($vendorDir . '/laminas/laminas-json/src'),
|
||||
'Http\\Message\\' => array($vendorDir . '/php-http/message-factory/src'),
|
||||
'HeadlessChromium\\' => array($vendorDir . '/chrome-php/chrome/src'),
|
||||
'Halfpastfour\\Collection\\' => array($vendorDir . '/halfpastfouram/collection/src'),
|
||||
'Enterprise\\Models\\' => array($baseDir . '/enterprise/include/rest-api/models'),
|
||||
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
|
||||
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/src'),
|
||||
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
|
||||
'Artica\\PHPChartJS\\' => array($vendorDir . '/artica/phpchartjs/src'),
|
||||
'Amp\\Sync\\' => array($vendorDir . '/amphp/sync/src'),
|
||||
'Amp\\Serialization\\' => array($vendorDir . '/amphp/serialization/src'),
|
||||
'Amp\\Process\\' => array($vendorDir . '/amphp/process/lib'),
|
||||
'Amp\\Parser\\' => array($vendorDir . '/amphp/parser/lib'),
|
||||
'Amp\\Parser\\' => array($vendorDir . '/amphp/parser/src'),
|
||||
'Amp\\Parallel\\' => array($vendorDir . '/amphp/parallel/lib'),
|
||||
'Amp\\ParallelFunctions\\' => array($vendorDir . '/amphp/parallel-functions/src'),
|
||||
'Amp\\ByteStream\\' => array($vendorDir . '/amphp/byte-stream/lib'),
|
||||
|
|
|
@ -33,25 +33,18 @@ class ComposerAutoloaderInit94a17e624d873685991e8ae888e00eb9
|
|||
|
||||
$loader->register(true);
|
||||
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit94a17e624d873685991e8ae888e00eb9::$files;
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire94a17e624d873685991e8ae888e00eb9($fileIdentifier, $file);
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit94a17e624d873685991e8ae888e00eb9::$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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileIdentifier
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire94a17e624d873685991e8ae888e00eb9($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,21 +10,20 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
'e8aa6e4b5a1db2f56ae794f1505391a8' => __DIR__ . '/..' . '/amphp/amp/lib/functions.php',
|
||||
'76cd0796156622033397994f25b0d8fc' => __DIR__ . '/..' . '/amphp/amp/lib/Internal/functions.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'6cd5651c4fef5ed6b63e8d8b8ffbf3cc' => __DIR__ . '/..' . '/amphp/byte-stream/lib/functions.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'3da389f428d8ee50333e4391c3f45046' => __DIR__ . '/..' . '/amphp/serialization/src/functions.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'8dc56fe697ca93c4b40d876df1c94584' => __DIR__ . '/..' . '/amphp/process/lib/functions.php',
|
||||
'3da389f428d8ee50333e4391c3f45046' => __DIR__ . '/..' . '/amphp/serialization/src/functions.php',
|
||||
'bcb7d4fc55f4b1a7e10f5806723e9892' => __DIR__ . '/..' . '/amphp/sync/src/functions.php',
|
||||
'e187e371b30897d6dc51cac6a8c94ff6' => __DIR__ . '/..' . '/amphp/sync/src/ConcurrentIterator/functions.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'430de19db8b7ee88fdbe5c545d82d33d' => __DIR__ . '/..' . '/amphp/parallel/lib/Context/functions.php',
|
||||
'888e1afeed2e8d13ef5a662692091e6e' => __DIR__ . '/..' . '/amphp/parallel/lib/Sync/functions.php',
|
||||
'384cf4f2eb4d2f896db72315a76066ad' => __DIR__ . '/..' . '/amphp/parallel/lib/Worker/functions.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'538ca81a9a966a6716601ecf48f4eaef' => __DIR__ . '/..' . '/opis/closure/functions.php',
|
||||
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'861372841bb4b8ba9fdd215894666f40' => __DIR__ . '/..' . '/amphp/parallel-functions/src/functions.php',
|
||||
|
@ -66,13 +65,10 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
'P' =>
|
||||
array (
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
'PandoraFMS\\Enterprise\\' => 22,
|
||||
'PandoraFMS\\' => 11,
|
||||
),
|
||||
'O' =>
|
||||
array (
|
||||
'Opis\\Closure\\' => 13,
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
'Mpdf\\' => 5,
|
||||
|
@ -81,10 +77,12 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
),
|
||||
'L' =>
|
||||
array (
|
||||
'Laravel\\SerializableClosure\\' => 28,
|
||||
'Laminas\\Json\\' => 13,
|
||||
),
|
||||
'H' =>
|
||||
array (
|
||||
'Http\\Message\\' => 13,
|
||||
'HeadlessChromium\\' => 17,
|
||||
'Halfpastfour\\Collection\\' => 24,
|
||||
),
|
||||
|
@ -95,6 +93,7 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
),
|
||||
'D' =>
|
||||
array (
|
||||
'Doctrine\\Deprecations\\' => 22,
|
||||
'Doctrine\\Common\\Lexer\\' => 22,
|
||||
'DeepCopy\\' => 9,
|
||||
),
|
||||
|
@ -178,6 +177,10 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/src',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
),
|
||||
'PandoraFMS\\Enterprise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/enterprise/include/lib',
|
||||
|
@ -186,10 +189,6 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
array (
|
||||
0 => __DIR__ . '/../..' . '/include/lib',
|
||||
),
|
||||
'Opis\\Closure\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/opis/closure/src',
|
||||
),
|
||||
'Mpdf\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/mpdf/mpdf/src',
|
||||
|
@ -202,10 +201,18 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
array (
|
||||
0 => __DIR__ . '/../..' . '/include/rest-api/models',
|
||||
),
|
||||
'Laravel\\SerializableClosure\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/laravel/serializable-closure/src',
|
||||
),
|
||||
'Laminas\\Json\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/laminas/laminas-json/src',
|
||||
),
|
||||
'Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/message-factory/src',
|
||||
),
|
||||
'HeadlessChromium\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/chrome-php/chrome/src',
|
||||
|
@ -222,9 +229,13 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/egulias/email-validator/src',
|
||||
),
|
||||
'Doctrine\\Deprecations\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/src',
|
||||
),
|
||||
'DeepCopy\\' =>
|
||||
array (
|
||||
|
@ -248,7 +259,7 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
|
|||
),
|
||||
'Amp\\Parser\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/amphp/parser/lib',
|
||||
0 => __DIR__ . '/..' . '/amphp/parser/src',
|
||||
),
|
||||
'Amp\\Parallel\\' =>
|
||||
array (
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,7 @@
|
|||
'name' => 'pandorafms/console',
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => '8d950c3672332ae0f9823f784fe4b5b23704e18f',
|
||||
'reference' => 'f68cd81d35010b07e3e022ca1141a3fb639e6834',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
@ -29,36 +29,36 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'amphp/parallel' => array(
|
||||
'pretty_version' => 'v1.4.1',
|
||||
'version' => '1.4.1.0',
|
||||
'reference' => 'fbc128383c1ffb3823866f71b88d8c4722a25ce9',
|
||||
'pretty_version' => 'v1.4.3',
|
||||
'version' => '1.4.3.0',
|
||||
'reference' => '3aac213ba7858566fd83d38ccb85b91b2d652cb0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../amphp/parallel',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'amphp/parallel-functions' => array(
|
||||
'pretty_version' => 'v1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => 'af9795d51abfafc3676cbe7e17965479491abaad',
|
||||
'pretty_version' => 'v1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => '04e92fcacfc921a56dfe12c23b3265e62593a7cb',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../amphp/parallel-functions',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'amphp/parser' => array(
|
||||
'pretty_version' => 'v1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => 'f83e68f03d5b8e8e0365b8792985a7f341c57ae1',
|
||||
'pretty_version' => 'v1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => 'ff1de4144726c5dad5fab97f66692ebe8de3e151',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../amphp/parser',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'amphp/process' => array(
|
||||
'pretty_version' => 'v1.1.3',
|
||||
'version' => '1.1.3.0',
|
||||
'reference' => 'f09e3ed3b0a953ccbfff1140f12be4a884f0aa83',
|
||||
'pretty_version' => 'v1.1.4',
|
||||
'version' => '1.1.4.0',
|
||||
'reference' => '76e9495fd6818b43a20167cb11d8a67f7744ee0f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../amphp/process',
|
||||
'aliases' => array(),
|
||||
|
@ -85,43 +85,52 @@
|
|||
'artica/phpchartjs' => array(
|
||||
'pretty_version' => 'v1.0.2',
|
||||
'version' => '1.0.2.0',
|
||||
'reference' => '4957e7cd699e50cee8e0ba7304e1423aafb2cad2',
|
||||
'reference' => '681980c084ad505f9dc811d3d1f02ffc9442ccee',
|
||||
'type' => 'package',
|
||||
'install_path' => __DIR__ . '/../artica/phpchartjs',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'chrome-php/chrome' => array(
|
||||
'pretty_version' => 'v1.7.1',
|
||||
'version' => '1.7.1.0',
|
||||
'reference' => '5783c749b2ee385d1c481b0906f1b8acef0296e4',
|
||||
'pretty_version' => 'v1.8.1',
|
||||
'version' => '1.8.1.0',
|
||||
'reference' => '9146c37c14d99cff3e4d9cd2f5ba843d149b3cbc',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../chrome-php/chrome',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'chrome-php/wrench' => array(
|
||||
'pretty_version' => 'v1.3.0',
|
||||
'version' => '1.3.0.0',
|
||||
'reference' => '68b8282d5d0d54a519c3212ee3e4c35bef40b7d9',
|
||||
'pretty_version' => 'v1.5.0',
|
||||
'version' => '1.5.0.0',
|
||||
'reference' => '725246324339e5fd5d798361b561e81004324f96',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../chrome-php/wrench',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'doctrine/deprecations' => array(
|
||||
'pretty_version' => 'v1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../doctrine/deprecations',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'doctrine/lexer' => array(
|
||||
'pretty_version' => '1.2.2',
|
||||
'version' => '1.2.2.0',
|
||||
'reference' => '9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c',
|
||||
'pretty_version' => '2.1.0',
|
||||
'version' => '2.1.0.0',
|
||||
'reference' => '39ab8fcf5a51ce4b85ca97c7a7d033eb12831124',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../doctrine/lexer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'egulias/email-validator' => array(
|
||||
'pretty_version' => '3.1.2',
|
||||
'version' => '3.1.2.0',
|
||||
'reference' => 'ee0db30118f661fb166bcffbf5d82032df484697',
|
||||
'pretty_version' => '3.2.5',
|
||||
'version' => '3.2.5.0',
|
||||
'reference' => 'b531a2311709443320c786feb4519cfaf94af796',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../egulias/email-validator',
|
||||
'aliases' => array(),
|
||||
|
@ -154,46 +163,46 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'laravel/serializable-closure' => array(
|
||||
'pretty_version' => 'v1.3.0',
|
||||
'version' => '1.3.0.0',
|
||||
'reference' => 'f23fe9d4e95255dacee1bf3525e0810d1a1b0f37',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../laravel/serializable-closure',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'monolog/monolog' => array(
|
||||
'pretty_version' => '2.8.0',
|
||||
'version' => '2.8.0.0',
|
||||
'reference' => '720488632c590286b88b80e62aa3d3d551ad4a50',
|
||||
'pretty_version' => '2.9.1',
|
||||
'version' => '2.9.1.0',
|
||||
'reference' => 'f259e2b15fb95494c83f52d3caad003bbf5ffaa1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../monolog/monolog',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'mpdf/mpdf' => array(
|
||||
'pretty_version' => 'v8.0.15',
|
||||
'version' => '8.0.15.0',
|
||||
'reference' => 'd8a5294a6cc2e814c4157aecc8d7ac25014b18ed',
|
||||
'pretty_version' => 'v8.1.5',
|
||||
'version' => '8.1.5.0',
|
||||
'reference' => 'c264ce27af0d794ecd04e201b7e37a06b8a9d720',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../mpdf/mpdf',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'myclabs/deep-copy' => array(
|
||||
'pretty_version' => '1.10.2',
|
||||
'version' => '1.10.2.0',
|
||||
'reference' => '776f831124e9c62e1a2c601ecc52e776d8bb7220',
|
||||
'pretty_version' => '1.11.1',
|
||||
'version' => '1.11.1.0',
|
||||
'reference' => '7284c22080590fb39f2ffa3e9057f10a4ddd0e0c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../myclabs/deep-copy',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'opis/closure' => array(
|
||||
'pretty_version' => '3.6.3',
|
||||
'version' => '3.6.3.0',
|
||||
'reference' => '3d81e4309d2a927abbe66df935f4bb60082805ad',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../opis/closure',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'pandorafms/console' => array(
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => '8d950c3672332ae0f9823f784fe4b5b23704e18f',
|
||||
'reference' => 'f68cd81d35010b07e3e022ca1141a3fb639e6834',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
@ -208,6 +217,24 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-http/message-factory' => array(
|
||||
'pretty_version' => 'v1.0.2',
|
||||
'version' => '1.0.2.0',
|
||||
'reference' => 'a478cb11f66a6ac48d8954216cfed9aa06a501a1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-http/message-factory',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-message' => array(
|
||||
'pretty_version' => '1.1',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-message',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/log' => array(
|
||||
'pretty_version' => '2.0.0',
|
||||
'version' => '2.0.0.0',
|
||||
|
@ -224,9 +251,9 @@
|
|||
),
|
||||
),
|
||||
'setasign/fpdi' => array(
|
||||
'pretty_version' => 'v2.3.6',
|
||||
'version' => '2.3.6.0',
|
||||
'reference' => '6231e315f73e4f62d72b73f3d6d78ff0eed93c31',
|
||||
'pretty_version' => 'v2.3.7',
|
||||
'version' => '2.3.7.0',
|
||||
'reference' => 'bccc892d5fa1f48c43f8ba7db5ed4ba6f30c8c05',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../setasign/fpdi',
|
||||
'aliases' => array(),
|
||||
|
@ -242,9 +269,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/filesystem' => array(
|
||||
'pretty_version' => 'v5.4.13',
|
||||
'version' => '5.4.13.0',
|
||||
'reference' => 'ac09569844a9109a5966b9438fc29113ce77cf51',
|
||||
'pretty_version' => 'v6.0.19',
|
||||
'version' => '6.0.19.0',
|
||||
'reference' => '3d49eec03fda1f0fc19b7349fbbe55ebc1004214',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/filesystem',
|
||||
'aliases' => array(),
|
||||
|
@ -260,27 +287,27 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-iconv' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => 'f1aed619e28cb077fc83fac8c4c0383578356e40',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '927013f3aac555983a5059aada98e1907d842695',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-idn' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => '749045c69efb97c70d25d7463abba812e91f3a44',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '639084e360537a19f9ee352433b84ce831f3d2da',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-normalizer' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => '8590a5f561694770bdcd3f9b5c69dde6945028e8',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
||||
'aliases' => array(),
|
||||
|
@ -296,9 +323,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php72' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => '9a142215a36a3888e30d0a9eeea9766764e96976',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '869329b1e9894268a8a61dabb69153029b7a8c97',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php72',
|
||||
'aliases' => array(),
|
||||
|
@ -314,9 +341,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/process' => array(
|
||||
'pretty_version' => 'v5.4.11',
|
||||
'version' => '5.4.11.0',
|
||||
'reference' => '6e75fe6874cbc7e4773d049616ab450eff537bf1',
|
||||
'pretty_version' => 'v6.0.19',
|
||||
'version' => '6.0.19.0',
|
||||
'reference' => '2114fd60f26a296cc403a7939ab91478475a33d4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/process',
|
||||
'aliases' => array(),
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 80000)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
|
||||
if (!(PHP_VERSION_ID >= 80002)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.2". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2019 Zindex Software
|
||||
Copyright (c) 2020-2021 Doctrine Project
|
||||
|
||||
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:
|
||||
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.
|
||||
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,154 @@
|
|||
# Doctrine Deprecations
|
||||
|
||||
A small (side-effect free by default) layer on top of
|
||||
`trigger_error(E_USER_DEPRECATED)` or PSR-3 logging.
|
||||
|
||||
- no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under
|
||||
- options to avoid having to rely on error handlers global state by using PSR-3 logging
|
||||
- deduplicate deprecation messages to avoid excessive triggering and reduce overhead
|
||||
|
||||
We recommend to collect Deprecations using a PSR logger instead of relying on
|
||||
the global error handler.
|
||||
|
||||
## Usage from consumer perspective:
|
||||
|
||||
Enable Doctrine deprecations to be sent to a PSR3 logger:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
|
||||
```
|
||||
|
||||
Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)`
|
||||
messages.
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::enableWithTriggerError();
|
||||
```
|
||||
|
||||
If you only want to enable deprecation tracking, without logging or calling `trigger_error` then call:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
|
||||
```
|
||||
|
||||
Tracking is enabled with all three modes and provides access to all triggered
|
||||
deprecations and their individual count:
|
||||
|
||||
```php
|
||||
$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations();
|
||||
|
||||
foreach ($deprecations as $identifier => $count) {
|
||||
echo $identifier . " was triggered " . $count . " times\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Suppressing Specific Deprecations
|
||||
|
||||
Disable triggering about specific deprecations:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier");
|
||||
```
|
||||
|
||||
Disable all deprecations from a package
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm");
|
||||
```
|
||||
|
||||
### Other Operations
|
||||
|
||||
When used within PHPUnit or other tools that could collect multiple instances of the same deprecations
|
||||
the deduplication can be disabled:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::withoutDeduplication();
|
||||
```
|
||||
|
||||
Disable deprecation tracking again:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::disable();
|
||||
```
|
||||
|
||||
## Usage from a library/producer perspective:
|
||||
|
||||
When you want to unconditionally trigger a deprecation even when called
|
||||
from the library itself then the `trigger` method is the way to go:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::trigger(
|
||||
"doctrine/orm",
|
||||
"https://link/to/deprecations-description",
|
||||
"message"
|
||||
);
|
||||
```
|
||||
|
||||
If variable arguments are provided at the end, they are used with `sprintf` on
|
||||
the message.
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::trigger(
|
||||
"doctrine/orm",
|
||||
"https://github.com/doctrine/orm/issue/1234",
|
||||
"message %s %d",
|
||||
"foo",
|
||||
1234
|
||||
);
|
||||
```
|
||||
|
||||
When you want to trigger a deprecation only when it is called by a function
|
||||
outside of the current package, but not trigger when the package itself is the cause,
|
||||
then use:
|
||||
|
||||
```php
|
||||
\Doctrine\Deprecations\Deprecation::triggerIfCalledFromOutside(
|
||||
"doctrine/orm",
|
||||
"https://link/to/deprecations-description",
|
||||
"message"
|
||||
);
|
||||
```
|
||||
|
||||
Based on the issue link each deprecation message is only triggered once per
|
||||
request.
|
||||
|
||||
A limited stacktrace is included in the deprecation message to find the
|
||||
offending location.
|
||||
|
||||
Note: A producer/library should never call `Deprecation::enableWith` methods
|
||||
and leave the decision how to handle deprecations to application and
|
||||
frameworks.
|
||||
|
||||
## Usage in PHPUnit tests
|
||||
|
||||
There is a `VerifyDeprecations` trait that you can use to make assertions on
|
||||
the occurrence of deprecations within a test.
|
||||
|
||||
```php
|
||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
||||
|
||||
class MyTest extends TestCase
|
||||
{
|
||||
use VerifyDeprecations;
|
||||
|
||||
public function testSomethingDeprecation()
|
||||
{
|
||||
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
|
||||
|
||||
triggerTheCodeWithDeprecation();
|
||||
}
|
||||
|
||||
public function testSomethingDeprecationFixed()
|
||||
{
|
||||
$this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
|
||||
|
||||
triggerTheCodeWithoutDeprecation();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What is a deprecation identifier?
|
||||
|
||||
An identifier for deprecations is just a link to any resource, most often a
|
||||
Github Issue or Pull Request explaining the deprecation and potentially its
|
||||
alternative.
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"type": "library",
|
||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.5",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"doctrine/coding-standard": "^9"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"DeprecationTests\\": "test_fixtures/src",
|
||||
"Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
266
pandora_console/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php
vendored
Normal file
266
pandora_console/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php
vendored
Normal file
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Deprecations;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_reduce;
|
||||
use function debug_backtrace;
|
||||
use function sprintf;
|
||||
use function strpos;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function trigger_error;
|
||||
|
||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Manages Deprecation logging in different ways.
|
||||
*
|
||||
* By default triggered exceptions are not logged.
|
||||
*
|
||||
* To enable different deprecation logging mechanisms you can call the
|
||||
* following methods:
|
||||
*
|
||||
* - Minimal collection of deprecations via getTriggeredDeprecations()
|
||||
* \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
|
||||
*
|
||||
* - Uses @trigger_error with E_USER_DEPRECATED
|
||||
* \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
|
||||
*
|
||||
* - Sends deprecation messages via a PSR-3 logger
|
||||
* \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
|
||||
*
|
||||
* Packages that trigger deprecations should use the `trigger()` or
|
||||
* `triggerIfCalledFromOutside()` methods.
|
||||
*/
|
||||
class Deprecation
|
||||
{
|
||||
private const TYPE_NONE = 0;
|
||||
private const TYPE_TRACK_DEPRECATIONS = 1;
|
||||
private const TYPE_TRIGGER_ERROR = 2;
|
||||
private const TYPE_PSR_LOGGER = 4;
|
||||
|
||||
/** @var int */
|
||||
private static $type = self::TYPE_NONE;
|
||||
|
||||
/** @var LoggerInterface|null */
|
||||
private static $logger;
|
||||
|
||||
/** @var array<string,bool> */
|
||||
private static $ignoredPackages = [];
|
||||
|
||||
/** @var array<string,int> */
|
||||
private static $ignoredLinks = [];
|
||||
|
||||
/** @var bool */
|
||||
private static $deduplication = true;
|
||||
|
||||
/**
|
||||
* Trigger a deprecation for the given package and identfier.
|
||||
*
|
||||
* The link should point to a Github issue or Wiki entry detailing the
|
||||
* deprecation. It is additionally used to de-duplicate the trigger of the
|
||||
* same deprecation during a request.
|
||||
*
|
||||
* @param mixed $args
|
||||
*/
|
||||
public static function trigger(string $package, string $link, string $message, ...$args): void
|
||||
{
|
||||
if (self::$type === self::TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists($link, self::$ignoredLinks)) {
|
||||
self::$ignoredLinks[$link]++;
|
||||
} else {
|
||||
self::$ignoredLinks[$link] = 1;
|
||||
}
|
||||
|
||||
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(self::$ignoredPackages[$package])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
$message = sprintf($message, ...$args);
|
||||
|
||||
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a deprecation for the given package and identifier when called from outside.
|
||||
*
|
||||
* "Outside" means we assume that $package is currently installed as a
|
||||
* dependency and the caller is not a file in that package. When $package
|
||||
* is installed as a root package then deprecations triggered from the
|
||||
* tests folder are also considered "outside".
|
||||
*
|
||||
* This deprecation method assumes that you are using Composer to install
|
||||
* the dependency and are using the default /vendor/ folder and not a
|
||||
* Composer plugin to change the install location. The assumption is also
|
||||
* that $package is the exact composer packge name.
|
||||
*
|
||||
* Compared to {@link trigger()} this method causes some overhead when
|
||||
* deprecation tracking is enabled even during deduplication, because it
|
||||
* needs to call {@link debug_backtrace()}
|
||||
*
|
||||
* @param mixed $args
|
||||
*/
|
||||
public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
|
||||
{
|
||||
if (self::$type === self::TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
// first check that the caller is not from a tests folder, in which case we always let deprecations pass
|
||||
if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
|
||||
$path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR;
|
||||
|
||||
if (strpos($backtrace[0]['file'], $path) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($backtrace[1]['file'], $path) !== false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($link, self::$ignoredLinks)) {
|
||||
self::$ignoredLinks[$link]++;
|
||||
} else {
|
||||
self::$ignoredLinks[$link] = 1;
|
||||
}
|
||||
|
||||
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(self::$ignoredPackages[$package])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf($message, ...$args);
|
||||
|
||||
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $backtrace
|
||||
*/
|
||||
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
|
||||
{
|
||||
if ((self::$type & self::TYPE_PSR_LOGGER) > 0) {
|
||||
$context = [
|
||||
'file' => $backtrace[0]['file'],
|
||||
'line' => $backtrace[0]['line'],
|
||||
'package' => $package,
|
||||
'link' => $link,
|
||||
];
|
||||
|
||||
self::$logger->notice($message, $context);
|
||||
}
|
||||
|
||||
if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message .= sprintf(
|
||||
' (%s:%d called by %s:%d, %s, package %s)',
|
||||
self::basename($backtrace[0]['file']),
|
||||
$backtrace[0]['line'],
|
||||
self::basename($backtrace[1]['file']),
|
||||
$backtrace[1]['line'],
|
||||
$link,
|
||||
$package
|
||||
);
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-local-aware version of PHPs basename function.
|
||||
*/
|
||||
private static function basename(string $filename): string
|
||||
{
|
||||
$pos = strrpos($filename, DIRECTORY_SEPARATOR);
|
||||
|
||||
if ($pos === false) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
return substr($filename, $pos + 1);
|
||||
}
|
||||
|
||||
public static function enableTrackingDeprecations(): void
|
||||
{
|
||||
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
|
||||
}
|
||||
|
||||
public static function enableWithTriggerError(): void
|
||||
{
|
||||
self::$type |= self::TYPE_TRIGGER_ERROR;
|
||||
}
|
||||
|
||||
public static function enableWithPsrLogger(LoggerInterface $logger): void
|
||||
{
|
||||
self::$type |= self::TYPE_PSR_LOGGER;
|
||||
self::$logger = $logger;
|
||||
}
|
||||
|
||||
public static function withoutDeduplication(): void
|
||||
{
|
||||
self::$deduplication = false;
|
||||
}
|
||||
|
||||
public static function disable(): void
|
||||
{
|
||||
self::$type = self::TYPE_NONE;
|
||||
self::$logger = null;
|
||||
self::$deduplication = true;
|
||||
|
||||
foreach (self::$ignoredLinks as $link => $count) {
|
||||
self::$ignoredLinks[$link] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function ignorePackage(string $packageName): void
|
||||
{
|
||||
self::$ignoredPackages[$packageName] = true;
|
||||
}
|
||||
|
||||
public static function ignoreDeprecations(string ...$links): void
|
||||
{
|
||||
foreach ($links as $link) {
|
||||
self::$ignoredLinks[$link] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getUniqueTriggeredDeprecationsCount(): int
|
||||
{
|
||||
return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) {
|
||||
return $carry + $count;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns each triggered deprecation link identifier and the amount of occurrences.
|
||||
*
|
||||
* @return array<string,int>
|
||||
*/
|
||||
public static function getTriggeredDeprecations(): array
|
||||
{
|
||||
return self::$ignoredLinks;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Deprecations\PHPUnit;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
trait VerifyDeprecations
|
||||
{
|
||||
/** @var array<string,int> */
|
||||
private $doctrineDeprecationsExpectations = [];
|
||||
|
||||
/** @var array<string,int> */
|
||||
private $doctrineNoDeprecationsExpectations = [];
|
||||
|
||||
public function expectDeprecationWithIdentifier(string $identifier): void
|
||||
{
|
||||
$this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
}
|
||||
|
||||
public function expectNoDeprecationWithIdentifier(string $identifier): void
|
||||
{
|
||||
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @before
|
||||
*/
|
||||
public function enableDeprecationTracking(): void
|
||||
{
|
||||
Deprecation::enableTrackingDeprecations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @after
|
||||
*/
|
||||
public function verifyDeprecationsAreTriggered(): void
|
||||
{
|
||||
foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {
|
||||
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
|
||||
$this->assertTrue(
|
||||
$actualCount > $expectation,
|
||||
sprintf(
|
||||
"Expected deprecation with identifier '%s' was not triggered by code executed in test.",
|
||||
$identifier
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) {
|
||||
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
|
||||
$this->assertTrue(
|
||||
$actualCount === $expectation,
|
||||
sprintf(
|
||||
"Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.",
|
||||
$identifier
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset>
|
||||
<arg name="basepath" value="."/>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg name="parallel" value="80"/>
|
||||
<arg name="cache" value=".phpcs-cache"/>
|
||||
<arg name="colors"/>
|
||||
|
||||
<!-- Ignore warnings, show progress of the run and show sniff names -->
|
||||
<arg value="nps"/>
|
||||
|
||||
<config name="php_version" value="70100"/>
|
||||
|
||||
<!-- Directories to be checked -->
|
||||
<file>lib</file>
|
||||
<file>tests</file>
|
||||
|
||||
<!-- Include full Doctrine Coding Standard -->
|
||||
<rule ref="Doctrine">
|
||||
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint" />
|
||||
</rule>
|
||||
</ruleset>
|
|
@ -0,0 +1,14 @@
|
|||
Note about upgrading: Doctrine uses static and runtime mechanisms to raise
|
||||
awareness about deprecated code.
|
||||
|
||||
- Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or
|
||||
Static Analysis tools (like Psalm, phpstan)
|
||||
- Use of our low-overhead runtime deprecation API, details:
|
||||
https://github.com/doctrine/deprecations/
|
||||
|
||||
# Upgrade to 2.0.0
|
||||
|
||||
`AbstractLexer::glimpse()` and `AbstractLexer::peek()` now return
|
||||
instances of `Doctrine\Common\Lexer\Token`, which is an array-like class
|
||||
Using it as an array is deprecated in favor of using properties of that class.
|
||||
Using `count()` on it is deprecated with no replacement.
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "doctrine/lexer",
|
||||
"type": "library",
|
||||
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"php",
|
||||
"parser",
|
||||
|
@ -9,27 +10,41 @@
|
|||
"annotations",
|
||||
"docblock"
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
|
||||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Johannes Schmitt",
|
||||
"email": "schmittjoh@gmail.com"
|
||||
}
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
"php": "^7.1 || ^8.0",
|
||||
"doctrine/deprecations": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpstan/phpstan": "1.3",
|
||||
"doctrine/coding-standard": "^9 || ^10",
|
||||
"phpstan/phpstan": "^1.3",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
||||
"vimeo/psalm": "^4.11"
|
||||
"psalm/plugin-phpunit": "^0.18.3",
|
||||
"vimeo/psalm": "^4.11 || ^5.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" }
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\Lexer\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Doctrine\\Tests\\": "tests/Doctrine" }
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\Common\\Lexer\\": "tests"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="5"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="lib/Doctrine/Common/Lexer" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
</psalm>
|
|
@ -5,9 +5,10 @@ declare(strict_types=1);
|
|||
namespace Doctrine\Common\Lexer;
|
||||
|
||||
use ReflectionClass;
|
||||
use UnitEnum;
|
||||
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function preg_split;
|
||||
use function sprintf;
|
||||
use function substr;
|
||||
|
@ -18,6 +19,9 @@ use const PREG_SPLIT_OFFSET_CAPTURE;
|
|||
|
||||
/**
|
||||
* Base class for writing simple lexers, i.e. for creating small DSLs.
|
||||
*
|
||||
* @template T of UnitEnum|string|int
|
||||
* @template V of string|int
|
||||
*/
|
||||
abstract class AbstractLexer
|
||||
{
|
||||
|
@ -31,14 +35,7 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Array of scanned tokens.
|
||||
*
|
||||
* Each token is an associative array containing three items:
|
||||
* - 'value' : the string value of the token in the input string
|
||||
* - 'type' : the type of the token (identifier, numeric, string, input
|
||||
* parameter, none)
|
||||
* - 'position' : the position of the token in the input string
|
||||
*
|
||||
* @var mixed[][]
|
||||
* @psalm-var list<array{value: string, type: string|int|null, position: int}>
|
||||
* @var list<Token<T, V>>
|
||||
*/
|
||||
private $tokens = [];
|
||||
|
||||
|
@ -60,7 +57,7 @@ abstract class AbstractLexer
|
|||
* The next token in the input.
|
||||
*
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{value: string, type: string|int|null, position: int}|null
|
||||
* @psalm-var Token<T, V>|null
|
||||
*/
|
||||
public $lookahead;
|
||||
|
||||
|
@ -68,7 +65,7 @@ abstract class AbstractLexer
|
|||
* The last matched/seen token.
|
||||
*
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{value: string, type: string|int|null, position: int}|null
|
||||
* @psalm-var Token<T, V>|null
|
||||
*/
|
||||
public $token;
|
||||
|
||||
|
@ -148,31 +145,37 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Checks whether a given token matches the current lookahead.
|
||||
*
|
||||
* @param int|string $token
|
||||
* @param T $type
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-assert-if-true !=null $this->lookahead
|
||||
*/
|
||||
public function isNextToken($token)
|
||||
public function isNextToken($type)
|
||||
{
|
||||
return $this->lookahead !== null && $this->lookahead['type'] === $token;
|
||||
return $this->lookahead !== null && $this->lookahead->isA($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any of the given tokens matches the current lookahead.
|
||||
*
|
||||
* @param string[] $tokens
|
||||
* @param list<T> $types
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-assert-if-true !=null $this->lookahead
|
||||
*/
|
||||
public function isNextTokenAny(array $tokens)
|
||||
public function isNextTokenAny(array $types)
|
||||
{
|
||||
return $this->lookahead !== null && in_array($this->lookahead['type'], $tokens, true);
|
||||
return $this->lookahead !== null && $this->lookahead->isA(...$types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to the next token in the input string.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-assert-if-true !null $this->lookahead
|
||||
*/
|
||||
public function moveNext()
|
||||
{
|
||||
|
@ -187,13 +190,13 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Tells the lexer to skip input tokens until it sees a token with the given value.
|
||||
*
|
||||
* @param string $type The token type to skip until.
|
||||
* @param T $type The token type to skip until.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function skipUntil($type)
|
||||
{
|
||||
while ($this->lookahead !== null && $this->lookahead['type'] !== $type) {
|
||||
while ($this->lookahead !== null && ! $this->lookahead->isA($type)) {
|
||||
$this->moveNext();
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +204,7 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Checks if given value is identical to the given token.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $value
|
||||
* @param int|string $token
|
||||
*
|
||||
* @return bool
|
||||
|
@ -215,7 +218,7 @@ abstract class AbstractLexer
|
|||
* Moves the lookahead token forward.
|
||||
*
|
||||
* @return mixed[]|null The next token or NULL if there are no more tokens ahead.
|
||||
* @psalm-return array{value: string, type: string|int|null, position: int}|null
|
||||
* @psalm-return Token<T, V>|null
|
||||
*/
|
||||
public function peek()
|
||||
{
|
||||
|
@ -230,7 +233,7 @@ abstract class AbstractLexer
|
|||
* Peeks at the next token, returns it and immediately resets the peek.
|
||||
*
|
||||
* @return mixed[]|null The next token or NULL if there are no more tokens ahead.
|
||||
* @psalm-return array{value: string, type: string|int|null, position: int}|null
|
||||
* @psalm-return Token<T, V>|null
|
||||
*/
|
||||
public function glimpse()
|
||||
{
|
||||
|
@ -268,26 +271,32 @@ abstract class AbstractLexer
|
|||
|
||||
foreach ($matches as $match) {
|
||||
// Must remain before 'value' assignment since it can change content
|
||||
$type = $this->getType($match[0]);
|
||||
$firstMatch = $match[0];
|
||||
$type = $this->getType($firstMatch);
|
||||
|
||||
$this->tokens[] = [
|
||||
'value' => $match[0],
|
||||
'type' => $type,
|
||||
'position' => $match[1],
|
||||
];
|
||||
$this->tokens[] = new Token(
|
||||
$firstMatch,
|
||||
$type,
|
||||
$match[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the literal for a given token.
|
||||
*
|
||||
* @param int|string $token
|
||||
* @param T $token
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getLiteral($token)
|
||||
{
|
||||
if ($token instanceof UnitEnum) {
|
||||
return get_class($token) . '::' . $token->name;
|
||||
}
|
||||
|
||||
$className = static::class;
|
||||
|
||||
$reflClass = new ReflectionClass($className);
|
||||
$constants = $reflClass->getConstants();
|
||||
|
||||
|
@ -329,7 +338,9 @@ abstract class AbstractLexer
|
|||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return int|string|null
|
||||
* @return T|null
|
||||
*
|
||||
* @param-out V $value
|
||||
*/
|
||||
abstract protected function getType(&$value);
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Common\Lexer;
|
||||
|
||||
use ArrayAccess;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use ReturnTypeWillChange;
|
||||
use UnitEnum;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* @template T of UnitEnum|string|int
|
||||
* @template V of string|int
|
||||
* @implements ArrayAccess<string,mixed>
|
||||
*/
|
||||
final class Token implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* The string value of the token in the input string
|
||||
*
|
||||
* @readonly
|
||||
* @var V
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* The type of the token (identifier, numeric, string, input parameter, none)
|
||||
*
|
||||
* @readonly
|
||||
* @var T|null
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The position of the token in the input string
|
||||
*
|
||||
* @readonly
|
||||
* @var int
|
||||
*/
|
||||
public $position;
|
||||
|
||||
/**
|
||||
* @param V $value
|
||||
* @param T|null $type
|
||||
*/
|
||||
public function __construct($value, $type, int $position)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->type = $type;
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
/** @param T ...$types */
|
||||
public function isA(...$types): bool
|
||||
{
|
||||
return in_array($this->type, $types, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the value, type or position property instead
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/lexer',
|
||||
'https://github.com/doctrine/lexer/pull/79',
|
||||
'Accessing %s properties via ArrayAccess is deprecated, use the value, type or position property instead',
|
||||
self::class
|
||||
);
|
||||
|
||||
return in_array($offset, ['value', 'type', 'position'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the value, type or position property instead
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param O $offset
|
||||
*
|
||||
* @return mixed
|
||||
* @psalm-return (
|
||||
* O is 'value'
|
||||
* ? V
|
||||
* : (
|
||||
* O is 'type'
|
||||
* ? T|null
|
||||
* : (
|
||||
* O is 'position'
|
||||
* ? int
|
||||
* : mixed
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @template O of array-key
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/lexer',
|
||||
'https://github.com/doctrine/lexer/pull/79',
|
||||
'Accessing %s properties via ArrayAccess is deprecated, use the value, type or position property instead',
|
||||
self::class
|
||||
);
|
||||
|
||||
return $this->$offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated no replacement planned
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/lexer',
|
||||
'https://github.com/doctrine/lexer/pull/79',
|
||||
'Setting %s properties via ArrayAccess is deprecated',
|
||||
self::class
|
||||
);
|
||||
|
||||
$this->$offset = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated no replacement planned
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/lexer',
|
||||
'https://github.com/doctrine/lexer/pull/79',
|
||||
'Setting %s properties via ArrayAccess is deprecated',
|
||||
self::class
|
||||
);
|
||||
|
||||
$this->$offset = null;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* Access to local part and domain part from EmailParser
|
||||
* Validations outside of the scope of the RFC will be considered "extra" validations, thus opening the door for adding new; will live in their own folder "extra" (as requested in #248, #195, #183).
|
||||
|
||||
## Breacking changes
|
||||
## Breaking changes
|
||||
|
||||
* PHP version upgraded to match Symfony's (as of 12/2020).
|
||||
* DNSCheckValidation now fails for missing MX records. While the RFC argues that the existence of only A records to be valid, starting in v3 they will be considered invalid.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Contributing
|
||||
|
||||
When contributing to this repository make sure to follow the Pull request process below.
|
||||
When contributing to this repository make sure to follow the Pull request process below.
|
||||
Reduce to the minimum 3rd party dependencies.
|
||||
|
||||
Please note we have a [code of conduct](#Code of Conduct), please follow it in all your interactions with the project.
|
||||
|
@ -14,7 +14,7 @@ When doing a PR to v2 remember that you also have to do the PR port to v3, or te
|
|||
3. Describe the changes you are proposing
|
||||
1. If adding an extra validation state the benefits of adding it and the problem is solving
|
||||
2. Document in the readme, by adding it to the list
|
||||
4. Provide appropiate tests for the code you are submitting: aim to keep the existing coverage percentage.
|
||||
4. Provide appropriate tests for the code you are submitting: aim to keep the existing coverage percentage.
|
||||
5. Add your Twitter handle (if you have) so we can thank you there.
|
||||
|
||||
## License
|
||||
|
@ -139,11 +139,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|||
version 2.0, available at
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
|
||||
at [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013-2021 Eduardo Gulias Davis
|
||||
Copyright (c) 2013-2022 Eduardo Gulias Davis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"doctrine/lexer": "^1.2",
|
||||
"doctrine/lexer": "^1.2|^2",
|
||||
"symfony/polyfill-intl-idn": "^1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^8.5.8|^9.3.3",
|
||||
"vimeo/psalm": "^4"
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,65 +3,69 @@
|
|||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Doctrine\Common\Lexer\AbstractLexer;
|
||||
use Doctrine\Common\Lexer\Token;
|
||||
|
||||
/**
|
||||
* @extends AbstractLexer<int, string>
|
||||
*/
|
||||
class EmailLexer extends AbstractLexer
|
||||
{
|
||||
//ASCII values
|
||||
const S_EMPTY = null;
|
||||
const C_NUL = 0;
|
||||
const S_HTAB = 9;
|
||||
const S_LF = 10;
|
||||
const S_CR = 13;
|
||||
const S_SP = 32;
|
||||
const EXCLAMATION = 33;
|
||||
const S_DQUOTE = 34;
|
||||
const NUMBER_SIGN = 35;
|
||||
const DOLLAR = 36;
|
||||
const PERCENTAGE = 37;
|
||||
const AMPERSAND = 38;
|
||||
const S_SQUOTE = 39;
|
||||
const S_OPENPARENTHESIS = 40;
|
||||
const S_CLOSEPARENTHESIS = 41;
|
||||
const ASTERISK = 42;
|
||||
const S_PLUS = 43;
|
||||
const S_COMMA = 44;
|
||||
const S_HYPHEN = 45;
|
||||
const S_DOT = 46;
|
||||
const S_SLASH = 47;
|
||||
const S_COLON = 58;
|
||||
const S_SEMICOLON = 59;
|
||||
const S_LOWERTHAN = 60;
|
||||
const S_EQUAL = 61;
|
||||
const S_GREATERTHAN = 62;
|
||||
const QUESTIONMARK = 63;
|
||||
const S_AT = 64;
|
||||
const S_OPENBRACKET = 91;
|
||||
const S_BACKSLASH = 92;
|
||||
const S_CLOSEBRACKET = 93;
|
||||
const CARET = 94;
|
||||
const S_UNDERSCORE = 95;
|
||||
const S_BACKTICK = 96;
|
||||
const S_OPENCURLYBRACES = 123;
|
||||
const S_PIPE = 124;
|
||||
const S_CLOSECURLYBRACES = 125;
|
||||
const S_TILDE = 126;
|
||||
const C_DEL = 127;
|
||||
const INVERT_QUESTIONMARK= 168;
|
||||
const INVERT_EXCLAMATION = 173;
|
||||
const GENERIC = 300;
|
||||
const S_IPV6TAG = 301;
|
||||
const INVALID = 302;
|
||||
const CRLF = 1310;
|
||||
const S_DOUBLECOLON = 5858;
|
||||
const ASCII_INVALID_FROM = 127;
|
||||
const ASCII_INVALID_TO = 199;
|
||||
public const S_EMPTY = null;
|
||||
public const C_NUL = 0;
|
||||
public const S_HTAB = 9;
|
||||
public const S_LF = 10;
|
||||
public const S_CR = 13;
|
||||
public const S_SP = 32;
|
||||
public const EXCLAMATION = 33;
|
||||
public const S_DQUOTE = 34;
|
||||
public const NUMBER_SIGN = 35;
|
||||
public const DOLLAR = 36;
|
||||
public const PERCENTAGE = 37;
|
||||
public const AMPERSAND = 38;
|
||||
public const S_SQUOTE = 39;
|
||||
public const S_OPENPARENTHESIS = 40;
|
||||
public const S_CLOSEPARENTHESIS = 41;
|
||||
public const ASTERISK = 42;
|
||||
public const S_PLUS = 43;
|
||||
public const S_COMMA = 44;
|
||||
public const S_HYPHEN = 45;
|
||||
public const S_DOT = 46;
|
||||
public const S_SLASH = 47;
|
||||
public const S_COLON = 58;
|
||||
public const S_SEMICOLON = 59;
|
||||
public const S_LOWERTHAN = 60;
|
||||
public const S_EQUAL = 61;
|
||||
public const S_GREATERTHAN = 62;
|
||||
public const QUESTIONMARK = 63;
|
||||
public const S_AT = 64;
|
||||
public const S_OPENBRACKET = 91;
|
||||
public const S_BACKSLASH = 92;
|
||||
public const S_CLOSEBRACKET = 93;
|
||||
public const CARET = 94;
|
||||
public const S_UNDERSCORE = 95;
|
||||
public const S_BACKTICK = 96;
|
||||
public const S_OPENCURLYBRACES = 123;
|
||||
public const S_PIPE = 124;
|
||||
public const S_CLOSECURLYBRACES = 125;
|
||||
public const S_TILDE = 126;
|
||||
public const C_DEL = 127;
|
||||
public const INVERT_QUESTIONMARK= 168;
|
||||
public const INVERT_EXCLAMATION = 173;
|
||||
public const GENERIC = 300;
|
||||
public const S_IPV6TAG = 301;
|
||||
public const INVALID = 302;
|
||||
public const CRLF = 1310;
|
||||
public const S_DOUBLECOLON = 5858;
|
||||
public const ASCII_INVALID_FROM = 127;
|
||||
public const ASCII_INVALID_TO = 199;
|
||||
|
||||
/**
|
||||
* US-ASCII visible characters not valid for atext (@link http://tools.ietf.org/html/rfc5322#section-3.2.3)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $charValue = array(
|
||||
protected $charValue = [
|
||||
'{' => self::S_OPENCURLYBRACES,
|
||||
'}' => self::S_CLOSECURLYBRACES,
|
||||
'(' => self::S_OPENPARENTHESIS,
|
||||
|
@ -105,11 +109,29 @@ class EmailLexer extends AbstractLexer
|
|||
'?' => self::QUESTIONMARK,
|
||||
'#' => self::NUMBER_SIGN,
|
||||
'¡' => self::INVERT_EXCLAMATION,
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public const INVALID_CHARS_REGEX = "/[^\p{S}\p{C}\p{Cc}]+/iu";
|
||||
|
||||
public const VALID_UTF8_REGEX = '/\p{Cc}+/u';
|
||||
|
||||
public const CATCHABLE_PATTERNS = [
|
||||
'[a-zA-Z]+[46]?', //ASCII and domain literal
|
||||
'[^\x00-\x7F]', //UTF-8
|
||||
'[0-9]+',
|
||||
'\r\n',
|
||||
'::',
|
||||
'\s+?',
|
||||
'.',
|
||||
];
|
||||
|
||||
public const NON_CATCHABLE_PATTERNS = [
|
||||
'[\xA0-\xff]+',
|
||||
];
|
||||
|
||||
public const MODIFIERS = 'iu';
|
||||
|
||||
/** @var bool */
|
||||
protected $hasInvalidTokens = false;
|
||||
|
||||
/**
|
||||
|
@ -122,38 +144,34 @@ class EmailLexer extends AbstractLexer
|
|||
/**
|
||||
* The last matched/seen token.
|
||||
*
|
||||
* @var array
|
||||
* @var array|Token
|
||||
*
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
* @psalm-var array{value:string, type:null|int, position:int}
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
* @psalm-var array{value:string, type:null|int, position:int}|Token<int, string>
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* The next token in the input.
|
||||
*
|
||||
* @var array|null
|
||||
* @var array|Token|null
|
||||
*
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
* @psalm-var array{position: int, type: int|null|string, value: int|string}|Token<int, string>|null
|
||||
*/
|
||||
public $lookahead;
|
||||
|
||||
/**
|
||||
* @psalm-var array{value:'', type:null, position:0}
|
||||
*/
|
||||
/** @psalm-var array{value:'', type:null, position:0} */
|
||||
private static $nullToken = [
|
||||
'value' => '',
|
||||
'type' => null,
|
||||
'position' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $accumulator = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
/** @var bool */
|
||||
private $hasToRecord = false;
|
||||
|
||||
public function __construct()
|
||||
|
@ -162,24 +180,13 @@ class EmailLexer extends AbstractLexer
|
|||
$this->lookahead = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
public function reset() : void
|
||||
{
|
||||
$this->hasInvalidTokens = false;
|
||||
parent::reset();
|
||||
$this->previous = $this->token = self::$nullToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInvalidTokens()
|
||||
{
|
||||
return $this->hasInvalidTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @throws \UnexpectedValueException
|
||||
|
@ -187,7 +194,7 @@ class EmailLexer extends AbstractLexer
|
|||
*
|
||||
* @psalm-suppress InvalidScalarArgument
|
||||
*/
|
||||
public function find($type)
|
||||
public function find($type) : bool
|
||||
{
|
||||
$search = clone $this;
|
||||
$search->skipUntil($type);
|
||||
|
@ -198,30 +205,26 @@ class EmailLexer extends AbstractLexer
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPrevious
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPrevious()
|
||||
{
|
||||
return $this->previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* moveNext
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveNext()
|
||||
public function moveNext() : bool
|
||||
{
|
||||
if ($this->hasToRecord && $this->previous === self::$nullToken) {
|
||||
$this->accumulator .= $this->token['value'];
|
||||
}
|
||||
|
||||
$this->previous = $this->token;
|
||||
$this->previous = $this->token instanceof Token
|
||||
? ['value' => $this->token->value, 'type' => $this->token->type, 'position' => $this->token->position]
|
||||
: $this->token;
|
||||
|
||||
if($this->lookahead === null) {
|
||||
$this->lookahead = self::$nullToken;
|
||||
}
|
||||
|
||||
$hasNext = parent::moveNext();
|
||||
$this->token = $this->token ?: self::$nullToken;
|
||||
|
||||
if ($this->hasToRecord) {
|
||||
$this->accumulator .= $this->token['value'];
|
||||
|
@ -230,36 +233,6 @@ class EmailLexer extends AbstractLexer
|
|||
return $hasNext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getCatchablePatterns()
|
||||
{
|
||||
return array(
|
||||
'[a-zA-Z]+[46]?', //ASCII and domain literal
|
||||
'[^\x00-\x7F]', //UTF-8
|
||||
'[0-9]+',
|
||||
'\r\n',
|
||||
'::',
|
||||
'\s+?',
|
||||
'.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical non-catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getNonCatchablePatterns()
|
||||
{
|
||||
return [
|
||||
'[\xA0-\xff]+',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve token type. Also processes the token value if necessary.
|
||||
*
|
||||
|
@ -272,7 +245,7 @@ class EmailLexer extends AbstractLexer
|
|||
$encoded = $value;
|
||||
|
||||
if (mb_detect_encoding($value, 'auto', true) !== 'UTF-8') {
|
||||
$encoded = utf8_encode($value);
|
||||
$encoded = mb_convert_encoding($value, 'UTF-8', 'Windows-1252');
|
||||
}
|
||||
|
||||
if ($this->isValid($encoded)) {
|
||||
|
@ -292,51 +265,64 @@ class EmailLexer extends AbstractLexer
|
|||
return self::GENERIC;
|
||||
}
|
||||
|
||||
protected function isInvalidChar(string $value) : bool
|
||||
{
|
||||
if(preg_match("/[^\p{S}\p{C}\p{Cc}]+/iu", $value) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isValid(string $value) : bool
|
||||
{
|
||||
if (isset($this->charValue[$value])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return isset($this->charValue[$value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNullType($value)
|
||||
protected function isNullType(string $value) : bool
|
||||
{
|
||||
if ($value === "\0") {
|
||||
return true;
|
||||
}
|
||||
return $value === "\0";
|
||||
}
|
||||
|
||||
return false;
|
||||
protected function isInvalidChar(string $value) : bool
|
||||
{
|
||||
return !preg_match(self::INVALID_CHARS_REGEX, $value);
|
||||
}
|
||||
|
||||
protected function isUTF8Invalid(string $value) : bool
|
||||
{
|
||||
if (preg_match('/\p{Cc}+/u', $value)) {
|
||||
return true;
|
||||
}
|
||||
return preg_match(self::VALID_UTF8_REGEX, $value) !== false;
|
||||
}
|
||||
|
||||
return false;
|
||||
public function hasInvalidTokens() : bool
|
||||
{
|
||||
return $this->hasInvalidTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* getPrevious
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getModifiers()
|
||||
public function getPrevious() : array
|
||||
{
|
||||
return 'iu';
|
||||
return $this->previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getCatchablePatterns() : array
|
||||
{
|
||||
return self::CATCHABLE_PATTERNS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical non-catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getNonCatchablePatterns() : array
|
||||
{
|
||||
return self::NON_CATCHABLE_PATTERNS;
|
||||
}
|
||||
|
||||
protected function getModifiers() : string
|
||||
{
|
||||
return self::MODIFIERS;
|
||||
}
|
||||
|
||||
public function getAccumulatedValues() : string
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
use Egulias\EmailValidator\Parser\DomainPart;
|
||||
|
@ -13,7 +12,7 @@ use Egulias\EmailValidator\Result\Reason\NoLocalPart;
|
|||
|
||||
class EmailParser extends Parser
|
||||
{
|
||||
const EMAIL_MAX_LENGTH = 254;
|
||||
public const EMAIL_MAX_LENGTH = 254;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Parser;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\IDLeftPart;
|
||||
use Egulias\EmailValidator\Parser\IDRightPart;
|
||||
|
@ -15,7 +13,7 @@ use Egulias\EmailValidator\Result\Reason\NoLocalPart;
|
|||
class MessageIDParser extends Parser
|
||||
{
|
||||
|
||||
const EMAILID_MAX_LENGTH = 254;
|
||||
public const EMAILID_MAX_LENGTH = 254;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -90,4 +88,4 @@ class MessageIDParser extends Parser
|
|||
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,8 @@ class Comment extends PartParser
|
|||
|
||||
if($this->openedParenthesis >= 1) {
|
||||
return new InvalidEmail(new UnclosedComment(), $this->lexer->token['value']);
|
||||
} else if ($this->openedParenthesis < 0) {
|
||||
}
|
||||
if ($this->openedParenthesis < 0) {
|
||||
return new InvalidEmail(new UnOpenedComment(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
|
@ -100,4 +101,4 @@ class Comment extends PartParser
|
|||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,4 +15,4 @@ interface CommentStrategy
|
|||
public function endOfLoopValidations(EmailLexer $lexer) : Result;
|
||||
|
||||
public function getWarnings() : array;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,4 +34,4 @@ class DomainComment implements CommentStrategy
|
|||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,4 +34,4 @@ class LocalComment implements CommentStrategy
|
|||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,15 @@ use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
|
|||
|
||||
class DomainLiteral extends PartParser
|
||||
{
|
||||
public const IPV4_REGEX = '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
|
||||
|
||||
public const OBSOLETE_WARNINGS = [
|
||||
EmailLexer::INVALID,
|
||||
EmailLexer::C_DEL,
|
||||
EmailLexer::S_LF,
|
||||
EmailLexer::S_BACKSLASH
|
||||
];
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
$this->addTagWarnings();
|
||||
|
@ -41,7 +50,7 @@ class DomainLiteral extends PartParser
|
|||
}
|
||||
|
||||
if ($this->lexer->isNextTokenAny(
|
||||
array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
|
||||
array(EmailLexer::S_HTAB, EmailLexer::S_SP, EmailLexer::CRLF)
|
||||
)) {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
$this->parseFWS();
|
||||
|
@ -138,11 +147,8 @@ class DomainLiteral extends PartParser
|
|||
|
||||
public function convertIPv4ToIPv6(string $addressLiteralIPv4) : string
|
||||
{
|
||||
$matchesIP = array();
|
||||
$IPv4Match = preg_match(
|
||||
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
|
||||
$addressLiteralIPv4,
|
||||
$matchesIP);
|
||||
$matchesIP = [];
|
||||
$IPv4Match = preg_match(self::IPV4_REGEX, $addressLiteralIPv4, $matchesIP);
|
||||
|
||||
// Extract IPv4 part from the end of the address-literal (if there is one)
|
||||
if ($IPv4Match > 0) {
|
||||
|
@ -164,11 +170,8 @@ class DomainLiteral extends PartParser
|
|||
*/
|
||||
protected function checkIPV4Tag($addressLiteral) : bool
|
||||
{
|
||||
$matchesIP = array();
|
||||
$IPv4Match = preg_match(
|
||||
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
|
||||
$addressLiteral,
|
||||
$matchesIP);
|
||||
$matchesIP = [];
|
||||
$IPv4Match = preg_match(self::IPV4_REGEX, $addressLiteral, $matchesIP);
|
||||
|
||||
// Extract IPv4 part from the end of the address-literal (if there is one)
|
||||
|
||||
|
@ -186,11 +189,7 @@ class DomainLiteral extends PartParser
|
|||
|
||||
private function addObsoleteWarnings() : void
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::INVALID ||
|
||||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_LF ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_BACKSLASH
|
||||
) {
|
||||
if(in_array($this->lexer->token['type'], self::OBSOLETE_WARNINGS)) {
|
||||
$this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
|
||||
}
|
||||
}
|
||||
|
@ -209,4 +208,4 @@ class DomainLiteral extends PartParser
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Doctrine\Common\Lexer\Token;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Warning\TLD;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
|
@ -24,8 +25,8 @@ use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
|
|||
|
||||
class DomainPart extends PartParser
|
||||
{
|
||||
const DOMAIN_MAX_LENGTH = 253;
|
||||
const LABEL_MAX_LENGTH = 63;
|
||||
public const DOMAIN_MAX_LENGTH = 253;
|
||||
public const LABEL_MAX_LENGTH = 63;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -212,7 +213,10 @@ class DomainPart extends PartParser
|
|||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkNotAllowedChars(array $token) : Result
|
||||
/**
|
||||
* @psalm-param array|Token<int, string> $token
|
||||
*/
|
||||
private function checkNotAllowedChars($token) : Result
|
||||
{
|
||||
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
|
||||
if (isset($notAllowed[$token['type']])) {
|
||||
|
@ -292,7 +296,7 @@ class DomainPart extends PartParser
|
|||
private function isLabelTooLong(string $label) : bool
|
||||
{
|
||||
if (preg_match('/[^\x00-\x7F]/', $label)) {
|
||||
idn_to_ascii(utf8_decode($label), IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
|
||||
idn_to_ascii($label, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
|
||||
return (bool) ($idnaInfo['errors'] & IDNA_ERROR_LABEL_TOO_LONG);
|
||||
}
|
||||
return strlen($label) > self::LABEL_MAX_LENGTH;
|
||||
|
@ -309,4 +313,4 @@ class DomainPart extends PartParser
|
|||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Parser\Parser;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
||||
|
@ -19,18 +18,19 @@ class DoubleQuote extends PartParser
|
|||
$validQuotedString = $this->checkDQUOTE();
|
||||
if($validQuotedString->isInvalid()) return $validQuotedString;
|
||||
|
||||
$special = array(
|
||||
$special = [
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_LF => true
|
||||
);
|
||||
];
|
||||
|
||||
$invalid = array(
|
||||
$invalid = [
|
||||
EmailLexer::C_NUL => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_LF => true
|
||||
);
|
||||
];
|
||||
|
||||
$setSpecialsWarning = true;
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
@ -84,4 +84,4 @@ class DoubleQuote extends PartParser
|
|||
return new ValidEmail();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,14 @@ use Egulias\EmailValidator\Result\ValidEmail;
|
|||
|
||||
class FoldingWhiteSpace extends PartParser
|
||||
{
|
||||
public const FWS_TYPES = [
|
||||
EmailLexer::S_SP,
|
||||
EmailLexer::S_HTAB,
|
||||
EmailLexer::S_CR,
|
||||
EmailLexer::S_LF,
|
||||
EmailLexer::CRLF
|
||||
];
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
if (!$this->isFWS()) {
|
||||
|
@ -73,10 +81,6 @@ class FoldingWhiteSpace extends PartParser
|
|||
return false;
|
||||
}
|
||||
|
||||
return $this->lexer->token['type'] === EmailLexer::S_SP ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_HTAB ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_CR ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_LF ||
|
||||
$this->lexer->token['type'] === EmailLexer::CRLF;
|
||||
return in_array($this->lexer->token['type'], self::FWS_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\CommentsInIDRight;
|
||||
|
||||
|
@ -13,4 +12,4 @@ class IDLeftPart extends LocalPart
|
|||
{
|
||||
return new InvalidEmail(new CommentsInIDRight(), $this->lexer->token['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,18 +12,18 @@ class IDRightPart extends DomainPart
|
|||
{
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$invalidDomainTokens = array(
|
||||
$invalidDomainTokens = [
|
||||
EmailLexer::S_DQUOTE => true,
|
||||
EmailLexer::S_SQUOTE => true,
|
||||
EmailLexer::S_BACKTICK => true,
|
||||
EmailLexer::S_SEMICOLON => true,
|
||||
EmailLexer::S_GREATERTHAN => true,
|
||||
EmailLexer::S_LOWERTHAN => true,
|
||||
);
|
||||
];
|
||||
|
||||
if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,17 @@ use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
|
|||
|
||||
class LocalPart extends PartParser
|
||||
{
|
||||
public const INVALID_TOKENS = [
|
||||
EmailLexer::S_COMMA => EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON => EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID => EmailLexer::INVALID
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -88,17 +99,7 @@ class LocalPart extends PartParser
|
|||
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$invalidTokens = array(
|
||||
EmailLexer::S_COMMA => EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON => EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID => EmailLexer::INVALID
|
||||
);
|
||||
if (isset($invalidTokens[$this->lexer->token['type']])) {
|
||||
if (isset(self::INVALID_TOKENS[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
|
@ -161,4 +162,4 @@ class LocalPart extends PartParser
|
|||
|
||||
return new ValidEmail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,4 +60,4 @@ abstract class PartParser
|
|||
&&
|
||||
$this->lexer->token['type'] !== EmailLexer::GENERIC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use Egulias\EmailValidator\Result\Reason\Reason;
|
|||
|
||||
class InvalidEmail implements Result
|
||||
{
|
||||
private $token;
|
||||
private $token;
|
||||
/**
|
||||
* @var Reason
|
||||
*/
|
||||
|
@ -43,4 +43,4 @@ class InvalidEmail implements Result
|
|||
return $this->reason;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class AtextAfterCFWS implements Reason
|
|||
{
|
||||
return 'ATEXT found after CFWS';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ namespace Egulias\EmailValidator\Result\Reason;
|
|||
|
||||
class CRLFAtTheEnd implements Reason
|
||||
{
|
||||
const CODE = 149;
|
||||
const REASON = "CRLF at the end";
|
||||
public const CODE = 149;
|
||||
public const REASON = "CRLF at the end";
|
||||
|
||||
public function code() : int
|
||||
{
|
||||
|
|
|
@ -13,4 +13,4 @@ class CharNotAllowed implements Reason
|
|||
{
|
||||
return "Character not allowed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class CommaInDomain implements Reason
|
|||
{
|
||||
return "Comma ',' is not allowed in domain part";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class CommentsInIDRight implements Reason
|
|||
{
|
||||
return 'Comments are not allowed in IDRight for message-id';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,4 +10,4 @@ abstract class DetailedReason implements Reason
|
|||
{
|
||||
$this->detailedDescription = $details;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class DomainAcceptsNoMail implements Reason
|
|||
{
|
||||
return 'Domain accepts no mail (Null MX, RFC7505)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,4 @@ class ExceptionFound implements Reason
|
|||
{
|
||||
return $this->exception->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class ExpectingDomainLiteralClose implements Reason
|
|||
{
|
||||
return "Closing bracket ']' for domain literal not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class LocalOrReservedDomain implements Reason
|
|||
{
|
||||
return 'Local, mDNS or reserved domain (RFC2606, RFC6762)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class NoDNSRecord implements Reason
|
|||
{
|
||||
return 'No MX or A DSN record was found for this email';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ interface Reason
|
|||
* Short description of the result, human readable.
|
||||
*/
|
||||
public function description() : string;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ class UnOpenedComment implements Reason
|
|||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Missing openning comment parentheses - https://tools.ietf.org/html/rfc5322#section-3.2.2';
|
||||
return 'Missing opening comment parentheses - https://tools.ietf.org/html/rfc5322#section-3.2.2';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,4 @@ class UnusualElements implements Reason
|
|||
{
|
||||
return 'Unusual element found, wourld render invalid in majority of cases. Element found: ' . $this->element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,4 @@ interface Result
|
|||
* Code for user land to act upon.
|
||||
*/
|
||||
public function code() : int;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Result;
|
||||
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\SpoofEmail as ReasonSpoofEmail;
|
||||
|
||||
class SpoofEmail extends InvalidEmail
|
||||
|
@ -11,4 +10,4 @@ class SpoofEmail extends InvalidEmail
|
|||
$this->reason = new ReasonSpoofEmail();
|
||||
parent::__construct($this->reason, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,4 @@ class ValidEmail implements Result
|
|||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,29 @@ class DNSCheckValidation implements EmailValidation
|
|||
*/
|
||||
protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;
|
||||
|
||||
/**
|
||||
* Reserved Top Level DNS Names (https://tools.ietf.org/html/rfc2606#section-2),
|
||||
* mDNS and private DNS Namespaces (https://tools.ietf.org/html/rfc6762#appendix-G)
|
||||
*/
|
||||
public const RESERVED_DNS_TOP_LEVEL_NAMES = [
|
||||
// Reserved Top Level DNS Names
|
||||
'test',
|
||||
'example',
|
||||
'invalid',
|
||||
'localhost',
|
||||
|
||||
// mDNS
|
||||
'local',
|
||||
|
||||
// Private DNS Namespaces
|
||||
'intranet',
|
||||
'internal',
|
||||
'private',
|
||||
'corp',
|
||||
'home',
|
||||
'lan',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
@ -32,12 +55,22 @@ class DNSCheckValidation implements EmailValidation
|
|||
*/
|
||||
private $mxRecords = [];
|
||||
|
||||
/**
|
||||
* @var DNSGetRecordWrapper
|
||||
*/
|
||||
private $dnsGetRecord;
|
||||
|
||||
public function __construct()
|
||||
public function __construct(?DNSGetRecordWrapper $dnsGetRecord = null)
|
||||
{
|
||||
if (!function_exists('idn_to_ascii')) {
|
||||
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
|
||||
}
|
||||
|
||||
if ($dnsGetRecord == null) {
|
||||
$dnsGetRecord = new DNSGetRecordWrapper();
|
||||
}
|
||||
|
||||
$this->dnsGetRecord = $dnsGetRecord;
|
||||
}
|
||||
|
||||
public function isValid(string $email, EmailLexer $emailLexer) : bool
|
||||
|
@ -53,29 +86,8 @@ class DNSCheckValidation implements EmailValidation
|
|||
// Get the domain parts
|
||||
$hostParts = explode('.', $host);
|
||||
|
||||
// Reserved Top Level DNS Names (https://tools.ietf.org/html/rfc2606#section-2),
|
||||
// mDNS and private DNS Namespaces (https://tools.ietf.org/html/rfc6762#appendix-G)
|
||||
$reservedTopLevelDnsNames = [
|
||||
// Reserved Top Level DNS Names
|
||||
'test',
|
||||
'example',
|
||||
'invalid',
|
||||
'localhost',
|
||||
|
||||
// mDNS
|
||||
'local',
|
||||
|
||||
// Private DNS Namespaces
|
||||
'intranet',
|
||||
'internal',
|
||||
'private',
|
||||
'corp',
|
||||
'home',
|
||||
'lan',
|
||||
];
|
||||
|
||||
$isLocalDomain = count($hostParts) <= 1;
|
||||
$isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], $reservedTopLevelDnsNames, true);
|
||||
$isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], self::RESERVED_DNS_TOP_LEVEL_NAMES, true);
|
||||
|
||||
// Exclude reserved top level DNS names
|
||||
if ($isLocalDomain || $isReservedTopLevel) {
|
||||
|
@ -120,27 +132,17 @@ class DNSCheckValidation implements EmailValidation
|
|||
*/
|
||||
private function validateDnsRecords($host) : bool
|
||||
{
|
||||
// A workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
set_error_handler(
|
||||
static function (int $errorLevel, string $errorMessage): ?bool {
|
||||
throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
|
||||
}
|
||||
);
|
||||
$dnsRecordsResult = $this->dnsGetRecord->getRecords($host, static::DNS_RECORD_TYPES_TO_CHECK);
|
||||
|
||||
try {
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
$dnsRecords = dns_get_record($host, static::DNS_RECORD_TYPES_TO_CHECK);
|
||||
} catch (\RuntimeException $exception) {
|
||||
if ($dnsRecordsResult->withError()) {
|
||||
$this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$dnsRecords = $dnsRecordsResult->getRecords();
|
||||
|
||||
// No MX, A or AAAA DNS records
|
||||
if ($dnsRecords === [] || $dnsRecords === false) {
|
||||
if ($dnsRecords === []) {
|
||||
$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
@ -167,6 +169,11 @@ class DNSCheckValidation implements EmailValidation
|
|||
*/
|
||||
private function validateMxRecord($dnsRecord) : bool
|
||||
{
|
||||
if (!isset($dnsRecord['type'])) {
|
||||
$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($dnsRecord['type'] !== 'MX') {
|
||||
return true;
|
||||
}
|
||||
|
@ -181,4 +188,4 @@ class DNSCheckValidation implements EmailValidation
|
|||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
pandora_console/vendor/egulias/email-validator/src/Validation/DNSGetRecordWrapper.php
vendored
Normal file
28
pandora_console/vendor/egulias/email-validator/src/Validation/DNSGetRecordWrapper.php
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
class DNSGetRecordWrapper
|
||||
{
|
||||
/**
|
||||
* @param string $host
|
||||
* @param int $type
|
||||
*/
|
||||
public function getRecords(string $host, int $type) : DNSRecords
|
||||
{
|
||||
// A workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
set_error_handler(
|
||||
static function (int $errorLevel, string $errorMessage): ?bool {
|
||||
throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
|
||||
}
|
||||
);
|
||||
try {
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
return new DNSRecords(dns_get_record($host, $type));
|
||||
} catch (\RuntimeException $exception) {
|
||||
return new DNSRecords([], true);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue