custom shipping endpoint errors after server migration and update to PHP 7.0.29 from PHP 5.3

GeoffreyGeoffrey Member
in Help
Hi I migrated our site to a new server last night and changed the DNS. I had tested everything in the new server environment prior to changing DNS, including our custom shipping endpoint script. All seemed to be working correctly before the DNS change. I had lowered the TTL to 300 at the time of change, so everything should have propagated fully.

Based on the FoxyCart error logs, it appears that immediately after the DNS change (starting with error at 2018-05-02 02:30:33) our custom shipping script began to throw errors for every query, and that persisted throughout today. In the previous server environment running PHP 5.3, the custom shipping script did not ever throw programmatic errors. The new server environment is running PHP 7.0.29, though I have not yet found any indication that the PHP version is causing the errors -- via deprecated or removed features. The errors have all been described as follows:

Warning: Invalid argument supplied for foreach() in shipping-endpoint.php on line 61 (and/or line 242, etc.)

There have also been a handful of timeout errors and errors without description. In any case, when I noticed this around noon, I commented out the specific foreach() blocks of code that were throwing the errors and the shipping endpoint proceeded to work as it should (but without a special product calculation tied to the blocks I commented out). I did a bit of further experimentation debugging this, primarily testing from the shipping rate query from the cart, and eventually it seemed like those blocks of code were no longer throwing the errors even though I didn't actually change anything about them. I had wondered if perhaps there was a DNS propagation issue here, but couldn't be sure. When the rate queries seemed to be working in the cart I thought it was resolved, but I failed to notice that the rate queries selected in the cart were not being passed to the checkout, and that the same foreach() error was still occurring when the rates were queried again at checkout. So I have reverted back to having those specific foreach() blocks commented out until I can get this fully resolved.

The thing that baffles me about all this is that there doesn't actually appear to be anything wrong with the offending blocks of code. The main troublemakers were these two blocks:


foreach ($cart_details['_embedded']['fx:items'] as $item) {
foreach ($item['_embedded']['fx:item_options'] as $option) {
if ($option['name'] == "class") {
$hasHeavy = true;
$heavyWeight += ($item['weight'] * $item['quantity']);
}
}
}



foreach ($h_fedex_rates as $key => $rate) {
if ($rate->serviceCode == "fedex_home_delivery") {
$h_fedex_home_delivery = $rate->shipmentCost + $rate->otherCost;
}
if ($rate->serviceCode == "fedex_2day") {
$h_fedex_2day = $rate->shipmentCost + $rate->otherCost;
}
if ($rate->serviceCode == "fedex_standard_overnight") {
$h_fedex_standard_overnight = $rate->shipmentCost + $rate->otherCost;
}
if ($rate->serviceCode == "fedex_priority_overnight") {
$h_fedex_priority_overnight = $rate->shipmentCost + $rate->otherCost;
}
}


Presumably the argument supplied in each of these cases is considered invalid because they are supposed to be arrays, and I have confirmed that the supplied arguments definitely are arrays, because there are other foreach() loops in the script that use the exact same arguments without causing any problem at all (particularly the "$cart_details['_embedded']['fx:items'] as $item" argument). So I'm really stumped on this one. Why would the exact same supplied argument work without issue in one foreach() loop and throw and error in another? As I said, the script as a whole was airtight and working perfectly before I switched the DNS to the new server environment. The script itself remained unchanged in that transition. There have been no other issues with the site or cart/checkout integration. Just this problem with the custom shipping endpoint.

Any ideas on what is going wrong here? I will whisper you a link to our dev environment where I still have the offending pieces of code active.
Comments
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    That is really weird! Thanks as always for your detailed overview.

    Would it be possible for you to whisper us the contents of your custom shipping endpoint script? We'll see if we can replicate those same errors on our side.
  • GeoffreyGeoffrey Member
    Any insights you have on this one yet? Or additional questions for me?
  • fc_adamfc_adam FoxyCart Team
    edited May 4
    @Geoffrey,

    Thanks for sending over your shipping endpoint. I've tested with the first portion of your code - up to the point that you include the logic for pulling rates from ShipStation, and I don't have any errors on my own PHP 7 server, it returns rates just fine.

    Like you, I'm really confused why that would be erroring. Especially when like you said, there are foreach loops above the ones that are erroring that share the same set up.

    I'm also not seeing anything obvious with changes for PHP 7 that could account for that... it's a really weird one!

    Perhaps you could try capturing the payload we send to your endpoint in a file, and then running the endpoint locally. You can then replicate the error happening, and output information right before the error point to see why it might be doing what it is.

    Sorry we couldn't be of more help here - let us know when you do find out what is causing it though, I'm really intrigued on this one.
  • fc_adamfc_adam FoxyCart Team
    edited May 4
    @Geoffrey,

    Actually - a quick follow-up, I think I know why the first foreach block is failing. It's not the $items, but the $option line that's failing. If a product doesn't have any custom product attributes - then that fx:item_options object won't be present. If you update that foreach to check if it is present before attempting to iterate over it, that should correct that error. That might also explain why you sometimes saw it error and sometimes didn't.

    We'll look at always returning at least an empty array there so there is a bit more consistency to the response.

    The other foreach loop - I'd recommend saving the output of the getRates() call and ensure it's returning what you expect.
  • GeoffreyGeoffrey Member
    OK. Thanks Adam! I'll give your suggestion on the options loop a try, and all experiment with outputting the payload to see if I can glean anything there. I'm still confused as to why this all worked without problem in the previous server environment though. Could it be that foreach() loop validation got more strict in the newer PHP versions or something?
  • GeoffreyGeoffrey Member
    @fc_adam, I ran the diagnostic test you recommended -- saving the output of the getRates() call to the text file when the query runs. Doing this made clear that in many conditions the getRates() call in error was being passed an argument that had a null value. Bingo! I was able to adjust the script conditions so that the getRates() call in question will only fire when being provided with a non-null value, and that seems to have fixed the problem. The script was previously making extra queries to ShipStation that were not necessary for the majority of cart items, so preventing those extra queries from happening when the returned values weren't being used cleaned everything up.

    Why this was working without error in the previous environment remains a mystery to me, though my suspicion is that perhaps PHP 5.3 handled null values being passed in the argument of a foreach() loop by ignoring and passing over the loop, whereas versions since then started throwing an error when this happens. I haven't seen anything specifically in the PHP version docs to suggest that change, but it's my best guess about why this error appeared only after the update.

    Anyway, thank you so much for your help and suggestions.
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    I'm glad you were able to get to the bottom of everything! Your theory sounds the most plausible to me too - that PHP 7 is just a little stricter in that regard.
Sign In or Register to comment.