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
This commit is contained in:
Jay Patel
2015-02-23 21:18:45 -06:00
parent 397f1a66e2
commit bedb6aa047
7 changed files with 89 additions and 25 deletions

View File

@@ -80,6 +80,7 @@ class PayPalModel
} }
return $list; return $list;
} }
return array();
} }
/** /**
@@ -180,16 +181,22 @@ class PayPalModel
public function fromArray($arr) public function fromArray($arr)
{ {
if (!empty($arr)) { if (!empty($arr)) {
// Iterate over each element in array
foreach ($arr as $k => $v) { foreach ($arr as $k => $v) {
// If the value is an array, it means, it is an object after conversion
if (is_array($v)) { if (is_array($v)) {
$clazz = ReflectionUtil::getPropertyClass(get_class($this), $k); // 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)) { if (ArrayUtil::isAssocArray($v)) {
/** @var self $o */ /** @var self $o */
$o = new $clazz(); $o = new $clazz();
$o->fromArray($v); $o->fromArray($v);
$this->assignValue($k, $o); $this->assignValue($k, $o);
} else { } else {
// Else, value is an array of object/data
$arr = array(); $arr = array();
// Iterate through each element in that array.
foreach ($v as $nk => $nv) { foreach ($v as $nk => $nv) {
if (is_array($nv)) { if (is_array($nv)) {
$o = new $clazz(); $o = new $clazz();
@@ -204,6 +211,9 @@ class PayPalModel
} else { } else {
$this->assignValue($k, $v); $this->assignValue($k, $v);
} }
} else {
$this->assignValue($k, $v);
}
} }
} }
return $this; return $this;
@@ -211,6 +221,7 @@ class PayPalModel
private function assignValue($key, $value) private function assignValue($key, $value)
{ {
// If we find the getter setter, use that, otherwise use magic method.
if (ModelAccessorValidator::validate($this, $this->convertToCamelCase($key))) { if (ModelAccessorValidator::validate($this, $this->convertToCamelCase($key))) {
$setter = "set" . $this->convertToCamelCase($key); $setter = "set" . $this->convertToCamelCase($key);
$this->$setter($value); $this->$setter($value);

View File

@@ -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 $class
* @param $propertyName * @param $propertyName
* @return string * @return null|string
* @throws PayPalConfigurationException
*/ */
public static function getPropertyClass($class, $propertyName) public static function getPropertyClass($class, $propertyName)
{ {
@@ -40,6 +43,11 @@ class ReflectionUtil
return get_class(new PayPalModel()); 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'])) { if (($annotations = self::propertyAnnotations($class, $propertyName)) && isset($annotations['return'])) {
$param = $annotations['return']; $param = $annotations['return'];
} }
@@ -72,9 +80,7 @@ class ReflectionUtil
} }
if (!($refl =& self::$propertiesRefl[$class][$propertyName])) { if (!($refl =& self::$propertiesRefl[$class][$propertyName])) {
$getter = method_exists($class, "get" . ucfirst($propertyName)) ? $getter = self::getter($class, $propertyName);
"get" . ucfirst($propertyName) :
"get" . preg_replace_callback("/([_\-\s]?([a-z0-9]+))/", "self::replace_callback", $propertyName);
$refl = new \ReflectionMethod($class, $getter); $refl = new \ReflectionMethod($class, $getter);
self::$propertiesRefl[$class][$propertyName] = $refl; self::$propertiesRefl[$class][$propertyName] = $refl;
} }
@@ -104,4 +110,19 @@ class ReflectionUtil
{ {
return ucwords($match[2]); 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);
}
} }

View File

@@ -46,7 +46,8 @@ class ModelAccessorValidator
return false; return false;
} }
} }
}
return true; return true;
} }
return false;
}
} }

View File

@@ -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() public function testInvalidMagicMethodWithDisabledValidation()
{ {
PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'disabled')); PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'disabled'));

View File

@@ -146,5 +146,3 @@ class PayPalCredentialManagerTest extends \PHPUnit_Framework_TestCase
$this->assertAttributeEquals($this->config['acct1.ClientSecret'], 'clientSecret', $cred); $this->assertAttributeEquals($this->config['acct1.ClientSecret'], 'clientSecret', $cred);
} }
} }
?>

View File

@@ -1,6 +1,7 @@
<?php <?php
namespace PayPal\Test\Validation; namespace PayPal\Test\Validation;
use PayPal\Core\PayPalConfigManager;
use PayPal\Test\Common\SimpleClass; use PayPal\Test\Common\SimpleClass;
use PayPal\Validation\ModelAccessorValidator; use PayPal\Validation\ModelAccessorValidator;
@@ -27,6 +28,16 @@ class ModelAccessValidatorTest extends \PHPUnit_Framework_TestCase
); );
} }
public function setUp()
{
PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'strict'));
}
public function tearDown()
{
PayPalConfigManager::getInstance()->addConfigs(array('validation.level' => 'strict'));
}
/** /**
* *
* @dataProvider positiveProvider * @dataProvider positiveProvider

View File

@@ -34,7 +34,7 @@ log.FileName=PayPal.log
; Logging level can be one of FINE, INFO, WARN or ERROR ; Logging level can be one of FINE, INFO, WARN or ERROR
; Logging is most verbose in the 'FINE' level and ; Logging is most verbose in the 'FINE' level and
; decreases as you proceed towards ERROR ; decreases as you proceed towards ERROR
log.LogLevel=FINE log.LogLevel=DEBUG
;Validation Configuration ;Validation Configuration
[validation] [validation]