diff --git a/src/ClassScanner/ClassMap.php b/src/ClassScanner/ClassMap.php index 4ff367d..83fde8d 100644 --- a/src/ClassScanner/ClassMap.php +++ b/src/ClassScanner/ClassMap.php @@ -134,8 +134,16 @@ public static function fromFile(string $filename): self */ public static function fromFileHandle($fileHandle): ClassMap { + \fseek($fileHandle, 0); $cacheContents = \stream_get_contents($fileHandle); + if ($cacheContents === '') { + throw new \InvalidArgumentException('Cannot read empty file handle'); + } + $cacheContentsJson = \json_decode($cacheContents, true, 512, \JSON_THROW_ON_ERROR); + if (!$cacheContentsJson) { + throw new \InvalidArgumentException('Cannot parse json from file handle'); + } $classMap = new ClassMap($cacheContentsJson['scanPaths'], $cacheContentsJson['basePath']); diff --git a/tests/ClassScanner/ClassMapTest.php b/tests/ClassScanner/ClassMapTest.php new file mode 100644 index 0000000..2dc9d81 --- /dev/null +++ b/tests/ClassScanner/ClassMapTest.php @@ -0,0 +1,82 @@ +yieldAttributes(FakeAllAttributes::class)]; + $spec = new ClassSpecification(FakeAllAttributes::class, FakeAllAttributes::FILE, $attributes); + + $map = new ClassMap([], '/'); + $map->addClass($spec); + + $this->assertContains(FakeAllAttributes::class, $map->getClasses()); + $this->assertContains(FakeAllAttributes::FILE, $map->getFiles()); + $this->assertNotNull($map->getClassSpecificationFor(FakeAllAttributes::class)); + $this->assertSame($spec, $map->getClassSpecificationFor(FakeAllAttributes::class)); + $this->assertCount(\count($attributes), $map->getAttributeSpecifications()); + } + + public function testAttributeClass(): void + { + $reflector = new Reflector(); + $spec1 = new ClassSpecification( + FakeAllAttributes::class, + FakeAllAttributes::FILE, + [...$reflector->yieldAttributes(FakeAllAttributes::class)] + ); + $spec2 = new ClassSpecification( + FakeWorkerAttribute::class, + FakeWorkerAttribute::FILE, + [...$reflector->yieldAttributes(FakeWorkerAttribute::class)] + ); + + $map = new ClassMap([], '/'); + $map->addClass($spec1); + $map->addClass($spec2); + + $this->assertFalse($map->isAttributeClassFile(FakeAllAttributes::FILE)); + $this->assertTrue($map->isAttributeClassFile(FakeWorkerAttribute::FILE)); + } + + public function testReconstituteFromFileHandle(): void + { + $reflector = new Reflector(); + $spec1 = new ClassSpecification( + FakeAllAttributes::class, + FakeAllAttributes::FILE, + [...$reflector->yieldAttributes(FakeAllAttributes::class)] + ); + $spec2 = new ClassSpecification( + FakeWorkerAttribute::class, + FakeWorkerAttribute::FILE, + [...$reflector->yieldAttributes(FakeWorkerAttribute::class)] + ); + + $stream = fopen('php://temp', 'w'); + + $map1 = new ClassMap([], '/'); + $map1->addClass($spec1); + $map1->addClass($spec2); + $map1->saveToFileHandle($stream); + + $map2 = ClassMap::fromFileHandle($stream); + + $this->assertContains(FakeAllAttributes::class, $map2->getClasses()); + $this->assertContains(FakeAllAttributes::FILE, $map2->getFiles()); + $this->assertContains(FakeWorkerAttribute::class, $map2->getClasses()); + $this->assertContains(FakeWorkerAttribute::FILE, $map2->getFiles()); + $this->assertCount(\count($spec1->getAttributes()), $map2->getClassSpecificationFor(FakeAllAttributes::class)->getAttributes()); + $this->assertCount(\count($spec2->getAttributes()), $map2->getClassSpecificationFor(FakeWorkerAttribute::class)->getAttributes()); + } +} \ No newline at end of file diff --git a/tests/Fake/FakeAllAttributes.php b/tests/Fake/FakeAllAttributes.php index fbb28f3..954da3c 100644 --- a/tests/Fake/FakeAllAttributes.php +++ b/tests/Fake/FakeAllAttributes.php @@ -10,6 +10,8 @@ class FakeAllAttributes #[FakeAttribute(\Attribute::TARGET_CLASS_CONSTANT)] public const CONSTANT = 1; + public const FILE = __FILE__; + public function __construct( #[FakeAttribute(\Attribute::TARGET_PARAMETER)] $parameter, diff --git a/tests/Fake/FakeWorkerAttribute.php b/tests/Fake/FakeWorkerAttribute.php index 91008b1..2b09742 100644 --- a/tests/Fake/FakeWorkerAttribute.php +++ b/tests/Fake/FakeWorkerAttribute.php @@ -13,6 +13,8 @@ #[BlueprintNamespace(__NAMESPACE__)] class FakeWorkerAttribute implements AttributeConfigInterface { + public const FILE = __FILE__; + private int $someSetting; public function __construct(int $someSetting = 1)