mirror of
https://github.com/doctrine/sql-formatter.git
synced 2026-03-23 22:42:14 +01:00
Modernize codebase for PHP 8 (#100)
This commit is contained in:
committed by
GitHub
parent
1f855459d4
commit
d1ac84aef7
14
.github/workflows/coding-standards.yml
vendored
Normal file
14
.github/workflows/coding-standards.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: "Coding Standards"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1"
|
||||
129
.github/workflows/continuous-integration.yml
vendored
129
.github/workflows/continuous-integration.yml
vendored
@@ -1,4 +1,3 @@
|
||||
|
||||
name: "Continuous Integration"
|
||||
|
||||
on:
|
||||
@@ -9,125 +8,11 @@ on:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
env:
|
||||
fail-fast: true
|
||||
|
||||
jobs:
|
||||
|
||||
unit-tests:
|
||||
name: "Unit tests"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Test fixes"
|
||||
run: "vendor/bin/phpunit --coverage-text"
|
||||
|
||||
static-analysis-phpstan:
|
||||
name: "Static Analysis with PHPStan"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
extensions: "mbstring"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
|
||||
|
||||
|
||||
static-analysis-psalm:
|
||||
name: "Static Analysis with Psalm"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with vimeo/psalm"
|
||||
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=4"
|
||||
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
# Remove -q when updating to phpcs v4
|
||||
- name: "Run squizlabs/php_codesniffer"
|
||||
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
|
||||
phpunit:
|
||||
name: "PHPUnit"
|
||||
uses: "doctrine/.github/.github/workflows/continuous-integration.yml@5.0.1"
|
||||
with:
|
||||
php-versions: '["8.1", "8.2", "8.3"]'
|
||||
secrets:
|
||||
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
|
||||
|
||||
14
.github/workflows/static-analysis.yml
vendored
Normal file
14
.github/workflows/static-analysis.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: "Static Analysis"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
static-analysis:
|
||||
name: "Static Analysis"
|
||||
uses: "doctrine/.github/.github/workflows/static-analysis.yml@5.0.1"
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
/.phpunit.result.cache
|
||||
/.phpunit.cache
|
||||
/.phpcs-cache
|
||||
vendor/
|
||||
/vendor
|
||||
/composer.lock
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpunit/phpunit": "^8.5 || ^9.6",
|
||||
"vimeo/psalm": "^4.11"
|
||||
"doctrine/coding-standard": "^12",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"vimeo/psalm": "^5.24"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -154,7 +154,7 @@ SQL,
|
||||
Usage:
|
||||
<pre>
|
||||
<?php highlight_string(
|
||||
'<?php' . "\n" . '$formatted = (new SqlFormatter())->format($sql);' . "\n" . '?>'
|
||||
'<?php' . "\n" . '$formatted = (new SqlFormatter())->format($sql);' . "\n" . '?>',
|
||||
); ?>
|
||||
</pre>
|
||||
</div>
|
||||
@@ -195,7 +195,7 @@ SQL,
|
||||
<pre><?= $sql; ?></pre>
|
||||
</td>
|
||||
<td><pre><?= htmlentities((new SqlFormatter(
|
||||
new NullHighlighter()
|
||||
new NullHighlighter(),
|
||||
))->format($sql)); ?></pre></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
@@ -208,7 +208,7 @@ SQL,
|
||||
Usage:
|
||||
<pre>
|
||||
<?php highlight_string(
|
||||
'<?php' . "\n" . '$highlighted = (new SqlFormatter())->highlight($sql);' . "\n" . '?>'
|
||||
'<?php' . "\n" . '$highlighted = (new SqlFormatter())->highlight($sql);' . "\n" . '?>',
|
||||
); ?>
|
||||
</pre>
|
||||
</div>
|
||||
@@ -234,7 +234,7 @@ SQL,
|
||||
Usage:
|
||||
<pre>
|
||||
<?php highlight_string(
|
||||
'<?php' . "\n" . '$compressed = (new SqlFormatter())->compress($sql);' . "\n" . '?>'
|
||||
'<?php' . "\n" . '$compressed = (new SqlFormatter())->compress($sql);' . "\n" . '?>',
|
||||
); ?>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
|
||||
<arg name="basepath" value="."/>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg name="parallel" value="80"/>
|
||||
<arg name="cache" value=".phpcs-cache"/>
|
||||
<arg name="colors" />
|
||||
|
||||
<!-- Show progress of the run -->
|
||||
<arg value="p"/>
|
||||
<!-- Ignore warnings, show progress of the run and show sniff names -->
|
||||
<arg value="nps"/>
|
||||
|
||||
<config name="php_version" value="80100"/>
|
||||
|
||||
<rule ref="Doctrine"/>
|
||||
|
||||
@@ -19,13 +22,6 @@
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<!-- Disable the rules that will require PHP 7.4 -->
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
|
||||
<properties>
|
||||
<property name="enableNativeTypeHint" value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="Generic.Files.InlineHTML">
|
||||
<exclude-pattern>examples/*.php</exclude-pattern>
|
||||
<exclude-pattern>tests/performance.php</exclude-pattern>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.0/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
executionOrder="depends,defects"
|
||||
forceCoversAnnotation="true"
|
||||
beStrictAboutCoversAnnotation="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
colors="true"
|
||||
verbose="true">
|
||||
cacheDirectory=".phpunit.cache"
|
||||
requireCoverageMetadata="true"
|
||||
beStrictAboutCoverageMetadata="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
<directory suffix="Test.php">tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<psalm
|
||||
errorLevel="5"
|
||||
resolveFromConfigFile="true"
|
||||
findUnusedBaselineEntry="true"
|
||||
findUnusedCode="false"
|
||||
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"
|
||||
|
||||
@@ -13,11 +13,9 @@ final class CliHighlighter implements Highlighter
|
||||
public const HIGHLIGHT_FUNCTIONS = 'functions';
|
||||
|
||||
/** @var array<string, string> */
|
||||
private $escapeSequences;
|
||||
private array $escapeSequences;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $escapeSequences
|
||||
*/
|
||||
/** @param array<string, string> $escapeSequences */
|
||||
public function __construct(array $escapeSequences = [])
|
||||
{
|
||||
$this->escapeSequences = $escapeSequences + [
|
||||
@@ -48,7 +46,7 @@ final class CliHighlighter implements Highlighter
|
||||
return $prefix . $value . "\x1b[0m";
|
||||
}
|
||||
|
||||
private function prefix(int $type): ?string
|
||||
private function prefix(int $type): string|null
|
||||
{
|
||||
if (! isset(self::TOKEN_TYPE_TO_HIGHLIGHT[$type])) {
|
||||
return null;
|
||||
@@ -64,7 +62,7 @@ final class CliHighlighter implements Highlighter
|
||||
PHP_EOL,
|
||||
$this->escapeSequences[self::HIGHLIGHT_ERROR],
|
||||
$value,
|
||||
"\x1b[0m"
|
||||
"\x1b[0m",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,15 @@ namespace Doctrine\SqlFormatter;
|
||||
|
||||
final class Cursor
|
||||
{
|
||||
/** @var int */
|
||||
private $position = -1;
|
||||
private int $position = -1;
|
||||
|
||||
/** @var Token[] */
|
||||
private $tokens;
|
||||
|
||||
/**
|
||||
* @param Token[] $tokens
|
||||
*/
|
||||
public function __construct(array $tokens)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
/** @param Token[] $tokens */
|
||||
public function __construct(
|
||||
private readonly array $tokens,
|
||||
) {
|
||||
}
|
||||
|
||||
public function next(?int $exceptTokenType = null): ?Token
|
||||
public function next(int|null $exceptTokenType = null): Token|null
|
||||
{
|
||||
while ($token = $this->tokens[++$this->position] ?? null) {
|
||||
if ($exceptTokenType !== null && $token->isOfType($exceptTokenType)) {
|
||||
@@ -33,7 +27,7 @@ final class Cursor
|
||||
return null;
|
||||
}
|
||||
|
||||
public function previous(?int $exceptTokenType = null): ?Token
|
||||
public function previous(int|null $exceptTokenType = null): Token|null
|
||||
{
|
||||
while ($token = $this->tokens[--$this->position] ?? null) {
|
||||
if ($exceptTokenType !== null && $token->isOfType($exceptTokenType)) {
|
||||
|
||||
@@ -16,21 +16,17 @@ final class HtmlHighlighter implements Highlighter
|
||||
{
|
||||
public const HIGHLIGHT_PRE = 'pre';
|
||||
|
||||
/**
|
||||
* This flag tells us if queries need to be enclosed in <pre> tags
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $usePre;
|
||||
|
||||
/** @var array<string, string> */
|
||||
private $htmlAttributes;
|
||||
private readonly array $htmlAttributes;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $htmlAttributes
|
||||
* @param bool $usePre This flag tells us if queries need to be enclosed in <pre> tags
|
||||
*/
|
||||
public function __construct(array $htmlAttributes = [], bool $usePre = true)
|
||||
{
|
||||
public function __construct(
|
||||
array $htmlAttributes = [],
|
||||
private readonly bool $usePre = true,
|
||||
) {
|
||||
$this->htmlAttributes = $htmlAttributes + [
|
||||
self::HIGHLIGHT_QUOTE => 'style="color: blue;"',
|
||||
self::HIGHLIGHT_BACKTICK_QUOTE => 'style="color: purple;"',
|
||||
@@ -43,7 +39,6 @@ final class HtmlHighlighter implements Highlighter
|
||||
self::HIGHLIGHT_VARIABLE => 'style="color: orange;"',
|
||||
self::HIGHLIGHT_PRE => 'style="color: black; background-color: white;"',
|
||||
];
|
||||
$this->usePre = $usePre;
|
||||
}
|
||||
|
||||
public function highlightToken(int $type, string $value): string
|
||||
@@ -62,7 +57,7 @@ final class HtmlHighlighter implements Highlighter
|
||||
return '<span ' . $attributes . '>' . $value . '</span>';
|
||||
}
|
||||
|
||||
public function attributes(int $type): ?string
|
||||
public function attributes(int $type): string|null
|
||||
{
|
||||
if (! isset(self::TOKEN_TYPE_TO_HIGHLIGHT[$type])) {
|
||||
return null;
|
||||
@@ -77,7 +72,7 @@ final class HtmlHighlighter implements Highlighter
|
||||
'%s<span %s>%s</span>',
|
||||
PHP_EOL,
|
||||
$this->htmlAttributes[self::HIGHLIGHT_ERROR],
|
||||
$value
|
||||
$value,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,10 @@ use const PHP_SAPI;
|
||||
|
||||
final class SqlFormatter
|
||||
{
|
||||
/** @var Highlighter */
|
||||
private $highlighter;
|
||||
private readonly Highlighter $highlighter;
|
||||
private readonly Tokenizer $tokenizer;
|
||||
|
||||
/** @var Tokenizer */
|
||||
private $tokenizer;
|
||||
|
||||
public function __construct(?Highlighter $highlighter = null)
|
||||
public function __construct(Highlighter|null $highlighter = null)
|
||||
{
|
||||
$this->tokenizer = new Tokenizer();
|
||||
$this->highlighter = $highlighter ?? (PHP_SAPI === 'cli' ? new CliHighlighter() : new HtmlHighlighter());
|
||||
@@ -73,7 +70,7 @@ final class SqlFormatter
|
||||
while ($token = $cursor->next(Token::TOKEN_TYPE_WHITESPACE)) {
|
||||
$highlighted = $this->highlighter->highlightToken(
|
||||
$token->type(),
|
||||
$token->value()
|
||||
$token->value(),
|
||||
);
|
||||
|
||||
// If we are increasing the special indent level now
|
||||
@@ -175,7 +172,7 @@ final class SqlFormatter
|
||||
Token::TOKEN_TYPE_RESERVED_TOPLEVEL,
|
||||
Token::TOKEN_TYPE_RESERVED_NEWLINE,
|
||||
Token::TOKEN_TYPE_COMMENT,
|
||||
Token::TOKEN_TYPE_BLOCK_COMMENT
|
||||
Token::TOKEN_TYPE_BLOCK_COMMENT,
|
||||
)
|
||||
) {
|
||||
break;
|
||||
@@ -334,7 +331,7 @@ final class SqlFormatter
|
||||
Token::TOKEN_TYPE_QUOTE,
|
||||
Token::TOKEN_TYPE_BACKTICK_QUOTE,
|
||||
Token::TOKEN_TYPE_WORD,
|
||||
Token::TOKEN_TYPE_NUMBER
|
||||
Token::TOKEN_TYPE_NUMBER,
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
@@ -347,7 +344,7 @@ final class SqlFormatter
|
||||
if (array_search('block', $indentTypes) !== false) {
|
||||
$return = rtrim($return, ' ');
|
||||
$return .= $this->highlighter->highlightErrorMessage(
|
||||
'WARNING: unclosed parentheses or section'
|
||||
'WARNING: unclosed parentheses or section',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -373,7 +370,7 @@ final class SqlFormatter
|
||||
while ($token = $cursor->next()) {
|
||||
$return .= $this->highlighter->highlightToken(
|
||||
$token->type(),
|
||||
$token->value()
|
||||
$token->value(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -405,7 +402,7 @@ final class SqlFormatter
|
||||
$token->isOfType(
|
||||
Token::TOKEN_TYPE_RESERVED,
|
||||
Token::TOKEN_TYPE_RESERVED_NEWLINE,
|
||||
Token::TOKEN_TYPE_RESERVED_TOPLEVEL
|
||||
Token::TOKEN_TYPE_RESERVED_TOPLEVEL,
|
||||
)
|
||||
) {
|
||||
$newValue = preg_replace('/\s+/', ' ', $token->value());
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\SqlFormatter;
|
||||
|
||||
use function in_array;
|
||||
use function strpos;
|
||||
use function str_contains;
|
||||
|
||||
final class Token
|
||||
{
|
||||
@@ -28,16 +28,10 @@ final class Token
|
||||
public const TOKEN_TYPE = 0;
|
||||
public const TOKEN_VALUE = 1;
|
||||
|
||||
/** @var int */
|
||||
private $type;
|
||||
|
||||
/** @var string */
|
||||
private $value;
|
||||
|
||||
public function __construct(int $type, string $value)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->value = $value;
|
||||
public function __construct(
|
||||
private readonly int $type,
|
||||
private readonly string $value,
|
||||
) {
|
||||
}
|
||||
|
||||
public function value(): string
|
||||
@@ -57,9 +51,9 @@ final class Token
|
||||
|
||||
public function hasExtraWhitespace(): bool
|
||||
{
|
||||
return strpos($this->value(), ' ') !== false ||
|
||||
strpos($this->value(), "\n") !== false ||
|
||||
strpos($this->value(), "\t") !== false;
|
||||
return str_contains($this->value(), ' ') ||
|
||||
str_contains($this->value(), "\n") ||
|
||||
str_contains($this->value(), "\t");
|
||||
}
|
||||
|
||||
public function withValue(string $value): self
|
||||
|
||||
@@ -18,17 +18,15 @@ use function strpos;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
final class Tokenizer
|
||||
{
|
||||
/**
|
||||
* Reserved words (for syntax highlighting)
|
||||
*
|
||||
* @var string[]
|
||||
* @var list<string>
|
||||
*/
|
||||
private $reserved = [
|
||||
private array $reserved = [
|
||||
'ACCESSIBLE',
|
||||
'ACTION',
|
||||
'AFTER',
|
||||
@@ -314,9 +312,9 @@ final class Tokenizer
|
||||
* For SQL formatting
|
||||
* These keywords will all be on their own line
|
||||
*
|
||||
* @var string[]
|
||||
* @var list<string>
|
||||
*/
|
||||
private $reservedToplevel = [
|
||||
private array $reservedToplevel = [
|
||||
'WITH',
|
||||
'SELECT',
|
||||
'FROM',
|
||||
@@ -345,8 +343,8 @@ final class Tokenizer
|
||||
'WINDOW',
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private $reservedNewline = [
|
||||
/** @var list<string> */
|
||||
private array $reservedNewline = [
|
||||
'LEFT OUTER JOIN',
|
||||
'RIGHT OUTER JOIN',
|
||||
'LEFT JOIN',
|
||||
@@ -360,8 +358,8 @@ final class Tokenizer
|
||||
'EXCLUDE',
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private $functions = [
|
||||
/** @var list<string> */
|
||||
private array $functions = [
|
||||
'ABS',
|
||||
'ACOS',
|
||||
'ADDDATE',
|
||||
@@ -687,27 +685,18 @@ final class Tokenizer
|
||||
|
||||
// Regular expressions for tokenizing
|
||||
|
||||
/** @var string */
|
||||
private $regexBoundaries;
|
||||
|
||||
/** @var string */
|
||||
private $regexReserved;
|
||||
|
||||
/** @var string */
|
||||
private $regexReservedNewline;
|
||||
|
||||
/** @var string */
|
||||
private $regexReservedToplevel;
|
||||
|
||||
/** @var string */
|
||||
private $regexFunction;
|
||||
private readonly string $regexBoundaries;
|
||||
private readonly string $regexReserved;
|
||||
private readonly string $regexReservedNewline;
|
||||
private readonly string $regexReservedToplevel;
|
||||
private readonly string $regexFunction;
|
||||
|
||||
/**
|
||||
* Punctuation that can be used as a boundary between other tokens
|
||||
*
|
||||
* @var string[]
|
||||
* @var list<string>
|
||||
*/
|
||||
private $boundaries = [
|
||||
private array $boundaries = [
|
||||
',',
|
||||
';',
|
||||
'::', // PostgreSQL cast operator
|
||||
@@ -737,7 +726,7 @@ final class Tokenizer
|
||||
public function __construct()
|
||||
{
|
||||
// Sort reserved word list from longest word to shortest, 3x faster than usort
|
||||
$reservedMap = array_combine($this->reserved, array_map('strlen', $this->reserved));
|
||||
$reservedMap = array_combine($this->reserved, array_map(strlen(...), $this->reserved));
|
||||
assert($reservedMap !== false);
|
||||
arsort($reservedMap);
|
||||
$this->reserved = array_keys($reservedMap);
|
||||
@@ -745,19 +734,19 @@ final class Tokenizer
|
||||
// Set up regular expressions
|
||||
$this->regexBoundaries = '(' . implode(
|
||||
'|',
|
||||
$this->quoteRegex($this->boundaries)
|
||||
$this->quoteRegex($this->boundaries),
|
||||
) . ')';
|
||||
$this->regexReserved = '(' . implode(
|
||||
'|',
|
||||
$this->quoteRegex($this->reserved)
|
||||
$this->quoteRegex($this->reserved),
|
||||
) . ')';
|
||||
$this->regexReservedToplevel = str_replace(' ', '\\s+', '(' . implode(
|
||||
'|',
|
||||
$this->quoteRegex($this->reservedToplevel)
|
||||
$this->quoteRegex($this->reservedToplevel),
|
||||
) . ')');
|
||||
$this->regexReservedNewline = str_replace(' ', '\\s+', '(' . implode(
|
||||
'|',
|
||||
$this->quoteRegex($this->reservedNewline)
|
||||
$this->quoteRegex($this->reservedNewline),
|
||||
) . ')');
|
||||
|
||||
$this->regexFunction = '(' . implode('|', $this->quoteRegex($this->functions)) . ')';
|
||||
@@ -816,7 +805,7 @@ final class Tokenizer
|
||||
*
|
||||
* @return Token An associative array containing the type and value of the token.
|
||||
*/
|
||||
private function createNextToken(string $string, ?Token $previous = null): Token
|
||||
private function createNextToken(string $string, Token|null $previous = null): Token
|
||||
{
|
||||
$matches = [];
|
||||
// Whitespace
|
||||
@@ -854,7 +843,7 @@ final class Tokenizer
|
||||
($string[0] === '`' || $string[0] === '['
|
||||
? Token::TOKEN_TYPE_BACKTICK_QUOTE
|
||||
: Token::TOKEN_TYPE_QUOTE),
|
||||
$this->getQuotedString($string)
|
||||
$this->getQuotedString($string),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -884,7 +873,7 @@ final class Tokenizer
|
||||
preg_match(
|
||||
'/^([0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)($|\s|"\'`|' . $this->regexBoundaries . ')/',
|
||||
$string,
|
||||
$matches
|
||||
$matches,
|
||||
)
|
||||
) {
|
||||
return new Token(Token::TOKEN_TYPE_NUMBER, $matches[1]);
|
||||
@@ -904,12 +893,12 @@ final class Tokenizer
|
||||
preg_match(
|
||||
'/^(' . $this->regexReservedToplevel . ')($|\s|' . $this->regexBoundaries . ')/',
|
||||
$upper,
|
||||
$matches
|
||||
$matches,
|
||||
)
|
||||
) {
|
||||
return new Token(
|
||||
Token::TOKEN_TYPE_RESERVED_TOPLEVEL,
|
||||
substr($upper, 0, strlen($matches[1]))
|
||||
substr($upper, 0, strlen($matches[1])),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -918,12 +907,12 @@ final class Tokenizer
|
||||
preg_match(
|
||||
'/^(' . $this->regexReservedNewline . ')($|\s|' . $this->regexBoundaries . ')/',
|
||||
$upper,
|
||||
$matches
|
||||
$matches,
|
||||
)
|
||||
) {
|
||||
return new Token(
|
||||
Token::TOKEN_TYPE_RESERVED_NEWLINE,
|
||||
substr($upper, 0, strlen($matches[1]))
|
||||
substr($upper, 0, strlen($matches[1])),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -932,12 +921,12 @@ final class Tokenizer
|
||||
preg_match(
|
||||
'/^(' . $this->regexReserved . ')($|\s|' . $this->regexBoundaries . ')/',
|
||||
$upper,
|
||||
$matches
|
||||
$matches,
|
||||
)
|
||||
) {
|
||||
return new Token(
|
||||
Token::TOKEN_TYPE_RESERVED,
|
||||
substr($upper, 0, strlen($matches[1]))
|
||||
substr($upper, 0, strlen($matches[1])),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -949,7 +938,7 @@ final class Tokenizer
|
||||
if (preg_match('/^(' . $this->regexFunction . '[(]|\s|[)])/', $upper, $matches)) {
|
||||
return new Token(
|
||||
Token::TOKEN_TYPE_RESERVED,
|
||||
substr($upper, 0, strlen($matches[1]) - 1)
|
||||
substr($upper, 0, strlen($matches[1]) - 1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -968,9 +957,10 @@ final class Tokenizer
|
||||
*/
|
||||
private function quoteRegex(array $strings): array
|
||||
{
|
||||
return array_map(static function (string $string): string {
|
||||
return preg_quote($string, '/');
|
||||
}, $strings);
|
||||
return array_map(
|
||||
static fn (string $string): string => preg_quote($string, '/'),
|
||||
$strings,
|
||||
);
|
||||
}
|
||||
|
||||
private function getQuotedString(string $string): string
|
||||
@@ -989,7 +979,7 @@ final class Tokenizer
|
||||
(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)|
|
||||
((\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*(\'|$))+))/sx',
|
||||
$string,
|
||||
$matches
|
||||
$matches,
|
||||
)
|
||||
) {
|
||||
$ret = $matches[1];
|
||||
|
||||
@@ -9,6 +9,8 @@ use Doctrine\SqlFormatter\HtmlHighlighter;
|
||||
use Doctrine\SqlFormatter\NullHighlighter;
|
||||
use Doctrine\SqlFormatter\SqlFormatter;
|
||||
use Generator;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use UnexpectedValueException;
|
||||
|
||||
@@ -21,48 +23,33 @@ use function pack;
|
||||
use function sprintf;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* @covers \Doctrine\SqlFormatter\SqlFormatter
|
||||
*/
|
||||
#[CoversClass(SqlFormatter::class)]
|
||||
final class SqlFormatterTest extends TestCase
|
||||
{
|
||||
/** @var string[] */
|
||||
private $sqlData;
|
||||
|
||||
/** @var SqlFormatter */
|
||||
private $formatter;
|
||||
|
||||
/** @var HtmlHighlighter */
|
||||
private $highlighter;
|
||||
private SqlFormatter $formatter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Force SqlFormatter to run in non-CLI mode for tests
|
||||
$this->highlighter = new HtmlHighlighter();
|
||||
$highlighter = new HtmlHighlighter();
|
||||
|
||||
$this->formatter = new SqlFormatter($this->highlighter);
|
||||
$this->formatter = new SqlFormatter($highlighter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider formatHighlightData
|
||||
*/
|
||||
#[DataProvider('formatHighlightData')]
|
||||
public function testFormatHighlight(string $sql, string $html): void
|
||||
{
|
||||
$this->assertEquals(trim($html), trim($this->formatter->format($sql)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider formatData
|
||||
*/
|
||||
#[DataProvider('formatData')]
|
||||
public function testFormat(string $sql, string $html): void
|
||||
{
|
||||
$formatter = new SqlFormatter(new NullHighlighter());
|
||||
$this->assertEquals(trim($html), trim($formatter->format($sql)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider highlightData
|
||||
*/
|
||||
#[DataProvider('highlightData')]
|
||||
public function testHighlight(string $sql, string $html): void
|
||||
{
|
||||
$this->assertEquals(trim($html), trim($this->formatter->highlight($sql)));
|
||||
@@ -87,18 +74,14 @@ final class SqlFormatterTest extends TestCase
|
||||
$this->assertEquals(trim($html), trim($this->formatter->highlight($sql)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider highlightCliData
|
||||
*/
|
||||
#[DataProvider('highlightCliData')]
|
||||
public function testCliHighlight(string $sql, string $html): void
|
||||
{
|
||||
$formatter = new SqlFormatter(new CliHighlighter());
|
||||
$this->assertEquals(trim($html), trim($formatter->format($sql)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider compressData
|
||||
*/
|
||||
#[DataProvider('compressData')]
|
||||
public function testCompress(string $sql, string $html): void
|
||||
{
|
||||
$this->assertEquals(trim($html), trim($this->formatter->compress($sql)));
|
||||
@@ -118,21 +101,19 @@ final class SqlFormatterTest extends TestCase
|
||||
$this->assertEquals($actual, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<mixed[]>
|
||||
*/
|
||||
private function fileDataProvider(string $file): Generator
|
||||
/** @return Generator<mixed[]> */
|
||||
private static function fileDataProvider(string $file): Generator
|
||||
{
|
||||
$contents = file_get_contents(__DIR__ . '/' . $file);
|
||||
assert($contents !== false);
|
||||
$formatHighlightData = explode("\n---\n", $contents);
|
||||
$sqlData = $this->sqlData();
|
||||
$sqlData = self::sqlData();
|
||||
if (count($formatHighlightData) !== count($sqlData)) {
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
'"%s" (%d sections) and sql.sql (%d sections) should have the same number of sections',
|
||||
$file,
|
||||
count($formatHighlightData),
|
||||
count($sqlData)
|
||||
count($sqlData),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -141,57 +122,42 @@ final class SqlFormatterTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<mixed[]>
|
||||
*/
|
||||
public function formatHighlightData(): Generator
|
||||
/** @return Generator<mixed[]> */
|
||||
public static function formatHighlightData(): Generator
|
||||
{
|
||||
return $this->fileDataProvider('format-highlight.html');
|
||||
return self::fileDataProvider('format-highlight.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<mixed[]>
|
||||
*/
|
||||
public function highlightCliData(): Generator
|
||||
/** @return Generator<mixed[]> */
|
||||
public static function highlightCliData(): Generator
|
||||
{
|
||||
return $this->fileDataProvider('clihighlight.html');
|
||||
return self::fileDataProvider('clihighlight.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<mixed[]>
|
||||
*/
|
||||
public function formatData(): Generator
|
||||
/** @return Generator<mixed[]> */
|
||||
public static function formatData(): Generator
|
||||
{
|
||||
return $this->fileDataProvider('format.html');
|
||||
return self::fileDataProvider('format.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<mixed[]>
|
||||
*/
|
||||
public function compressData(): Generator
|
||||
/** @return Generator<mixed[]> */
|
||||
public static function compressData(): Generator
|
||||
{
|
||||
return $this->fileDataProvider('compress.html');
|
||||
return self::fileDataProvider('compress.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<mixed[]>
|
||||
*/
|
||||
public function highlightData(): Generator
|
||||
/** @return Generator<mixed[]> */
|
||||
public static function highlightData(): Generator
|
||||
{
|
||||
return $this->fileDataProvider('highlight.html');
|
||||
return self::fileDataProvider('highlight.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function sqlData(): array
|
||||
/** @return mixed[] */
|
||||
private static function sqlData(): array
|
||||
{
|
||||
if (! $this->sqlData) {
|
||||
$contents = file_get_contents(__DIR__ . '/sql.sql');
|
||||
assert($contents !== false);
|
||||
$this->sqlData = explode("\n---\n", $contents);
|
||||
}
|
||||
$contents = file_get_contents(__DIR__ . '/sql.sql');
|
||||
assert($contents !== false);
|
||||
|
||||
return $this->sqlData;
|
||||
return explode("\n---\n", $contents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@ declare(strict_types=1);
|
||||
namespace Doctrine\SqlFormatter\Tests;
|
||||
|
||||
use Doctrine\SqlFormatter\Tokenizer;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(Tokenizer::class)]
|
||||
final class TokenizerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @doesNotPerformAssertions
|
||||
*/
|
||||
#[DoesNotPerformAssertions]
|
||||
public function testThereAreNoRegressions(): void
|
||||
{
|
||||
(new Tokenizer())->tokenize('*/');
|
||||
|
||||
Reference in New Issue
Block a user