From e2a9d8c06574c32a9c08b5e8eb45f41fe84d0915 Mon Sep 17 00:00:00 2001 From: "Michael J. Rubinsky" Date: Sat, 28 Feb 2009 11:11:35 -0500 Subject: [PATCH] Allow the format of the data returned by H_S_F_Request to be specified. This allows client code to request the raw XML or JSON data - but more importantly, it gets around Facebook's broken JSON support for method calls that return numeric ids that cause overflows when decoded. See: http://bugs.developers.facebook.com/show_bug.cgi?id=4301 --- .../lib/Horde/Service/Facebook.php | 55 +++++++++++++- .../lib/Horde/Service/Facebook/Photos.php | 86 +++++++++++++++++++--- .../lib/Horde/Service/Facebook/Request.php | 56 ++++++++++++-- 3 files changed, 179 insertions(+), 18 deletions(-) diff --git a/framework/Service_Facebook/lib/Horde/Service/Facebook.php b/framework/Service_Facebook/lib/Horde/Service/Facebook.php index 411e91cee..a8a90797c 100644 --- a/framework/Service_Facebook/lib/Horde/Service/Facebook.php +++ b/framework/Service_Facebook/lib/Horde/Service/Facebook.php @@ -101,16 +101,39 @@ class Horde_Service_Facebook */ protected $_context; - static protected $_objCache = array(); + /** + * Return format + * + * @var Horde_Service_Facebook::DATA_FORMAT_* constant + */ + public $dataFormat = self::DATA_FORMAT_ARRAY; - // TODO: Implement some kind of instance array for these types of classes... - protected $_auth = null; // H_S_Facebook_Auth + /** + * Data format used internally if DATA_FORMAT_OBJECT is specified. + * ('json' or 'xml'). Needed to overcome some current bugs in Facebook's + * JSON implementation. + */ + protected $_internalFormat = self::DATA_FORMAT_JSON; + + /** + * Cache for the various objects we lazy load in __get() + * + * @var hash of Horde_Service_Facebook_* objects + */ + static protected $_objCache = array(); const API_VALIDATION_ERROR = 1; const REST_SERVER_ADDR = 'http://api.facebook.com/restserver.php'; /** + * Data format returned to client code. + */ + const DATA_FORMAT_JSON = 'json'; + const DATA_FORMAT_XML = 'xml'; + const DATA_FORMAT_ARRAY = 'array'; + + /** * Const'r * * @param string $api_key Developer API key. @@ -182,6 +205,13 @@ class Horde_Service_Facebook */ public function __get($value) { + // First, see if it's an allowed protected value. + switch ($value) { + case 'internalFormat': + return $this->_internalFormat; + } + var_dump($value); + // If not, assume it's a method/action class... $class = 'Horde_Service_Facebook_' . ucfirst($value); if (!empty(self::$_objCache[$class])) { return self::$_objCache[$class]; @@ -256,6 +286,24 @@ class Horde_Service_Facebook } /** + * Setter for the internal data format. Returns the previously used + * format to make it easier for methods that need a certain format to + * reset the old format when done. + * + * @param Horde_Service_Facebook::DATA_FORMAT_* constant $format + * + * @return Horde_Service_Facebook::DATA_FORMAT_* constant + */ + public function setInternalFormat($format) + { + $old = $this->_internalFormat; + $this->_internalFormat = $format; + + return $old; + } + + + /** * Returns groups according to the filters specified. * * @param int $uid (Optional) User associated with groups. A null @@ -443,6 +491,7 @@ class Horde_Service_Facebook } else { $results = &$this->batchRequest->add($method, $params); } + return $results; } diff --git a/framework/Service_Facebook/lib/Horde/Service/Facebook/Photos.php b/framework/Service_Facebook/lib/Horde/Service/Facebook/Photos.php index 8b073b916..e96abfeaa 100644 --- a/framework/Service_Facebook/lib/Horde/Service/Facebook/Photos.php +++ b/framework/Service_Facebook/lib/Horde/Service/Facebook/Photos.php @@ -53,7 +53,19 @@ class Horde_Service_Facebook_Photos extends Horde_Service_Facebook_Base $params['session_key'] = $skey; } - return $this->_facebook->call_method('facebook.photos.addTag', $params); + // @TODO: HACK - This is needed since FB doesn't currently quote the aid + // in the returned JSON data, which leads to it being interpreted as + // a float which gets mangled due to precision issues. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $oldFormat = $this->_facebook->setInternalFormat(Horde_Service_Facebook::DATA_FORMAT_XML); + } + $results = $this->_facebook->call_method('facebook.photos.addTag', $params); + if (!empty($oldFormat)) { + $this->_facebook->setInternalFormat($oldFormat); + } + + return $results; + } /** @@ -94,7 +106,18 @@ class Horde_Service_Facebook_Photos extends Horde_Service_Facebook_Base $params['session_key'] = $skey; } - return $this->_facebook->call_method('facebook.photos.createAlbum', $params); + // @TODO: HACK - This is needed since FB doesn't currently quote the aid + // in the returned JSON data, which leads to it being interpreted as + // a float which gets mangled due to precision issues. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $oldFormat = $this->_facebook->setInternalFormat(Horde_Service_Facebook::DATA_FORMAT_XML); + } + $results = $this->_facebook->call_method('facebook.photos.createAlbum', $params); + if (!empty($oldFormat)) { + $this->_facebook->setInternalFormat($oldFormat); + } + + return $results; } /** @@ -131,7 +154,19 @@ class Horde_Service_Facebook_Photos extends Horde_Service_Facebook_Base $params['pids'] = $pids; } - return $this->_facebook->call_method('facebook.photos.get', $params); + // @TODO: HACK - This is needed since FB doesn't currently quote the aid + // in the returned JSON data, which leads to it being interpreted as + // a float which gets mangled due to precision issues. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $oldFormat = $this->_facebook->setInternalFormat(Horde_Service_Facebook::DATA_FORMAT_XML); + } + $results = $this->_facebook->call_method('facebook.photos.get', $params); + + if (!empty($oldFormat)) { + $this->_facebook->setInternalFormat($oldFormat); + } + + return $results; } /** @@ -154,10 +189,21 @@ class Horde_Service_Facebook_Photos extends Horde_Service_Facebook_Base Horde_Service_Facebook_ErrorCodes::API_EC_SESSION_REQUIRED); } - return $this->_facebook->call_method('facebook.photos.getAlbums', - array('uid' => $uid, - 'aids' => $aids, - 'session_key' => $skey)); + // @TODO: HACK - This is needed since FB doesn't currently quote the aid + // in the returned JSON data, which leads to it being interpreted as + // a float which gets mangled due to precision issues. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $oldFormat = $this->_facebook->setInternalFormat(Horde_Service_Facebook::DATA_FORMAT_XML); + } + $results = $this->_facebook->call_method('facebook.photos.getAlbums', + array('uid' => $uid, + 'aids' => $aids, + 'session_key' => $skey)); + if (!empty($oldFormat)) { + $this->_facebook->setInternalFormat($oldFormat); + } + + return $results; } /** @@ -177,7 +223,18 @@ class Horde_Service_Facebook_Photos extends Horde_Service_Facebook_Base Horde_Service_Facebook_ErrorCodes::API_EC_SESSION_REQUIRED); } - return $this->_facebook->call_method('facebook.photos.getTags', array('pids' => $pids, 'session_key' => $skey)); + // @TODO: HACK - This is needed since FB doesn't currently quote the aid + // in the returned JSON data, which leads to it being interpreted as + // a float which gets mangled due to precision issues. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $oldFormat = $this->_facebook->setInternalFormat(Horde_Service_Facebook::DATA_FORMAT_XML); + } + $results = $this->_facebook->call_method('facebook.photos.getTags', array('pids' => $pids, 'session_key' => $skey)); + if (!empty($oldFormat)) { + $this->_facebook->setInternalFormat($oldFormat); + } + + return $results; } /** @@ -209,7 +266,18 @@ class Horde_Service_Facebook_Photos extends Horde_Service_Facebook_Base $params['session_key'] = $skey; } - return $this->_facebook->call_upload_method('facebook.photos.upload', $params, $file); + // @TODO: HACK - This is needed since FB doesn't currently quote the aid + // in the returned JSON data, which leads to it being interpreted as + // a float which gets mangled due to precision issues. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $oldFormat = $this->_facebook->setInternalFormat(Horde_Service_Facebook::DATA_FORMAT_XML); + } + $results = $this->_facebook->call_upload_method('facebook.photos.upload', $params, $file); + if (!empty($oldFormat)) { + $this->_facebook->setInternalFormat($oldFormat); + } + + return $results; } } \ No newline at end of file diff --git a/framework/Service_Facebook/lib/Horde/Service/Facebook/Request.php b/framework/Service_Facebook/lib/Horde/Service/Facebook/Request.php index 1249253b6..7b4fbed3a 100644 --- a/framework/Service_Facebook/lib/Horde/Service/Facebook/Request.php +++ b/framework/Service_Facebook/lib/Horde/Service/Facebook/Request.php @@ -21,15 +21,27 @@ class Horde_Service_Facebook_Request /** * Run this request and return the data. - * TODO: Still return by ref until the rest of the code is refactored to not - * use the original post_request method call. * - * @return unknown_type + * @param string $dataFormat Optionally specify the datatype to return. + * + * @return Either raw XML, JSON, or an array of decoded values. */ public function &run() { $data = $this->_postRequest($this->_method, $this->_params); - $result = json_decode($data, true); + switch ($this->_facebook->dataFormat) { + case Horde_Service_Facebook::DATA_FORMAT_JSON: + case Horde_Service_Facebook::DATA_FORMAT_XML: + // Return the raw data, calling code is resposible for decoding. + return $data; + + case Horde_Service_Facebook::DATA_FORMAT_ARRAY: + if ($this->_facebook->internalFormat == Horde_Service_Facebook::DATA_FORMAT_JSON) { + $result = json_decode($data, true); + } else { + $result = $this->_xmlToResult($data); + } + } if (is_array($result) && isset($result['error_code'])) { throw new Horde_Service_Facebook_Exception($result['error_msg'], $result['error_code']); } @@ -63,8 +75,13 @@ class Horde_Service_Facebook_Request protected function _addStandardParams($method, &$params) { - // We only support JSON - $params['format'] = 'json'; + // Select the correct data format. + if ($this->_facebook->dataFormat == Horde_Service_Facebook::DATA_FORMAT_ARRAY) { + $params['format'] = $this->_facebook->internalFormat; + } else { + $params['format'] = $this->_facebook->dataFormat; + } + $params['method'] = $method; $params['api_key'] = $this->_facebook->api_key; $params['call_id'] = microtime(true); @@ -103,4 +120,31 @@ class Horde_Service_Facebook_Request return implode('&', $post_params); } + private function _xmlToResult($xml) + { + $sxml = simplexml_load_string($xml); + $result = self::_simplexmlToArray($sxml); + + return $result; + } + + private static function _simplexmlToArray($sxml) + { + $arr = array(); + if ($sxml) { + foreach ($sxml as $k => $v) { + if ($sxml['list']) { + $arr[] = self::_SimplexmlToArray($v); + } else { + $arr[$k] = self::_SimplexmlToArray($v); + } + } + } + if (sizeof($arr) > 0) { + return $arr; + } else { + return (string)$sxml; + } + } + } \ No newline at end of file -- 2.11.0