I have a section of code as follows
$decodedCredentials = json_decode($credentials, false);
if (!$this->isValidCredentials($decodedCredentials)) {
return null;
}
$credential = new Credential();
$credential->username = $decodedCredentials->username;
$credential->password = $decodedCredentials->password;
$credential->line = $decodedCredentials->line;
Further down in the class I have the following
private function isValidCredentials(mixed $credentials): bool
{
if (!is_object($credentials)) {
return false;
}
return is_object($credentials) &&
property_exists($credentials, 'username') &&
property_exists($credentials, 'password') &&
property_exists($credentials, 'line');
}
The complaint from PHPStan
------ --------------------------------------------
Line Authentication.php
------ --------------------------------------------
:146 Cannot access property $username on mixed.
:147 Cannot access property $password on mixed.
:148 Cannot access property $line on mixed.
------ --------------------------------------------
Perhaps in the normal civilian part of my brain this code makes sense that it should pass PHPStan but it doesn't
We are calling isValidCredentials
before trying to access those properties on the object and the check verifies all the things that PHPStan is complaining about.
I know that moving that check into the current function would solve the issue but that seems rather messy to me and I feel like this really should work and maybe I am missing something obvious. Bug perhaps?
It's complaining because there's a chance that such a property couldn't exist. I.e., mixed
could be an integer. You should be able to fix this simply by typehinting as \stdClass
instead.
Also note you're calling is_object()
twice, which you don't need to do. And since the new typehint already confirms this before even going in, you don't actually need either.
private function isValidCredentials(\stdClass $credentials): bool
{
return
property_exists($credentials, 'username') &&
property_exists($credentials, 'password') &&
property_exists($credentials, 'line');
}
See here.
Alternatively (and probably more desirable), I'd simply recommend serializing() your Credentials class. You're basically trying to reinvent the serialize/unserialize process which PHP already has built in. For example:
class Credentials
{
public string $username;
public string $password;
public string $line;
}
$creds = new Credentials();
$creds->user = 'foo';
$creds->pass = 'bar';
$creds->line = '123';
echo serialize($creds);
This yields a JSON-like string:
O:11:"Credentials":3:{s:4:"line";s:3:"123";s:4:"user";s:3:"foo";s:4:"pass";s:3:"bar";}
Which you can store/pass just like JSON. Then to get your object back, just:
$creds = unserialize($str);
is (!$creds instanceof Credentials) {
die('something went very wrong');
}
That said, the fact that you're putting password into a serialized/unserialized JSON string is somewhat of a code smell, and likely points to a bigger design problem.