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
5 changes: 5 additions & 0 deletions src/Analyzer/FileParserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ public static function createFileParser(TargetPhpVersion $targetPhpVersion, bool
$targetPhpVersion
);
}

public static function forPhpVersion(string $targetPhpVersion): FileParser
{
return self::createFileParser(TargetPhpVersion::create($targetPhpVersion), true);
}
}
9 changes: 8 additions & 1 deletion src/CLI/TargetPhpVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

class TargetPhpVersion
{
public const PHP_7_4 = '7.4';
public const PHP_8_0 = '8.0';
public const PHP_8_1 = '8.1';
public const PHP_8_2 = '8.2';
public const PHP_8_3 = '8.3';
public const PHP_8_4 = '8.4';

public const VALID_PHP_VERSIONS = [
'7.4',
'8.0',
Expand Down Expand Up @@ -35,7 +42,7 @@ private function __construct(string $version)

public static function latest(): self
{
return new self('8.4');
return new self(self::PHP_8_4);
}

public static function create(?string $version): self
Expand Down
134 changes: 134 additions & 0 deletions tests/Unit/Analyzer/FileParser/CanParseAttributesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

declare(strict_types=1);

namespace Arkitect\Tests\Unit\Analyzer\FileParser;

use Arkitect\Analyzer\FileParserFactory;
use Arkitect\Analyzer\FullyQualifiedClassName;
use Arkitect\CLI\TargetPhpVersion;
use PHPUnit\Framework\TestCase;

class CanParseAttributesTest extends TestCase
{
public function test_should_parse_class_attributes(): void
{
$code = <<< 'EOF'
<?php

use Bar\FooAttr;

#[FooAttr('bar')]
#[Baz]
class Foo {}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_0);
$fp->parse($code, 'relativePathName');
$cd = $fp->getClassDescriptions();

self::assertEquals(
[
FullyQualifiedClassName::fromString('Bar\\FooAttr'),
FullyQualifiedClassName::fromString('Baz'),
],
$cd[0]->getAttributes()
);
}

public function test_it_should_parse_traits_attributes(): void
{
$code = <<< 'EOF'
<?php

namespace Root\Cars;

use Bar\FooAttr;

#[FooAttr('bar')]
trait ATrait
{
#[Baz]
public function foo(): string { return 'foo'; }
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

self::assertEquals(
[
FullyQualifiedClassName::fromString('Bar\\FooAttr'),
FullyQualifiedClassName::fromString('Root\\Cars\\Baz'),
],
$cd[0]->getAttributes()
);
}

public function test_should_parse_enum_attributes(): void
{
$code = <<< 'EOF'
<?php

namespace Root\Cars;

use Bar\FooAttr;

#[FooAttr('bar')]
#[Baz]
enum Enum
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

self::assertEquals(
[
FullyQualifiedClassName::fromString('Bar\\FooAttr'),
FullyQualifiedClassName::fromString('Root\\Cars\\Baz'),
],
$cd[0]->getAttributes()
);
}

public function test_it_should_parse_interface_attributes(): void
{
$code = <<< 'EOF'
<?php

namespace Root\Cars;

use Bar\FooAttr;

#[FooAttr('bar')]
interface AnInterface
{
#[Baz]
public function foo(): string;
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

self::assertEquals(
[
FullyQualifiedClassName::fromString('Bar\\FooAttr'),
FullyQualifiedClassName::fromString('Root\\Cars\\Baz'),
],
$cd[0]->getAttributes()
);
}
}
159 changes: 159 additions & 0 deletions tests/Unit/Analyzer/FileParser/CanParseClassPropertiesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

declare(strict_types=1);

namespace Arkitect\Tests\Unit\Analyzer\FileParser;

use Arkitect\Analyzer\FileParserFactory;
use Arkitect\CLI\TargetPhpVersion;
use Arkitect\Expression\ForClasses\DependsOnlyOnTheseNamespaces;
use Arkitect\Expression\ForClasses\NotHaveDependencyOutsideNamespace;
use Arkitect\Rules\Violations;
use PHPUnit\Framework\TestCase;

class CanParseClassPropertiesTest extends TestCase
{
public function test_it_parse_typed_property(): void
{
$code = <<< 'EOF'
<?php
namespace MyProject\AppBundle\Application;

use Symfony\Component\Validator\Constraints\NotBlank;

class ApplicationLevelDto
{
public NotBlank $foo;
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

$violations = new Violations();

$notHaveDependencyOutsideNamespace = new DependsOnlyOnTheseNamespaces(['MyProject\AppBundle\Application']);
$notHaveDependencyOutsideNamespace->evaluate($cd[0], $violations, 'we want to add this rule for our software');

self::assertCount(1, $violations);
}

public function test_it_parse_typed_nullable_property(): void
{
$code = <<< 'EOF'
<?php
namespace MyProject\AppBundle\Application;

use Symfony\Component\Validator\Constraints\NotBlank;

class ApplicationLevelDto
{
public ?NotBlank $foo;
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

$violations = new Violations();

$notHaveDependencyOutsideNamespace = new DependsOnlyOnTheseNamespaces(['MyProject\AppBundle\Application']);
$notHaveDependencyOutsideNamespace->evaluate($cd[0], $violations, 'we want to add this rule for our software');

self::assertCount(1, $violations);
}

public function test_it_parse_scalar_typed_property(): void
{
$code = <<< 'EOF'
<?php

namespace MyProject\AppBundle\Application;

class ApplicationLevelDto
{
public bool $fooBool;
public int $fooInt;
public float $fooFloat;
public string $fooString;
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

$violations = new Violations();

$notHaveDependencyOutsideNamespace = new NotHaveDependencyOutsideNamespace('MyProject\AppBundle\Application');
$notHaveDependencyOutsideNamespace->evaluate($cd[0], $violations, 'we want to add this rule for our software');

self::assertCount(0, $violations);
}

public function test_it_parse_nullable_scalar_typed_property(): void
{
$code = <<< 'EOF'
<?php
namespace MyProject\AppBundle\Application;
class ApplicationLevelDto
{
public function __construct(
?bool $fooBool,
?int $fooInt,
?float $fooFloat,
?string $fooString
) {

}
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

$violations = new Violations();

$notHaveDependencyOutsideNamespace = new NotHaveDependencyOutsideNamespace('MyProject\AppBundle\Application');
$notHaveDependencyOutsideNamespace->evaluate($cd[0], $violations, 'we want to add this rule for our software');

self::assertCount(0, $violations);
}

public function test_it_parse_arrays_as_scalar_types(): void
{
$code = <<< 'EOF'
<?php

namespace App\Domain;

class MyClass
{
private array $field1;
public function __construct(array $field1)
{
$this->field1 = $field1;
}
}
EOF;

$fp = FileParserFactory::forPhpVersion(TargetPhpVersion::PHP_8_1);
$fp->parse($code, 'relativePathName');

$cd = $fp->getClassDescriptions();

$violations = new Violations();

$notHaveDependenciesOutside = new NotHaveDependencyOutsideNamespace('App\Domain');
$notHaveDependenciesOutside->evaluate($cd[0], $violations, 'we want to add this rule for our software');

self::assertCount(0, $violations);
}
}
Loading