php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78749 Wrong error message when maxOccurs is exceeded
Submitted: 2019-10-24 18:29 UTC Modified: 2019-11-28 18:13 UTC
From: tony at marston-home dot demon dot co dot uk Assigned:
Status: Open Package: SOAP related
PHP Version: 7.3.11 OS: Windows 10
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: tony at marston-home dot demon dot co dot uk
New email:
PHP Version: OS:

 

 [2019-10-24 18:29 UTC] tony at marston-home dot demon dot co dot uk
Description:
------------
In my WSDL I have a complexType entry which has a maxOccurs set to 1. If I exceed this limit the error message says "SOAP-ERROR: Encoding object has no 'first_name' property" where 'first_name' is the first required field in the enclosed structure. 

Test script:
---------------
WSDL:
<xsd:complexType name="ArrayOfContactInfo">
<xsd:sequence>
  <xsd:element name="contact" type="tns:contact" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="contact">
<xsd:all>
  <xsd:element name="title" type="xsd:string" minOccurs="0" maxOccurs="1"/>
  <xsd:element name="first_name" type="xsd:string" minOccurs="1" maxOccurs="1"/>
  <xsd:element name="middle_name" type="xsd:string" minOccurs="0" maxOccurs="1"/>
  <xsd:element name="last_name" type="xsd:string" minOccurs="1" maxOccurs="1"/>
</xsd:all>
</xsd:complexType>

Expected result:
----------------
I expect a more meaningful error message such as "More than N occurrences exceeded"

Actual result:
--------------
The error message is totally misleading.

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-10-24 19:43 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2019-10-24 19:43 UTC] requinix@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves.

A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external
resources such as databases, etc. If the script requires a
database to demonstrate the issue, please make sure it creates
all necessary tables, stored procedures etc.

Please avoid embedding huge scripts into the report.

I may be misreading the source, but it looks like you're triggering a code path that only occurs when there is no value available.
 [2019-10-25 17:11 UTC] tony at marston-home dot demon dot co dot uk
-Status: Feedback +Status: Open
 [2019-10-25 17:11 UTC] tony at marston-home dot demon dot co dot uk
I have created a zip file containing the two files necessary to reproduce this fault as it simply cannot be done in 20 lines of code or less. The WSDL file alone requires over 80 lines. This zip file is available at:
https://www.tonymarston.net/php-mysql/gmx_soap_bug_report.zip

The 'contact' element in the WSDL file has a 'maxOccurs' attribute which I am testing by sending 3 occurrences. If I set 'maxOccurs' to 1 it fails with "SOAP-ERROR: Encoding object has no 'first_name' property" even though the array does contain a value.

If I set 'maxOccurs' to 2 it does not fail (which it should as I have violated the maximum) and it also sends all 3 occurrences to the server.
 [2019-11-04 21:31 UTC] camporter1 at gmail dot com
It's possible that I'm not understanding the issue properly, but it seems like the issue here is that 'first_name' is trying to be accessed on the array of 'contact', which won't map to the type.

Replacing ArrayOfContactInfo with:

<xsd:complexType name="ArrayOfContactInfo">
  <complexContent>
    <restriction base="SOAP-ENC:Array">
      <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:contact[]"/>
    </restriction>
  </complexContent>
</xsd:complexType>

and removing the extra 'contact' array:

    'contacts' => [
        '0' => [
            'title' => 'Mr',
            'first_name' => 'Joe',
            'middle_name' => 'Xavier',
            'last_name' => 'Soap',
            'contact_role' => 'Account Manager',
            'telephone' => '075 40008000'
        ],
        '1' => [
            'title' => 'Mrs',
            'first_name' => 'Jane',
            'last_name' => 'Doe',
            'contact_role' => 'Assistant Account Manager',
            'telephone' => '075 40008001'
        ],
        '2' => [
            'title' => 'Miss',
            'first_name' => 'Dee',
            'last_name' => 'Meanour',
            'contact_role' => 'Deputy Assistant Account Manager',
            'telephone' => '075 40008003',
        ],
    ],

seems like it might be more in line with the expected behavior?
 [2019-11-05 09:45 UTC] tony at marston-home dot demon dot co dot uk
That structure is perfectly valid according to the WSDL definition, and it validates using third party tools such as OxygenXML and SOAPUI.

The XML I supplied contains three occurrences of <contact> and the SOAP Server ->handle() method deals properly with these when the WSDL specifies maxOccurs of 3 or more. 

If I set maxOccurs to 1 the error message is entirely wrong.

