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

fix: corrects JSON encoding for various data types #118

Merged
merged 6 commits into from Oct 20, 2021

Conversation

Justintime50
Copy link
Member

@Justintime50 Justintime50 commented Oct 18, 2021

Summary

This is an attempt at fixing JSON encoding for various data types. I tried to keep this as least destructive as possible (we could/should be using the JsonSerializable interface; however, I didn't want to introduce another larger rewrite if we could for now correct this with a simple pass.

This is accomplished now by:

  1. Not using the array_filter which had some interesting side effects
  2. Making any element that we previously would set as an empty array if no data was present to null instead
  3. Sending all data as strings (as this was the previous behavior and what almost every field of our API expects)

Tests

  • null tests
  • empty string tests
  • string tests
  • array tests (empty and filled)
  • float tests
  • integer tests
  • boolean tests

I added a few tests to check encoding and updated others. Looking at the output of cassettes, I'm much happier with this approach than the previous as it's behaving much closer to what I'd expect.

Docs about type comparisons: https://www.php.net/manual/en/types.comparisons.php

Closes #117

@fiendish
Copy link
Contributor

fiendish commented Oct 19, 2021

Not using the array_filter which had some interesting side effects

Can you clarify what side effects?

Making any element that we previously would set as an empty array if no data was present to null instead

if (!$data) {
$data = array();
}
still erroneously converts False into an array. I strongly suspect that it wants to be checking is_null instead of !.

Sending all integers as strings

I'm not clear on the purpose of checking for integer vs sending everything as strings, which was the previous inherent behavior.

lib/EasyPost/Requestor.php Outdated Show resolved Hide resolved
lib/EasyPost/Requestor.php Show resolved Hide resolved
@fiendish
Copy link
Contributor

fiendish commented Oct 19, 2021

We are sending null instead of just dropping keys/objects if the user explicitly passes null. This is probably an acceptable behavior; however, we may want to simply drop them altogether?

Because you've removed the array filter? I think it wouldn't hurt to throw a null value check inside the is_array foreach before recursing.

Like, if we're only looking for strval conversion here and not yet applying more explicit field type checks, maybe ...

@@ -55,11 +55,9 @@
      */
     private static function _encodeObjects($data)
     {
-        if (!$data) {
-            $data = array();
-        }
-
-        if ($data instanceof EasypostResource) {
+        if (is_null($data)) {
+            return array();  // if the root is null, return an empty set of params at least
+        } elseif ($data instanceof EasypostResource) {
             return array("id" => self::utf8($data->id));
         } elseif ($data === true) {
             return 'true';
@@ -68,12 +66,14 @@
         } elseif (is_array($data)) {
             $resource = array();
             foreach ($data as $k => $v) {
-                $resource[$k] = self::_encodeObjects($v);
-            }
-
-            return array_filter($resource);
+                if (!is_null($v)) {
+                    $resource[$k] = self::_encodeObjects($v);
+                }
+            }
+
+            return $resource;
         } else {
-            return self::utf8($data);
+            return self::utf8(strval($data));
         }
     }

Though I didn't test it

@Justintime50
Copy link
Member Author

Not using the array_filter which had some interesting side effects

Can you clarify what side effects?

Making any element that we previously would set as an empty array if no data was present to null instead

if (!$data) {
$data = array();
}

still erroneously converts False into an array. I strongly suspect that it wants to be checking is_null instead of !.

Sending all integers as strings

I'm not clear on the purpose of checking for integer vs sending everything as strings, which was the previous inherent behavior.

I added the array_filter, it wasn't initially there and shouldn't have been. It's meant to be used with a function callback written by the user but we aren't even using it that way.

I think with your other suggested changes here we can easily fix the False issue.

@Justintime50 Justintime50 marked this pull request as ready for review October 19, 2021 16:35
Copy link
Contributor

@fiendish fiendish left a comment

Choose a reason for hiding this comment

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

√ for passing tests, except for one weird thing

lib/EasyPost/Requestor.php Outdated Show resolved Hide resolved
lib/EasyPost/Requestor.php Outdated Show resolved Hide resolved
lib/EasyPost/Requestor.php Outdated Show resolved Hide resolved
@fiendish
Copy link
Contributor

fiendish commented Oct 19, 2021

It seems at this point like it may be cleaner to have all the early checks in _encodeObjects return null instead of array(), keep using some kind of filter on the array to remove them, and then have the original caller set array() if the end response there is null.

@fiendish
Copy link
Contributor

What about

@@ -55,25 +55,26 @@
      */
     private static function _encodeObjects($data)
     {
-        if (!$data) {
-            $data = array();
-        }
-
-        if ($data instanceof EasypostResource) {
+        if (is_null($data) or ($data === "")) {
+            return null;
+        } elseif ($data instanceof EasypostResource) {
             return array("id" => self::utf8($data->id));
         } elseif ($data === true) {
             return 'true';
         } elseif ($data === false) {
             return 'false';
         } elseif (is_array($data)) {
+            if (empty($data)) {
+                return null;
+            }
+
             $resource = array();
             foreach ($data as $k => $v) {
                 $resource[$k] = self::_encodeObjects($v);
             }
-
-            return array_filter($resource);
+            return array_filter($resource, function($v) {return !is_null($v);});
         } else {
-            return self::utf8($data);
+            return self::utf8(strval($data));
         }
     }
 
@@ -152,6 +153,9 @@
 
         $absUrl = $this->apiUrl($url);
         $params = self::_encodeObjects($params);
+        if (is_null($params)) {
+            $params = array();
+        }
 
         $langVersion = phpversion();
         $uname = php_uname();

@fiendish fiendish self-requested a review October 19, 2021 20:30
fiendish
fiendish previously approved these changes Oct 19, 2021
Copy link
Contributor

@fiendish fiendish left a comment

Choose a reason for hiding this comment

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

👍

lib/EasyPost/Requestor.php Outdated Show resolved Hide resolved
lib/EasyPost/Requestor.php Outdated Show resolved Hide resolved
jontsai
jontsai previously approved these changes Oct 20, 2021
@Justintime50 Justintime50 merged commit 14d3220 into master Oct 20, 2021
@Justintime50 Justintime50 deleted the fix_json_encoding branch October 20, 2021 19:39
@klaxian
Copy link

klaxian commented Oct 20, 2021

Thank you very much for working on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error when trying to buy USPS postage with version 4.0.1
4 participants