Skip to content

Commit 483b878

Browse files
Support php 8.4 (#447)
* adds php 8.4 to build test matrix * runs static checks in the build matrix only on php 7.4 to avoid getting different results based on the tool and/or php version * updates our custom NameResolver (which has the capability to parse docblocks) to latest version
1 parent cda58ca commit 483b878

File tree

7 files changed

+85
-30
lines changed

7 files changed

+85
-30
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
16+
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ]
1717
coverage-driver: [ 'pcov' ]
1818

1919
steps:
@@ -43,10 +43,12 @@ jobs:
4343
run: composer install --prefer-dist
4444

4545
- name: Coding Standard Checks
46+
if: ${{ matrix.php-versions == '7.4' }}
4647
run: PHP_CS_FIXER_IGNORE_ENV=1 ./bin/php-cs-fixer fix --dry-run -v
4748

4849
- name: Static Analysis
49-
run: ./bin/psalm.phar
50+
if: ${{ matrix.php-versions == '7.4' }}
51+
run: ./bin/psalm.phar --no-cache
5052

5153
- name: Test
5254
run: ./bin/phpunit -d memory_limit=-1 --coverage-clover clover.xml
@@ -58,7 +60,7 @@ jobs:
5860
token: ${{ secrets.CODECOV_TOKEN }}
5961

6062
phar:
61-
runs-on: "ubuntu-20.04"
63+
runs-on: "ubuntu-22.04"
6264
needs: build
6365
steps:
6466
- uses: actions/checkout@v3
@@ -95,8 +97,7 @@ jobs:
9597
runs-on: "ubuntu-22.04"
9698
strategy:
9799
matrix:
98-
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
99-
100+
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ]
100101
steps:
101102
- name: Install PHP
102103
uses: shivammathur/setup-php@v2

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ coverage: ## it launches coverage
3333
phpdbg -qrr ./bin/phpunit --coverage-html build/coverage
3434

3535
csfix: ## it launches cs fix
36-
bin/php-cs-fixer fix -v
36+
PHP_CS_FIXER_IGNORE_ENV=1 bin/php-cs-fixer fix -v
3737

3838
psalm: ## it launches psalm
3939
bin/psalm.phar
4040

4141
build: ## it launches all the build
4242
composer install
4343
PHP_CS_FIXER_IGNORE_ENV=1 bin/php-cs-fixer fix -v
44-
bin/psalm
44+
bin/psalm.phar --no-cache
4545
bin/phpunit
4646

4747
sfbuild: ## it launches all the build

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"symfony/event-dispatcher": "^3.0|^4.0|^5.0|^6.0|^7.0",
2929
"symfony/console": "^3.0|^4.0|^5.0|^6.0|^7.0",
3030
"symfony/polyfill-php80": "^1.20",
31-
"nikic/php-parser": "~4",
31+
"nikic/php-parser": "~5",
3232
"webmozart/assert": "^1.9",
3333
"ext-json": "*",
3434
"phpstan/phpdoc-parser": "^1.2",
@@ -60,4 +60,4 @@
6060
"bin": [
6161
"bin-stub/phparkitect"
6262
]
63-
}
63+
}

src/Analyzer/FileParser.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<?php
2+
23
declare(strict_types=1);
34

45
namespace Arkitect\Analyzer;
56

67
use Arkitect\CLI\TargetPhpVersion;
78
use Arkitect\Rules\ParsingError;
89
use PhpParser\ErrorHandler\Collecting;
9-
use PhpParser\Lexer\Emulative;
1010
use PhpParser\NodeTraverser;
1111
use PhpParser\ParserFactory;
12+
use PhpParser\PhpVersion;
1213

1314
class FileParser implements Parser
1415
{
@@ -33,12 +34,7 @@ public function __construct(
3334
$this->fileVisitor = $fileVisitor;
3435
$this->parsingErrors = [];
3536

36-
$lexer = new Emulative([
37-
'usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'],
38-
'phpVersion' => $targetPhpVersion->get() ?? phpversion(),
39-
]);
40-
41-
$this->parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, $lexer);
37+
$this->parser = (new ParserFactory())->createForVersion(PhpVersion::fromString($targetPhpVersion->get() ?? phpversion()));
4238
$this->traverser = $traverser;
4339
$this->traverser->addVisitor($nameResolver);
4440
$this->traverser->addVisitor($this->fileVisitor);

src/Analyzer/NameResolver.php

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
class NameResolver extends NodeVisitorAbstract
2828
{
2929
/** @var NameContext Naming context */
30-
protected $nameContext;
30+
protected NameContext $nameContext;
3131

3232
/** @var bool Whether to preserve original names */
33-
protected $preserveOriginalNames;
33+
protected bool $preserveOriginalNames;
3434

3535
/** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */
36-
protected $replaceNodes;
36+
protected bool $replaceNodes;
3737

3838
/** @var bool Whether to parse DocBlock Custom Annotations */
3939
protected $parseCustomAnnotations;
@@ -55,8 +55,8 @@ class NameResolver extends NodeVisitorAbstract
5555
* namespacedName attribute, as usual.)
5656
* * parseCustomAnnotations (default true): Whether to parse DocBlock Custom Annotations.
5757
*
58-
* @param ErrorHandler|null $errorHandler Error handler
59-
* @param array $options Options
58+
* @param ErrorHandler|null $errorHandler Error handler
59+
* @param array{preserveOriginalNames?: bool, replaceNodes?: bool, parseCustomAnnotations?: bool} $options Options
6060
*/
6161
public function __construct(?ErrorHandler $errorHandler = null, array $options = [])
6262
{
@@ -79,7 +79,7 @@ public function getNameContext(): NameContext
7979
return $this->nameContext;
8080
}
8181