If I set maxOccurs to 2 there is no error message complaining that I have exceeded maxOccurs.

It is your validation of the structure which is wrong and not the structure itself.
 [2019-11-05 17:52 UTC] requinix@php.net
-Summary: Wrong error message when maxOccurs is exceeded +Summary: No comprehensive documentation on how SoapClient serializes data per the WSDL -Status: Open +Status: Verified -Type: Bug +Type: Documentation Problem
 [2019-11-05 17:52 UTC] requinix@php.net
Here's what is happening:

If maxOccurs is 1 then the PHP value should not be a list. Because there's only possibly one. It doesn't make sense to have a list when there can only be a single entity/value in it. Like having the title be an array("Title") is weird.

If maxOccurs is unbounded or >1, PHP does not validate against the count. It just doesn't. If the max is 2 and you give 3 then it will put in all 3 (which you can verify through __getLastRequest).

So there's two issues here:
1. That behavior is undocumented. I think it's the most important aspect of this report, so I'm repurposing this as a doc bug.
2. The missing maxOccurs validation. I don't see this as high priority as it doesn't seem that SoapClient has any particular intention to perform validation beyond what is required to serialize the data to XML; maxOccurs when >1 isn't being validated, minOccurs when >1 isn't being validated, and there are probably other rules not being validated either.
 [2019-11-05 18:45 UTC] tony at marston-home dot demon dot co dot uk
I disagree completely with your assessment. 

In my WSDL the element <contacts> can occur 0 or 1 times. It is a container for any number of <contact> elements, and each <contact> contains its own group of elements which can occur 0 or 1 times each.

The idea that the value of maxOccurs need not be validated is nonsense. The official specification at http://www.w3.org/TR/xmlschema-0/#OccurrenceConstraints contains the sentence "The maximum number of times an element may appear is determined by the value of a maxOccurs attribute in its declaration." To any reasonable person this would mean that if the WSDL provides a value for maxOccurs then the XML document should checked to ensure that this limit is not violated.
 [2019-11-05 19:45 UTC] requinix@php.net
> It is a container for any number of <contact> elements,
Not according to the WSDLs. The one in your report very clearly says maxOccurs=1 and the one in your download says maxOccurs=3.

Currently PHP only really cares if maxOccurs>1 or not. If it is then the value being serialized is actually a list (ie, array) of values, who each are to be serialized individually. If you have maxOccurs=1 for an object and pass a list then PHP will complain because the list doesn't have attributes.

> The idea that the value of maxOccurs need not be validated is nonsense.
In the world of specifications there are two words with two different meanings: "need" and "should".

The data does not *need* to be validated because the developer is supposed to know what they're doing, and if they get it wrong then it's not like PHP can automatically recover. The data *should* be validated because that's A Good Thing To Do.

PHP does not validate right now. *Should* it? Yes, so the developer can be presented with nicer error messages and, as a more egregious example, not attempt to send invalid data to a service that the client could have known was invalid. But does it *need* to? No, because when used correctly ext/soap is perfectly capable of sending data in its current state.

Let me repeat that last sentence to emphasize why I'm making this a doc bug: ext/soap is perfectly capable of sending data in its current state. When you passed a list of objects where the WSDL specified one object, the mistake was on you. PHP could have given you a better error message that more directly indicated the source of the problem, absolutely*, but that doesn't change how PHP was not able to recover and somehow adapt the data to suit. You, the developer, would have to go in and fix your code.

Now I really don't want this argument to turn into both of us repeating ourselves until the other person gives up, so here's what you should do: since ext/soap does not currently attempt to validate the data against the WSDL in any way, or even have the functionality to do so AFAIK, you should *request* that validation be *added* so that developers like you and me could make use of it and benefit from the failure messages it may produce. I would suggest that SoapClient gets a "validate" (or perhaps "__validate") method that takes a function name and parameters and validates, and/or that __soapCall() gets an option to opt-into validating before sending.


* In case this is a source of confusion, I'm saying PHP could give a better message *in principle*. The cause of your problem is PHP being given a list where it needs an object, but it isn't discovering anything wrong until it reaches the point where it tries to serialize a "first_name" property when it was working on an array with keys (0,1,2). With my quick reading through the source, I don't believe that it *currently* can accurately report some sort of "expecting object, received list" message at that point in the process - all it knows there is that the array doesn't have the required key.
 [2019-11-06 09:45 UTC] tony at marston-home dot demon dot co dot uk
It is clear that you do not understand the structure that I am using.

