Allow the format of the data returned by H_S_F_Request to be specified.
authorMichael J. Rubinsky <mrubinsk@horde.org>
Sat, 28 Feb 2009 16:11:35 +0000 (11:11 -0500)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Sat, 28 Feb 2009 16:11:35 +0000 (11:11 -0500)
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

framework/Service_Facebook/lib/Horde/Service/Facebook.php
framework/Service_Facebook/lib/Horde/Service/Facebook/Photos.php
framework/Service_Facebook/lib/Horde/Service/Facebook/Request.php

index 411e91c..a8a9079 100644 (file)
@@ -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;
     }
 
index 8b073b9..e96abfe 100644 (file)
@@ -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
index 1249253..7b4fbed 100644 (file)
@@ -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