Skip to content
Open
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
6 changes: 3 additions & 3 deletions src/Utils/ObjectHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static function strictGet(string $class, string $name): void
$rc = new \ReflectionClass($class);
$hint = self::getSuggestion(array_merge(
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()),
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'),
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:(?!@)[^\s$*]+[\s*]+)*+\$(\w+)~m'),
), $name);
throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
}
Expand All @@ -45,7 +45,7 @@ public static function strictSet(string $class, string $name): void
$rc = new \ReflectionClass($class);
$hint = self::getSuggestion(array_merge(
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()),
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'),
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:(?!@)[^\s$*]+[\s*]+)*+\$(\w+)~m'),
), $name);
throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
}
Expand Down Expand Up @@ -131,7 +131,7 @@ public static function getMagicProperties(string $class): array

$rc = new \ReflectionClass($class);
preg_match_all(
'~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx',
'~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ (?:(?!@)[^\s$*]+[\s*]+)++ \$ (\w+) ()~mx',
(string) $rc->getDocComment(),
$matches,
PREG_SET_ORDER,
Expand Down
53 changes: 53 additions & 0 deletions tests/Utils/ObjectHelpers.getMagicProperites().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ require __DIR__ . '/../bootstrap.php';
* @property int $getter2 by ref
* @property int $setter
* @property A\B $both with typehint
* @property array<string, string> $generic with space in type
* @property array<int, array<string, mixed>> $nested
* @property array{a: int, b?: string} $shape
* @property array{
* multi: string,
* line: int,
* } $multiline
* @property array<string,
* int> $multilineGeneric
* @property int $missing
* @property int $Upper
* @property int $cAse
Expand All @@ -27,6 +36,9 @@ require __DIR__ . '/../bootstrap.php';
* @property-x int $invalid1
* @property $invalid2
* abc @property int $invalid3
* @property array{
* unterminated: string,
* @property-read int $bridged
*/
class TestClass
{
Expand Down Expand Up @@ -55,6 +67,41 @@ class TestClass
}


public function getGeneric()
{
}


public function getNested()
{
}


public function getShape()
{
}


public function getMultiline()
{
}


public function getMultilineGeneric()
{
}


public function getBridged()
{
}


public function setBridged()
{
}


public function getUpper()
{
}
Expand Down Expand Up @@ -118,10 +165,16 @@ Assert::same([
'getter2' => 0b0101,
'setter' => 0b1000,
'both' => 0b1011,
'generic' => 0b0011,
'nested' => 0b0011,
'shape' => 0b0011,
'multiline' => 0b0011,
'multilineGeneric' => 0b0011,
'Upper' => 0b0011,
'protected' => 0b0011,
'read' => 0b0011,
'write' => 0b1000,
'bridged' => 0b0011,
], ObjectHelpers::getMagicProperties('TestClass'));


Expand Down
56 changes: 56 additions & 0 deletions tests/Utils/ObjectHelpers.strictness.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,59 @@ Assert::exception(
MemberAccessException::class,
'Cannot read an undeclared property TestClass::$protectedX.',
);


// hints from @property annotations with whitespace inside the type
/**
* @property array<string, string> $generic
* @property array{
* foo: string,
* } $multiline
* @property-read array<int, string> $readOnly
* @property-write array<int, string> $writeOnly
*/
class AnnotatedClass
{
}

Assert::exception(
fn() => ObjectHelpers::strictGet('AnnotatedClass', 'gneric'),
MemberAccessException::class,
'Cannot read an undeclared property AnnotatedClass::$gneric, did you mean $generic?',
);

Assert::exception(
fn() => ObjectHelpers::strictSet('AnnotatedClass', 'gneric'),
MemberAccessException::class,
'Cannot write to an undeclared property AnnotatedClass::$gneric, did you mean $generic?',
);

Assert::exception(
fn() => ObjectHelpers::strictGet('AnnotatedClass', 'multilin'),
MemberAccessException::class,
'Cannot read an undeclared property AnnotatedClass::$multilin, did you mean $multiline?',
);

Assert::exception(
fn() => ObjectHelpers::strictGet('AnnotatedClass', 'readOnli'),
MemberAccessException::class,
'Cannot read an undeclared property AnnotatedClass::$readOnli, did you mean $readOnly?',
);

Assert::exception(
fn() => ObjectHelpers::strictSet('AnnotatedClass', 'writeOnli'),
MemberAccessException::class,
'Cannot write to an undeclared property AnnotatedClass::$writeOnli, did you mean $writeOnly?',
);

Assert::exception(
fn() => ObjectHelpers::strictSet('AnnotatedClass', 'readOnli'),
MemberAccessException::class,
'Cannot write to an undeclared property AnnotatedClass::$readOnli.',
);

Assert::exception(
fn() => ObjectHelpers::strictGet('AnnotatedClass', 'writeOnli'),
MemberAccessException::class,
'Cannot read an undeclared property AnnotatedClass::$writeOnli.',
);
Loading