Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed compatibility with SoapClient #7140

Merged
merged 2 commits into from Dec 14, 2021
Merged

Fixed compatibility with SoapClient #7140

merged 2 commits into from Dec 14, 2021

Conversation

yethee
Copy link
Contributor

@yethee yethee commented Dec 12, 2021

SoapClient

Some methods can return null but currently these cases are not detected by psalm. As instance, __getLastResponse() returns null when connection was closed before receiving a response.

https://psalm.dev/r/e7364a92c6

If we remove the check for null, then can get an error of incompatibility types, at runtime:

TypeError: preg_match() expects parameter 2 to be string, null given

Affected methods:

SoapFault

Value of property detail can be mixed.

<SOAP-ENV:Fault>
  <faultcode>SOAP-ENV:Client</faultcode>
  <faultstring>Bad request</faultstring>
  <detail>
    <ns3:FaultResponse xmlns:ns3="http://direct.yandex.com/api/v5/general">
      <requestId>1010101010101010101</requestId>
      <errorCode>8000</errorCode>
      <errorDetail>Error message</errorDetail>
    </ns3:FaultResponse>
  </detail>
</SOAP-ENV:Fault>
print_r($fault->detail);

stdClass Object
(
    [FaultResponse] => stdClass Object
        (
            [requestId] => 1010101010101010101
            [errorCode] => 8000
            [errorDetail] => Error message
        )
)

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/e7364a92c6
<?php

function getRequestId(SoapClient $service): ?string
{
    $headers = $service->__getLastResponseHeaders();
    
    if ($headers !== null && preg_match('/^RequestId:\s?(.+)$/im', $headers, $m)) {
        return $m[1];
    }
    
    return null;
}
Psalm output (using commit 2a570fb):

ERROR: RedundantConditionGivenDocblockType - 7:9 - Docblock-defined type string can never contain null

INFO: PossiblyUndefinedIntArrayOffset - 8:16 - Possibly undefined array offset '1' is risky given expected type 'array-key'. Consider using isset beforehand.

@orklah
Copy link
Collaborator

orklah commented Dec 12, 2021

@veewee You recently submitted changes in soap stubs, do you mind reviewing this PR if you're familiar with the extension?

@@ -12266,7 +12266,7 @@
'SoapFault::getPrevious' => ['?Exception|?Throwable'],
'SoapFault::getTrace' => ['list<array<string,mixed>>'],
'SoapFault::getTraceAsString' => ['string'],
'SoapFault::SoapFault' => ['object', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'string', 'detail='=>'string', 'faultname='=>'string', 'headerfault='=>'string'],
'SoapFault::SoapFault' => ['object', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'string', 'detail='=>'mixed', 'faultname='=>'string', 'headerfault='=>'string'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the details is ok, but the other props also don't seem to match the signature of SoapFault::__construct()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the constructor signature according to the documentation. And also the property map has been updated.

Copy link
Contributor

@veewee veewee Dec 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orklah : Do the names of these parameters matter (e.g. for named params)?
Since It is actually:

#[Pure]
public function __construct(
        array|string|null $code,
        string $string, 
        ?string $actor = null,
        mixed $details = null,
        ?string $name = null,
        mixed $headerFault = null
    ) {

See:

(It is more like an internal exception though, but maybe you'dd be creating it for testing purpose)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Names do matter, for when Psalm checks calls with named arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the signatures for SoapFault::__construct() and SoapFault::SoapFault().

@veewee
Copy link
Contributor

veewee commented Dec 12, 2021

@orklah Seems correct and documented:
https://www.php.net/manual/en/class.soapclient.php

I can imageine that they have always worked like this.

These can return null in non-wsdl mode or when overriding construct and calling these functions withouth the WSDL being parsed:

  • __getTypes()
  • __getFunctions()
$soap = new SoapClient(null, [
    'location' => 'http://127.0.0.1',
    'uri' => 'http://127.0.0.1',
]);

var_dump($soap->__getFunctions());
// > null
var_dump($soap->__getTypes());
// > null

These will return null if trace is disabled (or if the connection got a timeout - you can simulate this with the ini setting default_socket_timeout)

  • __getLastRequest()
  • __getLastRequestHeaders()
  • __getLastResponse()
  • __getLastResponseHeaders()
$soap = new SoapClient('http://www.dneonline.com/calculator.asmx?wsdl', [
    'trace' => 0,
]);
$result = $soap->Add(['intA' => 1, 'intB' => 2]);

var_dump($soap->__getLastRequest());
// > null
var_dump($soap->__getLastRequestHeaders());
// > null
var_dump($soap->__getLastResponse());
// > null
var_dump($soap->__getLastResponse());
// > null

The SoapFault indeed takes a ?mixed details prop.
It looks like the other properties are not fully in sync though.
See : https://www.php.net/manual/en/soapfault.construct.php

@weirdan weirdan added the release:fix The PR will be included in 'Fixes' section of the release notes label Dec 12, 2021
@weirdan
Copy link
Collaborator

weirdan commented Dec 12, 2021

test-with-real-projects/PHPUnit failure seems legit. You may want to PR a null check to PHPUnit, but for the purposes of our CI suite we will just baseline that error when it's merged.

@orklah
Copy link
Collaborator

orklah commented Dec 14, 2021

Thanks!

@weirdan can you update the baseline for phpunit?

@yethee yethee deleted the soap branch December 15, 2021 06:52
@weirdan
Copy link
Collaborator

weirdan commented Dec 15, 2021

@weirdan can you update the baseline for phpunit?

@orklah done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:fix The PR will be included in 'Fixes' section of the release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants