From bedb6aa047a1d4eb385743d14e5372c1c2355af4 Mon Sep 17 00:00:00 2001 From: Jay Patel Date: Mon, 23 Feb 2015 21:18:45 -0600 Subject: [PATCH] Ability to handle missing accesors for unknown objects in json - JSON body that has objects who do not have Model Getter Setters are handled properly --- lib/PayPal/Common/PayPalModel.php | 43 ++++++++++++------- lib/PayPal/Common/ReflectionUtil.php | 31 ++++++++++--- .../Validation/ModelAccessorValidator.php | 3 +- tests/PayPal/Test/Common/ModelTest.php | 22 ++++++++++ .../Test/Core/PayPalCredentialManagerTest.php | 2 - .../Validation/ModelAccessValidatorTest.php | 11 +++++ tests/sdk_config.ini | 2 +- 7 files changed, 89 insertions(+), 25 deletions(-) diff --git a/lib/PayPal/Common/PayPalModel.php b/lib/PayPal/Common/PayPalModel.php index 39ab085..260ecb2 100644 --- a/lib/PayPal/Common/PayPalModel.php +++ b/lib/PayPal/Common/PayPalModel.php @@ -80,6 +80,7 @@ class PayPalModel } return $list; } + return array(); } /** @@ -180,26 +181,35 @@ class PayPalModel public function fromArray($arr) { if (!empty($arr)) { + // Iterate over each element in array foreach ($arr as $k => $v) { + // If the value is an array, it means, it is an object after conversion if (is_array($v)) { - $clazz = ReflectionUtil::getPropertyClass(get_class($this), $k); - if (ArrayUtil::isAssocArray($v)) { - /** @var self $o */ - $o = new $clazz(); - $o->fromArray($v); - $this->assignValue($k, $o); - } else { - $arr = array(); - foreach ($v as $nk => $nv) { - if (is_array($nv)) { - $o = new $clazz(); - $o->fromArray($nv); - $arr[$nk] = $o; - } else { - $arr[$nk] = $nv; + // Determine the class of the object + if (($clazz = ReflectionUtil::getPropertyClass(get_class($this), $k)) != null){ + // If the value is an associative array, it means, its an object. Just make recursive call to it. + if (ArrayUtil::isAssocArray($v)) { + /** @var self $o */ + $o = new $clazz(); + $o->fromArray($v); + $this->assignValue($k, $o); + } else { + // Else, value is an array of object/data + $arr = array(); + // Iterate through each element in that array. + foreach ($v as $nk => $nv) { + if (is_array($nv)) { + $o = new $clazz(); + $o->fromArray($nv); + $arr[$nk] = $o; + } else { + $arr[$nk] = $nv; + } } + $this->assignValue($k, $arr); } - $this->assignValue($k, $arr); + } else { + $this->assignValue($k, $v); } } else { $this->assignValue($k, $v); @@ -211,6 +221,7 @@ class PayPalModel private function assignValue($key, $value) { + // If we find the getter setter, use that, otherwise use magic method. if (ModelAccessorValidator::validate($this, $this->convertToCamelCase($key))) { $setter = "set" . $this->convertToCamelCase($key); $this->$setter($value); diff --git a/lib/PayPal/Common/ReflectionUtil.php b/lib/PayPal/Common/ReflectionUtil.php index 55ee180..1af5618 100644 --- a/lib/PayPal/Common/ReflectionUtil.php +++ b/lib/PayPal/Common/ReflectionUtil.php @@ -27,11 +27,14 @@ class ReflectionUtil /** - * Gets Property Class + * Gets Property Class of the given property. + * If the class is null, it returns null. + * If the property is not found, it returns null. * * @param $class * @param $propertyName - * @return string + * @return null|string + * @throws PayPalConfigurationException */ public static function getPropertyClass($class, $propertyName) { @@ -40,6 +43,11 @@ class ReflectionUtil return get_class(new PayPalModel()); } + // If the class doesn't exist, or the method doesn't exist, return null. + if (!class_exists($class) || !method_exists($class, self::getter($class, $propertyName))) { + return null; + } + if (($annotations = self::propertyAnnotations($class, $propertyName)) && isset($annotations['return'])) { $param = $annotations['return']; } @@ -72,9 +80,7 @@ class ReflectionUtil } if (!($refl =& self::$propertiesRefl[$class][$propertyName])) { - $getter = method_exists($class, "get" . ucfirst($propertyName)) ? - "get" . ucfirst($propertyName) : - "get" . preg_replace_callback("/([_\-\s]?([a-z0-9]+))/", "self::replace_callback", $propertyName); + $getter = self::getter($class, $propertyName); $refl = new \ReflectionMethod($class, $getter); self::$propertiesRefl[$class][$propertyName] = $refl; } @@ -104,4 +110,19 @@ class ReflectionUtil { return ucwords($match[2]); } + + /** + * Returns the properly formatted getter function name based on class name and property + * Formats the property name to a standard getter function + * + * @param string $class + * @param string $propertyName + * @return string getter function name + */ + public static function getter($class, $propertyName) + { + return method_exists($class, "get" . ucfirst($propertyName)) ? + "get" . ucfirst($propertyName) : + "get" . preg_replace_callback("/([_\-\s]?([a-z0-9]+))/", "self::replace_callback", $propertyName); + } } diff --git a/lib/PayPal/Validation/ModelAccessorValidator.php b/lib/PayPal/Validation/ModelAccessorValidator.php index 3f324da..d8a4d6e 100644 --- a/lib/PayPal/Validation/ModelAccessorValidator.php +++ b/lib/PayPal/Validation/ModelAccessorValidator.php @@ -46,7 +46,8 @@ class ModelAccessorValidator return false; } } + return true; } - return true; + return false; } } diff --git a/tests/PayPal/Test/Common/ModelTest.php b/tests/PayPal/Test/Common/ModelTest.php index 9972394..c5a5a98 100644 --- a/tests/PayPal/Test/Common/ModelTest.php +++ b/tests/PayPal/Test/Common/ModelTest.php @@ -98,6 +98,28 @@ class ModelTest extends \PHPUnit_Framework_TestCase } } + /** + * Test Case to determine if the unknown object is returned, it would not add that object to the model. + */ + public function testUnknownObjectConversion() + { + PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'disabled')); + $json = '{"name":"test","unknown":{ "id" : "123", "object": "456"},"description":"description"}'; + + $obj = new SimpleClass(); + $obj->fromJson($json); + + $this->assertEquals("test", $obj->getName()); + $this->assertEquals("description", $obj->getDescription()); + $resultJson = $obj->toJSON(); + $this->assertContains("unknown", $resultJson); + $this->assertContains("id", $resultJson); + $this->assertContains("object", $resultJson); + $this->assertContains("123", $resultJson); + $this->assertContains("456", $resultJson); + PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'strict')); + } + public function testInvalidMagicMethodWithDisabledValidation() { PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'disabled')); diff --git a/tests/PayPal/Test/Core/PayPalCredentialManagerTest.php b/tests/PayPal/Test/Core/PayPalCredentialManagerTest.php index 6c93cc1..f87f552 100644 --- a/tests/PayPal/Test/Core/PayPalCredentialManagerTest.php +++ b/tests/PayPal/Test/Core/PayPalCredentialManagerTest.php @@ -146,5 +146,3 @@ class PayPalCredentialManagerTest extends \PHPUnit_Framework_TestCase $this->assertAttributeEquals($this->config['acct1.ClientSecret'], 'clientSecret', $cred); } } - -?> diff --git a/tests/PayPal/Test/Validation/ModelAccessValidatorTest.php b/tests/PayPal/Test/Validation/ModelAccessValidatorTest.php index 1dee93c..b670794 100644 --- a/tests/PayPal/Test/Validation/ModelAccessValidatorTest.php +++ b/tests/PayPal/Test/Validation/ModelAccessValidatorTest.php @@ -1,6 +1,7 @@ addConfigs(array('validation.level' => 'strict')); + } + + public function tearDown() + { + PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'strict')); + } + /** * * @dataProvider positiveProvider diff --git a/tests/sdk_config.ini b/tests/sdk_config.ini index 0957ccc..7167c96 100644 --- a/tests/sdk_config.ini +++ b/tests/sdk_config.ini @@ -34,7 +34,7 @@ log.FileName=PayPal.log ; Logging level can be one of FINE, INFO, WARN or ERROR ; Logging is most verbose in the 'FINE' level and ; decreases as you proceed towards ERROR -log.LogLevel=FINE +log.LogLevel=DEBUG ;Validation Configuration [validation]