0

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?

1
  • The type mixed is too unspecific for phpstan. You should use Reflection instead. Commented Jul 19, 2024 at 17:24

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Wouldnt this require an extra check in the original code to make sure that $credentials is an instanceof stdClass?
It does Parameter #1 $credentials of method isValidCredentials() expects stdClass, mixed given
Hrmmm, it's passing on the phpstan test site, what's different?
So this solution works but I end up with this in the original method !$decodedCredentials instanceof \stdClass || !$this->isValidCredentials($decodedCredentials) Still seems a bit messy to me but it gets me back to work at least. I wish PHPStan was smart enough to resolve this on its own
Updated w/ alternatives.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.