<contacts> is a container that can occur 0 or 1 times. It can contain a number of <contact> elements (arrayOfContactInfo) which themselves are containers for other elements.

It is the value of maxOccurs for the <contact> element which is being problematic. Although my data creates 3 occurrences I was testing to see what happens when I set maxOccurs to a smaller value.

If I set it to 2 it does not complain that I have exceeded the maxOccurs value.

If I set it to 1 it does complain, but the error message is totally wrong.

The official specification at http://www.w3.org/TR/xmlschema-0/#OccurrenceConstraints contains the sentence "The maximum number of times an element may appear is determined by the value of a maxOccurs attribute in its declaration." Your code is clearly not validating the maxOccurs value, therefore your code is not following the specification.

You say that the data does not *need* to be validated because the developer is supposed to know what they're doing, but you are missing the whole point of WSDL/XSD/DTD validation which verifies that the XML document which it is given conforms to the specified structure in order to trap any mistakes made by the developer. It is supposed to guarantee that the XML document which is received at the other end is exactly as expected. It does this by looking at the minOccurs and maxOccurs value for each element. An element is optional if minOccurs is zero but required if it is greater than zero. The specification also states that no element should occur more times than as stated in the maxOccurs value. Your code is NOT doing this, therefore your code does NOT conform to the specification.
 [2019-11-06 10:23 UTC] tony at marston-home dot demon dot co dot uk
You may not be familiar with my use of a <contacts> element which is a container for 1 or more <contact> elements, but if you look at the example XML document in http://www.w3.org/TR/xmlschema-0/#PO you will see that it has an <items> element which is a container for one or more <item> elements.

The <items> element has an implied maxOccurs of 1, and in my test data my <contacts> element has an explicit maxOccurs of 1. The <item> element has a maxOccurs of "unbounded", and in my test data my <contact> element has a specified limit, and I am testing to see what happens if the limit is exceeded when I supply 3 occurrences. If the maxOccurs value is 1 the error message is wrong. If the maxOccurs value is 2 there is no error message.

You said in your reply that "Currently PHP only really cares if maxOccurs>1 or not." This is clearly wrong. The WSDL specification clearly states that the maxOccurs value defines "The maximum number of times an element may appear" and as "1" is a valid value the XML document should be validated to ensure that this limit is not violated. This is not an optional piece of validation, it is REQUIRED.
 [2019-11-06 10:30 UTC] tony at marston-home dot demon dot co dot uk
-Summary: No comprehensive documentation on how SoapClient serializes data per the WSDL +Summary: Wrong error message when maxOccurs is exceeded -Type: Documentation Problem +Type: Bug
 [2019-11-06 10:30 UTC] tony at marston-home dot demon dot co dot uk
This is NOT a documentation error as your implementation clearly does NOT follow the WSDL specification.
 [2019-11-06 11:42 UTC] requinix@php.net
-Status: Verified +Status: Open
 [2019-11-06 11:42 UTC] requinix@php.net
So much for avoiding "repeating ourselves until the other person gives up".
 [2019-11-06 12:00 UTC] tony at marston-home dot demon dot co dot uk
All the while you try to squirm out of the fact that your code is not validating an XML document against the WSDL definition, specifically the value for maxOccurrs on any element AS DOCUMENTED IN THE W3C SPECIFICATION, then I will carry on complaining. Your idea that "Currently PHP only really cares if maxOccurs>1 or not" is clearly wrong as the value "1" should be validated as well.
 [2019-11-28 18:13 UTC] tony at marston-home dot demon dot co dot uk
Your statement that "The data does not *need* to be validated because the developer is supposed to know what they're doing" is not accurate. All input validation, whether it be to validate user input or developer input, is supposed to be carried out to ensure that the person providing the input has not made a mistake.

In the case of WSDL validation this is supposed to check the following:
a) That the structure of the XML request matches the structure in the WSDL.
b) That elements with minOccurs=1 actually exist.
c) That elements do not occur more than maxOccurs times. 

This means that elements in the XML structure which do not appear in the WSDL structure should cause an error, and that any violation of either minOccurs or maxOccurs should cause an error. This means that if I specify maxOccurs=2 in the WSDL but accidentally put 3 occurrences in the XML then this should cause an error. It's not rocket science.

It is also possible to have a <contacts> element with minOccurs=0 to be a container for a <contact> element with minOccurs=1. This signifies that the <contacts> element is optional, but if it is supplied then it must contain at least 1 <contact> element.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 16:01:28 2024 UTC