Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ]
coverage-driver: [ 'pcov' ]

steps:
Expand Down Expand Up @@ -43,10 +43,12 @@ jobs:
run: composer install --prefer-dist

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

- name: Static Analysis
run: ./bin/psalm.phar
if: ${{ matrix.php-versions == '7.4' }}
run: ./bin/psalm.phar --no-cache

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

phar:
runs-on: "ubuntu-20.04"
runs-on: "ubuntu-22.04"
needs: build
steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -95,8 +97,7 @@ jobs:
runs-on: "ubuntu-22.04"
strategy:
matrix:
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]

php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ]
steps:
- name: Install PHP
uses: shivammathur/setup-php@v2
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ coverage: ## it launches coverage
phpdbg -qrr ./bin/phpunit --coverage-html build/coverage

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

psalm: ## it launches psalm
bin/psalm.phar

build: ## it launches all the build
composer install
PHP_CS_FIXER_IGNORE_ENV=1 bin/php-cs-fixer fix -v
bin/psalm
bin/psalm.phar --no-cache
bin/phpunit

sfbuild: ## it launches all the build
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"symfony/event-dispatcher": "^3.0|^4.0|^5.0|^6.0|^7.0",
"symfony/console": "^3.0|^4.0|^5.0|^6.0|^7.0",
"symfony/polyfill-php80": "^1.20",
"nikic/php-parser": "~4",
"nikic/php-parser": "~5",
"webmozart/assert": "^1.9",
"ext-json": "*",
"phpstan/phpdoc-parser": "^1.2",
Expand Down Expand Up @@ -60,4 +60,4 @@
"bin": [
"bin-stub/phparkitect"
]
}
}
10 changes: 3 additions & 7 deletions src/Analyzer/FileParser.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?php

declare(strict_types=1);

namespace Arkitect\Analyzer;

use Arkitect\CLI\TargetPhpVersion;
use Arkitect\Rules\ParsingError;
use PhpParser\ErrorHandler\Collecting;
use PhpParser\Lexer\Emulative;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use PhpParser\PhpVersion;

class FileParser implements Parser
{
Expand All @@ -33,12 +34,7 @@ public function __construct(
$this->fileVisitor = $fileVisitor;
$this->parsingErrors = [];

$lexer = new Emulative([
'usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'],
'phpVersion' => $targetPhpVersion->get() ?? phpversion(),
]);

$this->parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, $lexer);
$this->parser = (new ParserFactory())->createForVersion(PhpVersion::fromString($targetPhpVersion->get() ?? phpversion()));
$this->traverser = $traverser;
$this->traverser->addVisitor($nameResolver);
$this->traverser->addVisitor($this->fileVisitor);
Expand Down
51 changes: 37 additions & 14 deletions src/Analyzer/NameResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
class NameResolver extends NodeVisitorAbstract
{
/** @var NameContext Naming context */
protected $nameContext;
protected NameContext $nameContext;

/** @var bool Whether to preserve original names */
protected $preserveOriginalNames;
protected bool $preserveOriginalNames;

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

/** @var bool Whether to parse DocBlock Custom Annotations */
protected $parseCustomAnnotations;
Expand All @@ -55,8 +55,8 @@
* namespacedName attribute, as usual.)
* * parseCustomAnnotations (default true): Whether to parse DocBlock Custom Annotations.
*
* @param ErrorHandler|null $errorHandler Error handler
* @param array $options Options
* @param ErrorHandler|null $errorHandler Error handler
* @param array{preserveOriginalNames?: bool, replaceNodes?: bool, parseCustomAnnotations?: bool} $options Options
*/
public function __construct(?ErrorHandler $errorHandler = null, array $options = [])
{
Expand All @@ -79,7 +79,7 @@
return $this->nameContext;
}

public function beforeTraverse(array $nodes)
public function beforeTraverse(array $nodes): ?array
{
$this->nameContext->startNamespace();

Expand Down Expand Up @@ -110,6 +110,8 @@
$this->resolveAttrGroups($node);
if (null !== $node->name) {
$this->addNamespacedName($node);
} else {
$node->namespacedName = null;
}
} elseif ($node instanceof Stmt\Interface_) {
foreach ($node->extends as &$interface) {
Expand All @@ -134,7 +136,8 @@
$this->resolveSignature($node);
$this->resolveAttrGroups($node);
$this->addNamespacedName($node);
} elseif ($node instanceof Stmt\ClassMethod
} elseif (
$node instanceof Stmt\ClassMethod
|| $node instanceof Expr\Closure
|| $node instanceof Expr\ArrowFunction
) {
Expand Down Expand Up @@ -183,15 +186,25 @@
}
}
}
} elseif ($node instanceof Node\PropertyHook) {
foreach ($node->params as $param) {
$param->type = $this->resolveType($param->type);
$this->resolveAttrGroups($param);

Check warning on line 192 in src/Analyzer/NameResolver.php

View check run for this annotation

Codecov / codecov/patch

src/Analyzer/NameResolver.php#L190-L192

Added lines #L190 - L192 were not covered by tests
}
$this->resolveAttrGroups($node);

Check warning on line 194 in src/Analyzer/NameResolver.php

View check run for this annotation

Codecov / codecov/patch

src/Analyzer/NameResolver.php#L194

Added line #L194 was not covered by tests
} elseif ($node instanceof Stmt\Const_) {
foreach ($node->consts as $const) {
$this->addNamespacedName($const);
}
} elseif ($node instanceof Stmt\ClassConst) {
if (null !== $node->type) {
$node->type = $this->resolveType($node->type);

Check warning on line 201 in src/Analyzer/NameResolver.php

View check run for this annotation

Codecov / codecov/patch

src/Analyzer/NameResolver.php#L201

Added line #L201 was not covered by tests
}
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\EnumCase) {
$this->resolveAttrGroups($node);
} elseif ($node instanceof Expr\StaticCall
} elseif (
$node instanceof Expr\StaticCall
|| $node instanceof Expr\StaticPropertyFetch
|| $node instanceof Expr\ClassConstFetch
|| $node instanceof Expr\New_
Expand Down Expand Up @@ -234,8 +247,8 @@
/**
* Resolve name, according to name resolver options.
*
* @param Name $name Function or constant name to resolve
* @param int $type One of Stmt\Use_::TYPE_*
* @param Name $name Function or constant name to resolve
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
*
* @return Name Resolved name, or original name with attribute
*/
Expand Down Expand Up @@ -307,10 +320,15 @@
}
}

private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null): void
/**
* @param Stmt\Use_::TYPE_* $type
* @param ?Name $prefix
*
* @psalm-suppress PossiblyNullArgument
*/
private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void
{
// Add prefix for group uses
/** @var Name $name */
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
// Type is determined either by individual element or whole use declaration
$type |= $use->type;
Expand Down Expand Up @@ -364,10 +382,15 @@
* @psalm-suppress MissingParamType
* @psalm-suppress PossiblyNullArgument
* @psalm-suppress MissingReturnType
* @psalm-suppress InvalidReturnStatement
*
* @template T of Node\Identifier|Name|Node\ComplexType|null
*
* @param T $node
*
* @param mixed $node
* @return T
*/
private function resolveType($node)
private function resolveType(?Node $node): ?Node
{
if ($node instanceof Name) {
return $this->resolveClassName($node);
Expand Down
1 change: 1 addition & 0 deletions src/CLI/TargetPhpVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class TargetPhpVersion
'8.1',
'8.2',
'8.3',
'8.4',
];

/** @var string|null */
Expand Down
34 changes: 34 additions & 0 deletions tests/Unit/Analyzer/FileVisitorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1343,4 +1343,38 @@ enum IntEnum: int
EOF
];
}

/**
* @requires PHP >= 8.4
*/
public function test_it_parse_property_hooks(): void
{
$code = <<< 'EOF'
<?php
namespace App\Foo;

class User {
private string $firstName;
private string $lastName;

public function __construct(string $firstName, string $lastName) {
$this->firstName = $firstName;
$this->lastName = $lastName;
}

public string $fullName {
get => $this->firstName . ' ' . $this->lastName;
set {[$this->firstName, $this->lastName] = explode(' ', $value, 2);}
}
}
EOF;

/** @var FileParser $fp */
$fp = FileParserFactory::createFileParser(TargetPhpVersion::create('8.4'));
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

$this->assertInstanceOf(ClassDescription::class, $cd[0]);
}
}