Skip to content

Commit

Permalink
Fix token scanning/parsing for PHP8 named arguments (#1019)
Browse files Browse the repository at this point in the history
In particular handle argument names matching reserved words like 'class',
'interface' or 'trait'!
  • Loading branch information
DerManoMann authored Dec 10, 2021
1 parent cd7cb60 commit 6bfe1c4
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 2 deletions.
17 changes: 17 additions & 0 deletions src/Analysers/TokenAnalyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
continue;
}

if (!is_array($token)) {
// PHP 8 named argument
continue;
}

$interfaceDefinition = false;
$traitDefinition = false;

Expand Down Expand Up @@ -177,6 +182,12 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
$traitDefinition = false;

$token = $this->nextToken($tokens, $parseContext);

if (!is_array($token)) {
// PHP 8 named argument
continue;
}

$schemaContext = new Context(['interface' => $token[1], 'line' => $token[2]], $parseContext);
if ($interfaceDefinition) {
$analysis->addInterfaceDefinition($interfaceDefinition);
Expand Down Expand Up @@ -211,6 +222,12 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
$interfaceDefinition = false;

$token = $this->nextToken($tokens, $parseContext);

if (!is_array($token)) {
// PHP 8 named argument
continue;
}

$schemaContext = new Context(['trait' => $token[1], 'line' => $token[2]], $parseContext);
if ($traitDefinition) {
$analysis->addTraitDefinition($traitDefinition);
Expand Down
22 changes: 21 additions & 1 deletion src/Analysers/TokenScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@ protected function scanTokens(array $tokens): array
}
continue;
}

switch ($token[0]) {
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
$stack[] = $token[1];
break;

case T_NAMESPACE:
$namespace = $this->nextWord($tokens);
break;

case T_USE:
if (!$stack) {
$uses = array_merge($uses, $this->parseFQNStatement($tokens, $token));
Expand All @@ -64,7 +67,12 @@ protected function scanTokens(array $tokens): array
$units[$currentName]['traits'] = array_merge($units[$currentName]['traits'], $traits);
}
break;

case T_CLASS:
if ($stack) {
break;
}

if ($lastToken && is_array($lastToken) && $lastToken[0] === T_DOUBLE_COLON) {
// ::class
break;
Expand All @@ -89,19 +97,30 @@ protected function scanTokens(array $tokens): array
$currentName = $namespace . '\\' . $token[1];
$units[$currentName] = ['uses' => $uses, 'interfaces' => [], 'traits' => [], 'methods' => [], 'properties' => []];
break;

case T_INTERFACE:
if ($stack) {
break;
}

$isInterface = true;
$token = $this->nextToken($tokens);
$currentName = $namespace . '\\' . $token[1];
$units[$currentName] = ['uses' => $uses, 'interfaces' => [], 'traits' => [], 'methods' => [], 'properties' => []];
break;

case T_TRAIT:
if ($stack) {
break;
}

$isInterface = false;
$token = $this->nextToken($tokens);
$currentName = $namespace . '\\' . $token[1];
$this->skipTo($tokens, '{', true);
$units[$currentName] = ['uses' => $uses, 'interfaces' => [], 'traits' => [], 'methods' => [], 'properties' => []];
break;

case T_EXTENDS:
$fqns = $this->parseFQNStatement($tokens, $token);
if ($isInterface && $currentName) {
Expand All @@ -117,6 +136,7 @@ protected function scanTokens(array $tokens): array
$units[$currentName]['interfaces'] = $this->resolveFQN($fqns, $namespace, $uses);
}
break;

case T_FUNCTION:
$token = $this->nextToken($tokens);

Expand All @@ -128,10 +148,10 @@ protected function scanTokens(array $tokens): array
// no function body
$this->skipTo($tokens, ';');
}

$units[$currentName]['methods'][] = $token[1];
}
break;

case T_VARIABLE:
if (1 == count($stack) && $currentName) {
$units[$currentName]['properties'][] = substr($token[1], 1);
Expand Down
12 changes: 12 additions & 0 deletions tests/Analysers/TokenAnalyserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,16 @@ public function testAnonymousFunctions()
$infos = $analysis->getAnnotationsOfType(Info::class, true);
$this->assertCount(1, $infos);
}

/**
* @requires PHP 8
*/
public function testPhp8NamedArguments()
{
$analysis = $this->analysisFromFixtures(['PHP/Php8NamedArguments.php'], [], new TokenAnalyser());
$schemas = $analysis->getAnnotationsOfType(Schema::class, true);

$this->assertCount(1, $schemas);
$analysis->process((new Generator())->getProcessors());
}
}
12 changes: 12 additions & 0 deletions tests/Analysers/TokenScannerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ public function scanCases()
],
],
],
'Php8NamedArguments' => [
'PHP/Php8NamedArguments.php',
[
'OpenApi\\Tests\\Fixtures\\PHP\\Php8NamedArguments' => [
'uses' => [],
'interfaces' => [],
'traits' => [],
'methods' => ['useFoo', 'foo'],
'properties' => [],
],
],
],
'AnonymousFunctions' => [
'PHP/AnonymousFunctions.php',
[
Expand Down
23 changes: 23 additions & 0 deletions tests/Fixtures/PHP/Php8NamedArguments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Tests\Fixtures\PHP;

/**
* @OA\Schema()
*/
class Php8NamedArguments
{
public function useFoo(): void
{
$this->foo(class: 'abc', interface: 'def', trait: 'xyz');
}

public function foo(string $class, string $interface, string $trait): void
{

}
}
6 changes: 5 additions & 1 deletion tests/Fixtures/PHP/php7.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

namespace OpenApi\Tests\Fixtures\PHP;

use PHPUnit\Framework\TestCase;

$a = new class {
public function foo()
{
Expand Down Expand Up @@ -56,4 +58,6 @@ public function fuu()

function deng()
{
}
}

$foo = TestCase::class;

0 comments on commit 6bfe1c4

Please sign in to comment.