mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 01:57:13 +00:00
Merge pull request #247 from phpDocumentor/safe_preg_split
Introduce safe preg_split
This commit is contained in:
@@ -14,13 +14,12 @@ declare(strict_types=1);
|
||||
namespace phpDocumentor\Reflection\DocBlock;
|
||||
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use Webmozart\Assert\Assert;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function ltrim;
|
||||
use function min;
|
||||
use function preg_split;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
@@ -98,7 +97,7 @@ class DescriptionFactory
|
||||
return [$contents];
|
||||
}
|
||||
|
||||
$parts = preg_split(
|
||||
return Utils::pregSplit(
|
||||
'/\{
|
||||
# "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally.
|
||||
(?!@\})
|
||||
@@ -127,9 +126,6 @@ class DescriptionFactory
|
||||
0,
|
||||
PREG_SPLIT_DELIM_CAPTURE
|
||||
);
|
||||
Assert::isArray($parts);
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,8 +18,8 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Fqsen;
|
||||
use phpDocumentor\Reflection\FqsenResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function preg_split;
|
||||
|
||||
/**
|
||||
* Reflection class for a @covers tag in a Docblock.
|
||||
@@ -51,8 +51,7 @@ final class Covers extends BaseTag implements Factory\StaticMethod
|
||||
Assert::notNull($descriptionFactory);
|
||||
Assert::notNull($resolver);
|
||||
|
||||
$parts = preg_split('/\s+/Su', $body, 2);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
|
||||
|
||||
return new static(
|
||||
$resolver->resolve($parts[0], $context),
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace phpDocumentor\Reflection\DocBlock\Tags;
|
||||
use phpDocumentor\Reflection\DocBlock\Description;
|
||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function preg_split;
|
||||
|
||||
/**
|
||||
* Reflection class for a @link tag in a Docblock.
|
||||
@@ -46,8 +46,7 @@ final class Link extends BaseTag implements Factory\StaticMethod
|
||||
) : self {
|
||||
Assert::notNull($descriptionFactory);
|
||||
|
||||
$parts = preg_split('/\s+/Su', $body, 2);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
|
||||
$description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
|
||||
|
||||
return new static($parts[0], $description);
|
||||
|
||||
@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Type;
|
||||
use phpDocumentor\Reflection\TypeResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
use function implode;
|
||||
use function preg_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
@@ -64,8 +64,7 @@ final class Param extends TagWithType implements Factory\StaticMethod
|
||||
[$firstPart, $body] = self::extractTypeFromBody($body);
|
||||
|
||||
$type = null;
|
||||
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$variableName = '';
|
||||
$isVariadic = false;
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Type;
|
||||
use phpDocumentor\Reflection\TypeResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
use function implode;
|
||||
use function preg_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
@@ -57,8 +57,7 @@ final class Property extends TagWithType implements Factory\StaticMethod
|
||||
|
||||
[$firstPart, $body] = self::extractTypeFromBody($body);
|
||||
$type = null;
|
||||
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$variableName = '';
|
||||
|
||||
// if the first item that is encountered is not a variable; it is a type
|
||||
|
||||
@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Type;
|
||||
use phpDocumentor\Reflection\TypeResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
use function implode;
|
||||
use function preg_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
@@ -57,8 +57,7 @@ final class PropertyRead extends TagWithType implements Factory\StaticMethod
|
||||
|
||||
[$firstPart, $body] = self::extractTypeFromBody($body);
|
||||
$type = null;
|
||||
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$variableName = '';
|
||||
|
||||
// if the first item that is encountered is not a variable; it is a type
|
||||
|
||||
@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Type;
|
||||
use phpDocumentor\Reflection\TypeResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
use function implode;
|
||||
use function preg_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
@@ -57,8 +57,7 @@ final class PropertyWrite extends TagWithType implements Factory\StaticMethod
|
||||
|
||||
[$firstPart, $body] = self::extractTypeFromBody($body);
|
||||
$type = null;
|
||||
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$variableName = '';
|
||||
|
||||
// if the first item that is encountered is not a variable; it is a type
|
||||
|
||||
@@ -20,9 +20,9 @@ use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
|
||||
use phpDocumentor\Reflection\FqsenResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function preg_match;
|
||||
use function preg_split;
|
||||
|
||||
/**
|
||||
* Reflection class for an {@}see tag in a Docblock.
|
||||
@@ -53,8 +53,7 @@ final class See extends BaseTag implements Factory\StaticMethod
|
||||
Assert::notNull($typeResolver);
|
||||
Assert::notNull($descriptionFactory);
|
||||
|
||||
$parts = preg_split('/\s+/Su', $body, 2);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
|
||||
$description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
|
||||
|
||||
// https://tools.ietf.org/html/rfc2396#section-3
|
||||
|
||||
@@ -18,8 +18,8 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Fqsen;
|
||||
use phpDocumentor\Reflection\FqsenResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function preg_split;
|
||||
|
||||
/**
|
||||
* Reflection class for a {@}uses tag in a Docblock.
|
||||
@@ -50,9 +50,7 @@ final class Uses extends BaseTag implements Factory\StaticMethod
|
||||
Assert::notNull($resolver);
|
||||
Assert::notNull($descriptionFactory);
|
||||
|
||||
$parts = preg_split('/\s+/Su', $body, 2);
|
||||
Assert::isArray($parts);
|
||||
Assert::allString($parts);
|
||||
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
|
||||
|
||||
return new static(
|
||||
$resolver->resolve($parts[0], $context),
|
||||
|
||||
@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
||||
use phpDocumentor\Reflection\Type;
|
||||
use phpDocumentor\Reflection\TypeResolver;
|
||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
||||
use phpDocumentor\Reflection\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
use function implode;
|
||||
use function preg_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
@@ -57,8 +57,7 @@ final class Var_ extends TagWithType implements Factory\StaticMethod
|
||||
|
||||
[$firstPart, $body] = self::extractTypeFromBody($body);
|
||||
|
||||
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
Assert::isArray($parts);
|
||||
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$type = null;
|
||||
$variableName = '';
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpDocumentor\Reflection\Exception;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use const PREG_BACKTRACK_LIMIT_ERROR;
|
||||
use const PREG_BAD_UTF8_ERROR;
|
||||
use const PREG_BAD_UTF8_OFFSET_ERROR;
|
||||
use const PREG_INTERNAL_ERROR;
|
||||
use const PREG_JIT_STACKLIMIT_ERROR;
|
||||
use const PREG_NO_ERROR;
|
||||
use const PREG_RECURSION_LIMIT_ERROR;
|
||||
|
||||
final class PcreException extends InvalidArgumentException
|
||||
{
|
||||
public static function createFromPhpError(int $errorCode) : self
|
||||
{
|
||||
switch ($errorCode) {
|
||||
case PREG_BACKTRACK_LIMIT_ERROR:
|
||||
return new self('Backtrack limit error');
|
||||
case PREG_RECURSION_LIMIT_ERROR:
|
||||
return new self('Recursion limit error');
|
||||
case PREG_BAD_UTF8_ERROR:
|
||||
return new self('Bad UTF8 error');
|
||||
case PREG_BAD_UTF8_OFFSET_ERROR:
|
||||
return new self('Bad UTF8 offset error');
|
||||
case PREG_JIT_STACKLIMIT_ERROR:
|
||||
return new self('Jit stacklimit error');
|
||||
case PREG_NO_ERROR:
|
||||
case PREG_INTERNAL_ERROR:
|
||||
default:
|
||||
}
|
||||
|
||||
return new self('Unknown Pcre error');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\Reflection;
|
||||
|
||||
use phpDocumentor\Reflection\Exception\PcreException;
|
||||
use function preg_last_error;
|
||||
use function preg_split as php_preg_split;
|
||||
|
||||
abstract class Utils
|
||||
{
|
||||
/**
|
||||
* Wrapper function for phps preg_split
|
||||
*
|
||||
* This function is inspired by {@link https://github.com/thecodingmachine/safe/blob/master/generated/pcre.php}. But
|
||||
* since this library is all about performance we decided to strip everything we don't need. Reducing the amount
|
||||
* of files that have to be loaded, ect.
|
||||
*
|
||||
* @param string $pattern The pattern to search for, as a string.
|
||||
* @param string $subject The input string.
|
||||
* @param int|null $limit If specified, then only substrings up to limit are returned with the
|
||||
* rest of the string being placed in the last substring. A limit of -1 or 0 means "no limit".
|
||||
* @param int $flags flags can be any combination of the following flags (combined with the | bitwise operator):
|
||||
* *PREG_SPLIT_NO_EMPTY*
|
||||
* If this flag is set, only non-empty pieces will be returned by preg_split().
|
||||
* *PREG_SPLIT_DELIM_CAPTURE*
|
||||
* If this flag is set, parenthesized expression in the delimiter pattern will be captured
|
||||
* and returned as well.
|
||||
* *PREG_SPLIT_OFFSET_CAPTURE*
|
||||
* If this flag is set, for every occurring match the appendant string offset will also be returned.
|
||||
* Note that this changes the return value in an array where every element is an array consisting of the
|
||||
* matched string at offset 0 and its string offset into subject at offset 1.
|
||||
*
|
||||
* @return string[] Returns an array containing substrings of subject split along boundaries matched by pattern
|
||||
*
|
||||
* @throws PcreException
|
||||
*/
|
||||
public static function pregSplit(string $pattern, string $subject, ?int $limit = -1, int $flags = 0) : array
|
||||
{
|
||||
$parts = php_preg_split($pattern, $subject, $limit, $flags);
|
||||
if ($parts === false) {
|
||||
throw PcreException::createFromPhpError(preg_last_error());
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpDocumentor\Reflection\Exception;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use const PREG_BACKTRACK_LIMIT_ERROR;
|
||||
use const PREG_BAD_UTF8_ERROR;
|
||||
use const PREG_BAD_UTF8_OFFSET_ERROR;
|
||||
use const PREG_INTERNAL_ERROR;
|
||||
use const PREG_JIT_STACKLIMIT_ERROR;
|
||||
use const PREG_NO_ERROR;
|
||||
use const PREG_RECURSION_LIMIT_ERROR;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \phpDocumentor\Reflection\Exception\PcreException
|
||||
*/
|
||||
final class PcreExceptionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers ::createFromPhpError
|
||||
* @dataProvider errorCodeProvider
|
||||
*/
|
||||
public function testErrorConversion(int $errorCode, string $message) : void
|
||||
{
|
||||
$this->assertSame($message, PcreException::createFromPhpError($errorCode)->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, (string|int)[]>
|
||||
*/
|
||||
public function errorCodeProvider() : array
|
||||
{
|
||||
return [
|
||||
[
|
||||
PREG_BACKTRACK_LIMIT_ERROR,
|
||||
'Backtrack limit error',
|
||||
],
|
||||
[
|
||||
PREG_RECURSION_LIMIT_ERROR,
|
||||
'Recursion limit error',
|
||||
],
|
||||
[
|
||||
PREG_BAD_UTF8_ERROR,
|
||||
'Bad UTF8 error',
|
||||
],
|
||||
[
|
||||
PREG_BAD_UTF8_OFFSET_ERROR,
|
||||
'Bad UTF8 offset error',
|
||||
],
|
||||
[
|
||||
PREG_JIT_STACKLIMIT_ERROR,
|
||||
'Jit stacklimit error',
|
||||
],
|
||||
[
|
||||
PREG_NO_ERROR,
|
||||
'Unknown Pcre error',
|
||||
],
|
||||
[
|
||||
PREG_INTERNAL_ERROR,
|
||||
'Unknown Pcre error',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpDocumentor\Reflection;
|
||||
|
||||
use phpDocumentor\Reflection\Exception\PcreException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function set_error_handler;
|
||||
use const E_WARNING;
|
||||
|
||||
final class PregSplitTest extends TestCase
|
||||
{
|
||||
/** @var callable|null */
|
||||
private $errorHandler = null;
|
||||
|
||||
protected function tearDown() : void
|
||||
{
|
||||
if ($this->errorHandler === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_error_handler($this->errorHandler, E_WARNING);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \phpDocumentor\Reflection\Utils::pregSplit
|
||||
*/
|
||||
public function testSimplePregSplit() : void
|
||||
{
|
||||
$result = Utils::pregSplit('/\s/', 'word split');
|
||||
|
||||
$this->assertSame(['word', 'split'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \phpDocumentor\Reflection\Utils::pregSplit
|
||||
*/
|
||||
public function testPregSplitThrowsOnError() : void
|
||||
{
|
||||
//We need to disable the error handler for phpunit... because we expect some errors here
|
||||
$this->errorHandler = set_error_handler(static function () : void {
|
||||
}, E_WARNING);
|
||||
|
||||
$this->expectException(PcreException::class);
|
||||
Utils::pregSplit('~InvalidRegular)Expression~', 'some word');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user