82-
public function beforeTraverse(array $nodes)
82+
public function beforeTraverse(array $nodes): ?array
8383
{
8484
$this->nameContext->startNamespace();
8585

@@ -110,6 +110,8 @@ public function enterNode(Node $node)
110110
$this->resolveAttrGroups($node);
111111
if (null !== $node->name) {
112112
$this->addNamespacedName($node);
113+
} else {
114+
$node->namespacedName = null;
113115
}
114116
} elseif ($node instanceof Stmt\Interface_) {
115117
foreach ($node->extends as &$interface) {
@@ -134,7 +136,8 @@ public function enterNode(Node $node)
134136
$this->resolveSignature($node);
135137
$this->resolveAttrGroups($node);
136138
$this->addNamespacedName($node);
137-
} elseif ($node instanceof Stmt\ClassMethod
139+
} elseif (
140+
$node instanceof Stmt\ClassMethod
138141
|| $node instanceof Expr\Closure
139142
|| $node instanceof Expr\ArrowFunction
140143
) {
@@ -183,15 +186,25 @@ public function enterNode(Node $node)
183186
}
184187
}
185188
}
189+
} elseif ($node instanceof Node\PropertyHook) {
190+
foreach ($node->params as $param) {
191+
$param->type = $this->resolveType($param->type);
192+
$this->resolveAttrGroups($param);
193+
}
194+
$this->resolveAttrGroups($node);
186195
} elseif ($node instanceof Stmt\Const_) {
187196
foreach ($node->consts as $const) {
188197
$this->addNamespacedName($const);
189198
}
190199
} elseif ($node instanceof Stmt\ClassConst) {
200+
if (null !== $node->type) {
201+
$node->type = $this->resolveType($node->type);
202+
}
191203
$this->resolveAttrGroups($node);
192204
} elseif ($node instanceof Stmt\EnumCase) {
193205
$this->resolveAttrGroups($node);
194-
} elseif ($node instanceof Expr\StaticCall
206+
} elseif (
207+
$node instanceof Expr\StaticCall
195208
|| $node instanceof Expr\StaticPropertyFetch
196209
|| $node instanceof Expr\ClassConstFetch
197210
|| $node instanceof Expr\New_
@@ -234,8 +247,8 @@ public function enterNode(Node $node)
234247
/**
235248
* Resolve name, according to name resolver options.
236249
*
237-
* @param Name $name Function or constant name to resolve
238-
* @param int $type One of Stmt\Use_::TYPE_*
250+
* @param Name $name Function or constant name to resolve
251+
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
239252
*
240253
* @return Name Resolved name, or original name with attribute
241254
*/
@@ -307,10 +320,15 @@ protected function resolveAttrGroups(Node $node): void
307320
}
308321
}
309322

310-
private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null): void
323+
/**
324+
* @param Stmt\Use_::TYPE_* $type
325+
* @param ?Name $prefix
326+
*
327+
* @psalm-suppress PossiblyNullArgument
328+
*/
329+
private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void
311330
{
312331
// Add prefix for group uses
313-
/** @var Name $name */
314332
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
315333
// Type is determined either by individual element or whole use declaration
316334
$type |= $use->type;
@@ -364,10 +382,15 @@ private function resolveSignature($node): void
364382
* @psalm-suppress MissingParamType
365383
* @psalm-suppress PossiblyNullArgument
366384
* @psalm-suppress MissingReturnType
385+
* @psalm-suppress InvalidReturnStatement
386+
*
387+
* @template T of Node\Identifier|Name|Node\ComplexType|null
388+
*
389+
* @param T $node
367390
*
368-
* @param mixed $node
391+
* @return T
369392
*/
370-
private function resolveType($node)
393+
private function resolveType(?Node $node): ?Node
371394
{
372395
if ($node instanceof Name) {
373396
return $this->resolveClassName($node);

src/CLI/TargetPhpVersion.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class TargetPhpVersion
1414
'8.1',
1515
'8.2',
1616
'8.3',
17+
'8.4',
1718
];
1819

1920
/** @var string|null */

tests/Unit/Analyzer/FileVisitorTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,4 +1343,38 @@ enum IntEnum: int
13431343
EOF
13441344
];
13451345
}
1346+
1347+
/**
1348+
* @requires PHP >= 8.4
1349+
*/
1350+
public function test_it_parse_property_hooks(): void
1351+
{
1352+
$code = <<< 'EOF'
1353+
<?php
1354+
namespace App\Foo;
1355+
1356+
class User {
1357+
private string $firstName;
1358+
private string $lastName;
1359+
1360+
public function __construct(string $firstName, string $lastName) {
1361+
$this->firstName = $firstName;
1362+
$this->lastName = $lastName;
1363+
}
1364+
1365+
public string $fullName {
1366+
get => $this->firstName . ' ' . $this->lastName;
1367+
set {[$this->firstName, $this->lastName] = explode(' ', $value, 2);}
1368+
}
1369+
}
1370+
EOF;
1371+
1372+
/** @var FileParser $fp */
1373+
$fp = FileParserFactory::createFileParser(TargetPhpVersion::create('8.4'));
1374+
$fp->parse($code, 'relativePathName');
1375+
1376+
$cd = $fp->getClassDescriptions();
1377+
1378+
$this->assertInstanceOf(ClassDescription::class, $cd[0]);
1379+
}
13461380
}

0 commit comments

Comments
 (0)