<?php
// Class we'll load properties-of into a property of another class
class DBLoginInfo
{
/**
* @param string $host
* @return void
*/
public function __construct(
public string $host
)
{}
}
// Load in properties one way
class DBConnectInfoAsArray
{
/**
* @param properties-of<DBLoginInfo> $read
*/
public function __construct(
public array $read
)
{}
}
// Load in properties another way
class DBConnectInfoAsArray2
{
/** @param properties-of<DBLoginInfo> */
public array $read;
public function __construct(array $read)
{
$this->read = $read;
}
}
/**
* Fail: Try to access host from first parent class
* @param properties-of<DBConnectInfoAsArray> $info
* @return void
*/
function blah(array $info)
{
$read = $info['read'];
echo $read['host'];
}
/**
* Fail: try to access host from second parent class
* @param properties-of<DBConnectInfoAsArray2> $info
* @return void
*/
function blah2(array $info)
{
$read = $info['read'];
// This says it's just a generic array... it doesn't seem to understand the docblock type
echo $read['host'];
}
/**
* Pass if you explicitly assign @var
* This one works by using @var to tell it what it should be, but it seems
* like psalm should have been able to tell what it is without using an @var
* @param properties-of<DBConnectInfoAsArray2> $info
* @return void
*/
function blah3(array $info)
{
/** @var properties-of<DBLoginInfo> */
$read = $info['read'];
echo $read['host'];
}
/**
* Pass if you don't use properties-of<DBConnectInfoAsArray>, and load the class itself.
* @return void
*/
function blah4(DBConnectInfoAsArray $info)
{
$read = $info->read;
echo $read['host'];
}
https://psalm.dev/r/e76bd1defd
```php
$read
*/
public function __construct(
public array $read
)
{}
}
// Load in properties another way
class DBConnectInfoAsArray2
{
/** @param properties-of */
public array $read;
public function __construct(array $read)
{
$this->read = $read;
}
}
/**
* Fail: Try to access host from first parent class
* @param properties-of $info
* @return void
*/
function blah(array $info)
{
$read = $info['read'];
echo $read['host'];
}
/**
* Fail: try to access host from second parent class
* @param properties-of $info
* @return void
*/
function blah2(array $info)
{
$read = $info['read'];
// This says it's just a generic array... it doesn't seem to understand the docblock type
echo $read['host'];
}
/**
* Pass if you explicitly assign @var
* This one works by using @var to tell it what it should be, but it seems
* like psalm should have been able to tell what it is without using an @var
* @param properties-of $info
* @return void
*/
function blah3(array $info)
{
/** @var properties-of */
$read = $info['read'];
echo $read['host'];
}
/**
* Pass if you don't use properties-of, and load the class itself.
* @return void
*/
function blah4(DBConnectInfoAsArray $info)
{
$read = $info->read;
echo $read['host'];
}
```
```
Psalm output (using commit 16b24bd):
ERROR: InvalidArrayAccess - 47:10 - Cannot access array value on non-array variable $read of type properties-of
INFO: MixedArgument - 47:10 - Argument 1 of echo cannot be mixed, expecting string
INFO: PossiblyUndefinedStringArrayOffset - 59:10 - Possibly undefined array offset ''host'' is risky given expected type 'array-key'. Consider using isset beforehand.
INFO: MixedArgument - 59:10 - Argument 1 of echo cannot be mixed, expecting string
```
psalm does not recognize the
properties-of<Class>
type if it's assigned to a property of a class, and then it gets fetched using properties-ofSee: https://psalm.dev/r/e76bd1defd