SHARE
TWEET

Untitled

a guest Sep 28th, 2009 733 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <?php
  2. // Copyright 2004-2009 Facebook. All Rights Reserved.
  3. //
  4. // +---------------------------------------------------------------------------+
  5. // | Facebook Platform PHP5 client                                             |
  6. // +---------------------------------------------------------------------------+
  7. // | Copyright (c) 2007-2009 Facebook, Inc.                                    |
  8. // | All rights reserved.                                                      |
  9. // |                                                                           |
  10. // | Redistribution and use in source and binary forms, with or without        |
  11. // | modification, are permitted provided that the following conditions        |
  12. // | are met:                                                                  |
  13. // |                                                                           |
  14. // | 1. Redistributions of source code must retain the above copyright         |
  15. // |    notice, this list of conditions and the following disclaimer.          |
  16. // | 2. Redistributions in binary form must reproduce the above copyright      |
  17. // |    notice, this list of conditions and the following disclaimer in the    |
  18. // |    documentation and/or other materials provided with the distribution.   |
  19. // |                                                                           |
  20. // | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
  21. // | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
  22. // | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
  23. // | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  24. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
  25. // | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
  29. // | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
  30. // +---------------------------------------------------------------------------+
  31. // | For help with this library, contact developers-help@facebook.com          |
  32. // +---------------------------------------------------------------------------+
  33. //
  34.  
  35. include_once 'jsonwrapper.php';
  36.  
  37. class FacebookRestClient {
  38.   public $secret;
  39.   public $session_key;
  40.   public $api_key;
  41.   // to save making the friends.get api call, this will get prepopulated on
  42.   // canvas pages
  43.   public $friends_list;
  44.   public $user;
  45.   // to save making the pages.isAppAdded api call, this will get prepopulated
  46.   // on canvas pages
  47.   public $added;
  48.   public $is_user;
  49.   // we don't pass friends list to iframes, but we want to make
  50.   // friends_get really simple in the canvas_user (non-logged in) case.
  51.   // So we use the canvas_user as default arg to friends_get
  52.   public $canvas_user;
  53.   public $batch_mode;
  54.   private $batch_queue;
  55.   private $call_as_apikey;
  56.   private $use_curl_if_available;
  57.  
  58.   const BATCH_MODE_DEFAULT = 0;
  59.   const BATCH_MODE_SERVER_PARALLEL = 0;
  60.   const BATCH_MODE_SERIAL_ONLY = 2;
  61.  
  62.   /**
  63.    * Create the client.
  64.    * @param string $session_key if you haven't gotten a session key yet, leave
  65.    *                            this as null and then set it later by just
  66.    *                            directly accessing the $session_key member
  67.    *                            variable.
  68.    */
  69.   public function __construct($api_key, $secret, $session_key=null) {
  70.     $this->secret       = $secret;
  71.     $this->session_key  = $session_key;
  72.     $this->api_key      = $api_key;
  73.     $this->batch_mode = FacebookRestClient::BATCH_MODE_DEFAULT;
  74.     $this->last_call_id = 0;
  75.     $this->call_as_apikey = '';
  76.     $this->use_curl_if_available = true;
  77.     $this->server_addr  = Facebook::get_facebook_url('api') . '/restserver.php';
  78.  
  79.     if (!empty($GLOBALS['facebook_config']['debug'])) {
  80.       $this->cur_id = 0;
  81.       ?>
  82. <script type="text/javascript">
  83. var types = ['params', 'xml', 'php', 'sxml'];
  84. function getStyle(elem, style) {
  85.   if (elem.getStyle) {
  86.     return elem.getStyle(style);
  87.   } else {
  88.     return elem.style[style];
  89.   }
  90. }
  91. function setStyle(elem, style, value) {
  92.   if (elem.setStyle) {
  93.     elem.setStyle(style, value);
  94.   } else {
  95.     elem.style[style] = value;
  96.   }
  97. }
  98. function toggleDisplay(id, type) {
  99.   for (var i = 0; i < types.length; i++) {
  100.     var t = types[i];
  101.     var pre = document.getElementById(t + id);
  102.     if (pre) {
  103.       if (t != type || getStyle(pre, 'display') == 'block') {
  104.         setStyle(pre, 'display', 'none');
  105.       } else {
  106.         setStyle(pre, 'display', 'block');
  107.       }
  108.     }
  109.   }
  110.   return false;
  111. }
  112. </script>
  113. <?php
  114.     }
  115.   }
  116.  
  117.   /**
  118.    * Set the default user id for methods that allow the caller
  119.    * to pass an uid parameter to identify the target user
  120.    * instead of a session key. This currently applies to
  121.    * the user preferences methods.
  122.    *
  123.    * @param $uid int the user id
  124.    */
  125.   public function set_user($uid) {
  126.     $this->user = $uid;
  127.   }
  128.  
  129.   /**
  130.    * Normally, if the cURL library/PHP extension is available, it is used for
  131.    * HTTP transactions.  This allows that behavior to be overridden, falling
  132.    * back to a vanilla-PHP implementation even if cURL is installed.
  133.    *
  134.    * @param $use_curl_if_available bool whether or not to use cURL if available
  135.    */
  136.   public function set_use_curl_if_available($use_curl_if_available) {
  137.     $this->use_curl_if_available = $use_curl_if_available;
  138.   }
  139.  
  140.   /**
  141.    * Start a batch operation.
  142.    */
  143.   public function begin_batch() {
  144.     if($this->batch_queue !== null) {
  145.       $code = FacebookAPIErrorCodes::API_EC_BATCH_ALREADY_STARTED;
  146.       $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
  147.       throw new FacebookRestClientException($description, $code);
  148.     }
  149.  
  150.     $this->batch_queue = array();
  151.   }
  152.  
  153.   /*
  154.    * End current batch operation
  155.    */
  156.   public function end_batch() {
  157.     if($this->batch_queue === null) {
  158.       $code = FacebookAPIErrorCodes::API_EC_BATCH_NOT_STARTED;
  159.       $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
  160.       throw new FacebookRestClientException($description, $code);
  161.     }
  162.  
  163.     $this->execute_server_side_batch();
  164.  
  165.     $this->batch_queue = null;
  166.   }
  167.  
  168.   private function execute_server_side_batch() {
  169.     $item_count = count($this->batch_queue);
  170.     $method_feed = array();
  171.     foreach($this->batch_queue as $batch_item) {
  172.       $method = $batch_item['m'];
  173.       $params = $batch_item['p'];
  174.       $this->finalize_params($method, $params);
  175.       $method_feed[] = $this->create_post_string($method, $params);
  176.     }
  177.  
  178.     $method_feed_json = json_encode($method_feed);
  179.  
  180.     $serial_only =
  181.       ($this->batch_mode == FacebookRestClient::BATCH_MODE_SERIAL_ONLY);
  182.     $params = array('method_feed' => $method_feed_json,
  183.                     'serial_only' => $serial_only);
  184.     if ($this->call_as_apikey) {
  185.       $params['call_as_apikey'] = $this->call_as_apikey;
  186.     }
  187.  
  188.     $xml = $this->post_request('batch.run', $params);
  189.  
  190.     $result = $this->convert_xml_to_result($xml, 'batch.run', $params);
  191.  
  192.  
  193.     if (is_array($result) && isset($result['error_code'])) {
  194.       throw new FacebookRestClientException($result['error_msg'],
  195.                                             $result['error_code']);
  196.     }
  197.  
  198.     for($i = 0; $i < $item_count; $i++) {
  199.       $batch_item = $this->batch_queue[$i];
  200.       $batch_item_result_xml = $result[$i];
  201.       $batch_item_result = $this->convert_xml_to_result($batch_item_result_xml,
  202.                                                         $batch_item['m'],
  203.                                                         $batch_item['p']);
  204.  
  205.       if (is_array($batch_item_result) &&
  206.           isset($batch_item_result['error_code'])) {
  207.         throw new FacebookRestClientException($batch_item_result['error_msg'],
  208.                                               $batch_item_result['error_code']);
  209.       }
  210.       $batch_item['r'] = $batch_item_result;
  211.     }
  212.   }
  213.  
  214.   public function begin_permissions_mode($permissions_apikey) {
  215.     $this->call_as_apikey = $permissions_apikey;
  216.   }
  217.  
  218.   public function end_permissions_mode() {
  219.     $this->call_as_apikey = '';
  220.   }
  221.  
  222.  
  223.   /*
  224.    * If a page is loaded via HTTPS, then all images and static
  225.    * resources need to be printed with HTTPS urls to avoid
  226.    * mixed content warnings. If your page loads with an HTTPS
  227.    * url, then call set_use_ssl_resources to retrieve the correct
  228.    * urls.
  229.    */
  230.   public function set_use_ssl_resources($is_ssl = true) {
  231.     $this->use_ssl_resources = $is_ssl;
  232.   }
  233.  
  234.   /**
  235.    * Returns public information for an application (as shown in the application
  236.    * directory) by either application ID, API key, or canvas page name.
  237.    *
  238.    * @param int $application_id              (Optional) app id
  239.    * @param string $application_api_key      (Optional) api key
  240.    * @param string $application_canvas_name  (Optional) canvas name
  241.    *
  242.    * Exactly one argument must be specified, otherwise it is an error.
  243.    *
  244.    * @return array  An array of public information about the application.
  245.    */
  246.   public function application_getPublicInfo($application_id=null,
  247.                                             $application_api_key=null,
  248.                                             $application_canvas_name=null) {
  249.     return $this->call_method('facebook.application.getPublicInfo',
  250.         array('application_id' => $application_id,
  251.               'application_api_key' => $application_api_key,
  252.               'application_canvas_name' => $application_canvas_name));
  253.   }
  254.  
  255.   /**
  256.    * Creates an authentication token to be used as part of the desktop login
  257.    * flow.  For more information, please see
  258.    * http://wiki.developers.facebook.com/index.php/Auth.createToken.
  259.    *
  260.    * @return string  An authentication token.
  261.    */
  262.   public function auth_createToken() {
  263.     return $this->call_method('facebook.auth.createToken');
  264.   }
  265.  
  266.   /**
  267.    * Returns the session information available after current user logs in.
  268.    *
  269.    * @param string $auth_token             the token returned by
  270.    *                                       auth_createToken or passed back to
  271.    *                                       your callback_url.
  272.    * @param bool $generate_session_secret  whether the session returned should
  273.    *                                       include a session secret
  274.    *
  275.    * @return array  An assoc array containing session_key, uid
  276.    */
  277.   public function auth_getSession($auth_token, $generate_session_secret=false) {
  278.     //Check if we are in batch mode
  279.     if($this->batch_queue === null) {
  280.       $result = $this->call_method('facebook.auth.getSession',
  281.           array('auth_token' => $auth_token,
  282.                 'generate_session_secret' => $generate_session_secret));
  283.       $this->session_key = $result['session_key'];
  284.  
  285.     if (!empty($result['secret']) && !$generate_session_secret) {
  286.       // desktop apps have a special secret
  287.       $this->secret = $result['secret'];
  288.     }
  289.       return $result;
  290.     }
  291.   }
  292.  
  293.   /**
  294.    * Generates a session-specific secret. This is for integration with
  295.    * client-side API calls, such as the JS library.
  296.    *
  297.    * @return array  A session secret for the current promoted session
  298.    *
  299.    * @error API_EC_PARAM_SESSION_KEY
  300.    *        API_EC_PARAM_UNKNOWN
  301.    */
  302.   public function auth_promoteSession() {
  303.       return $this->call_method('facebook.auth.promoteSession');
  304.   }
  305.  
  306.   /**
  307.    * Expires the session that is currently being used.  If this call is
  308.    * successful, no further calls to the API (which require a session) can be
  309.    * made until a valid session is created.
  310.    *
  311.    * @return bool  true if session expiration was successful, false otherwise
  312.    */
  313.   public function auth_expireSession() {
  314.       return $this->call_method('facebook.auth.expireSession');
  315.   }
  316.  
  317.   /**
  318.    * Revokes the user's agreement to the Facebook Terms of Service for your
  319.    * application.  If you call this method for one of your users, you will no
  320.    * longer be able to make API requests on their behalf until they again
  321.    * authorize your application.  Use with care.  Note that if this method is
  322.    * called without a user parameter, then it will revoke access for the
  323.    * current session's user.
  324.    *
  325.    * @param int $uid  (Optional) User to revoke
  326.    *
  327.    * @return bool  true if revocation succeeds, false otherwise
  328.    */
  329.   public function auth_revokeAuthorization($uid=null) {
  330.       return $this->call_method('facebook.auth.revokeAuthorization',
  331.           array('uid' => $uid));
  332.   }
  333.  
  334.   /**
  335.    * Returns the number of unconnected friends that exist in this application.
  336.    * This number is determined based on the accounts registered through
  337.    * connect.registerUsers() (see below).
  338.    */
  339.   public function connect_getUnconnectedFriendsCount() {
  340.     return $this->call_method('facebook.connect.getUnconnectedFriendsCount',
  341.         array());
  342.   }
  343.  
  344.  /**
  345.   * This method is used to create an association between an external user
  346.   * account and a Facebook user account, as per Facebook Connect.
  347.   *
  348.   * This method takes an array of account data, including a required email_hash
  349.   * and optional account data. For each connected account, if the user exists,
  350.   * the information is added to the set of the user's connected accounts.
  351.   * If the user has already authorized the site, the connected account is added
  352.   * in the confirmed state. If the user has not yet authorized the site, the
  353.   * connected account is added in the pending state.
  354.   *
  355.   * This is designed to help Facebook Connect recognize when two Facebook
  356.   * friends are both members of a external site, but perhaps are not aware of
  357.   * it.  The Connect dialog (see fb:connect-form) is used when friends can be
  358.   * identified through these email hashes. See the following url for details:
  359.   *
  360.   *   http://wiki.developers.facebook.com/index.php/Connect.registerUsers
  361.   *
  362.   * @param mixed $accounts A (JSON-encoded) array of arrays, where each array
  363.   *                        has three properties:
  364.   *                        'email_hash'  (req) - public email hash of account
  365.   *                        'account_id'  (opt) - remote account id;
  366.   *                        'account_url' (opt) - url to remote account;
  367.   *
  368.   * @return array  The list of email hashes for the successfully registered
  369.   *                accounts.
  370.   */
  371.   public function connect_registerUsers($accounts) {
  372.     return $this->call_method('facebook.connect.registerUsers',
  373.         array('accounts' => $accounts));
  374.   }
  375.  
  376.  /**
  377.   * Unregisters a set of accounts registered using connect.registerUsers.
  378.   *
  379.   * @param array $email_hashes  The (JSON-encoded) list of email hashes to be
  380.   *                             unregistered.
  381.   *
  382.   * @return array  The list of email hashes which have been successfully
  383.   *                unregistered.
  384.   */
  385.   public function connect_unregisterUsers($email_hashes) {
  386.     return $this->call_method('facebook.connect.unregisterUsers',
  387.         array('email_hashes' => $email_hashes));
  388.   }
  389.  
  390.   /**
  391.    * Returns events according to the filters specified.
  392.    *
  393.    * @param int $uid            (Optional) User associated with events. A null
  394.    *                            parameter will default to the session user.
  395.    * @param string $eids        (Optional) Filter by these comma-separated event
  396.    *                            ids. A null parameter will get all events for
  397.    *                            the user.
  398.    * @param int $start_time     (Optional) Filter with this unix time as lower
  399.    *                            bound.  A null or zero parameter indicates no
  400.    *                            lower bound.
  401.    * @param int $end_time       (Optional) Filter with this UTC as upper bound.
  402.    *                            A null or zero parameter indicates no upper
  403.    *                            bound.
  404.    * @param string $rsvp_status (Optional) Only show events where the given uid
  405.    *                            has this rsvp status.  This only works if you
  406.    *                            have specified a value for $uid.  Values are as
  407.    *                            in events.getMembers.  Null indicates to ignore
  408.    *                            rsvp status when filtering.
  409.    *
  410.    * @return array  The events matching the query.
  411.    */
  412.   public function &events_get($uid=null,
  413.                               $eids=null,
  414.                               $start_time=null,
  415.                               $end_time=null,
  416.                               $rsvp_status=null) {
  417.     return $this->call_method('facebook.events.get',
  418.         array('uid' => $uid,
  419.               'eids' => $eids,
  420.               'start_time' => $start_time,
  421.               'end_time' => $end_time,
  422.               'rsvp_status' => $rsvp_status));
  423.   }
  424.  
  425.   /**
  426.    * Returns membership list data associated with an event.
  427.    *
  428.    * @param int $eid  event id
  429.    *
  430.    * @return array  An assoc array of four membership lists, with keys
  431.    *                'attending', 'unsure', 'declined', and 'not_replied'
  432.    */
  433.   public function &events_getMembers($eid) {
  434.     return $this->call_method('facebook.events.getMembers',
  435.       array('eid' => $eid));
  436.   }
  437.  
  438.   /**
  439.    * RSVPs the current user to this event.
  440.    *
  441.    * @param int $eid             event id
  442.    * @param string $rsvp_status  'attending', 'unsure', or 'declined'
  443.    *
  444.    * @return bool  true if successful
  445.    */
  446.   public function &events_rsvp($eid, $rsvp_status) {
  447.     return $this->call_method('facebook.events.rsvp',
  448.         array(
  449.         'eid' => $eid,
  450.         'rsvp_status' => $rsvp_status));
  451.   }
  452.  
  453.   /**
  454.    * Cancels an event. Only works for events where application is the admin.
  455.    *
  456.    * @param int $eid                event id
  457.    * @param string $cancel_message  (Optional) message to send to members of
  458.    *                                the event about why it is cancelled
  459.    *
  460.    * @return bool  true if successful
  461.    */
  462.   public function &events_cancel($eid, $cancel_message='') {
  463.     return $this->call_method('facebook.events.cancel',
  464.         array('eid' => $eid,
  465.               'cancel_message' => $cancel_message));
  466.   }
  467.  
  468.   /**
  469.    * Creates an event on behalf of the user is there is a session, otherwise on
  470.    * behalf of app.  Successful creation guarantees app will be admin.
  471.    *
  472.    * @param assoc array $event_info  json encoded event information
  473.    *
  474.    * @return int  event id
  475.    */
  476.   public function &events_create($event_info) {
  477.     return $this->call_method('facebook.events.create',
  478.         array('event_info' => $event_info));
  479.   }
  480.  
  481.   /**
  482.    * Edits an existing event. Only works for events where application is admin.
  483.    *
  484.    * @param int $eid                 event id
  485.    * @param assoc array $event_info  json encoded event information
  486.    *
  487.    * @return bool  true if successful
  488.    */
  489.   public function &events_edit($eid, $event_info) {
  490.     return $this->call_method('facebook.events.edit',
  491.         array('eid' => $eid,
  492.               'event_info' => $event_info));
  493.   }
  494.  
  495.   /**
  496.    * Fetches and re-caches the image stored at the given URL, for use in images
  497.    * published to non-canvas pages via the API (for example, to user profiles
  498.    * via profile.setFBML, or to News Feed via feed.publishUserAction).
  499.    *
  500.    * @param string $url  The absolute URL from which to refresh the image.
  501.    *
  502.    * @return bool  true on success
  503.    */
  504.   public function &fbml_refreshImgSrc($url) {
  505.     return $this->call_method('facebook.fbml.refreshImgSrc',
  506.         array('url' => $url));
  507.   }
  508.  
  509.   /**
  510.    * Fetches and re-caches the content stored at the given URL, for use in an
  511.    * fb:ref FBML tag.
  512.    *
  513.    * @param string $url  The absolute URL from which to fetch content. This URL
  514.    *                     should be used in a fb:ref FBML tag.
  515.    *
  516.    * @return bool  true on success
  517.    */
  518.   public function &fbml_refreshRefUrl($url) {
  519.     return $this->call_method('facebook.fbml.refreshRefUrl',
  520.         array('url' => $url));
  521.   }
  522.  
  523.   /**
  524.    * Lets you insert text strings in their native language into the Facebook
  525.    * Translations database so they can be translated.
  526.    *
  527.    * @param array $native_strings  An array of maps, where each map has a 'text'
  528.    *                               field and a 'description' field.
  529.    *
  530.    * @return int  Number of strings uploaded.
  531.    */
  532.   public function &fbml_uploadNativeStrings($native_strings) {
  533.     return $this->call_method('facebook.fbml.uploadNativeStrings',
  534.         array('native_strings' => json_encode($native_strings)));
  535.   }
  536.  
  537.   /**
  538.    * Associates a given "handle" with FBML markup so that the handle can be
  539.    * used within the fb:ref FBML tag. A handle is unique within an application
  540.    * and allows an application to publish identical FBML to many user profiles
  541.    * and do subsequent updates without having to republish FBML on behalf of
  542.    * each user.
  543.    *
  544.    * @param string $handle  The handle to associate with the given FBML.
  545.    * @param string $fbml    The FBML to associate with the given handle.
  546.    *
  547.    * @return bool  true on success
  548.    */
  549.   public function &fbml_setRefHandle($handle, $fbml) {
  550.     return $this->call_method('facebook.fbml.setRefHandle',
  551.         array('handle' => $handle, 'fbml' => $fbml));
  552.   }
  553.  
  554.   /**
  555.    * Register custom tags for the application. Custom tags can be used
  556.    * to extend the set of tags available to applications in FBML
  557.    * markup.
  558.    *
  559.    * Before you call this function,
  560.    * make sure you read the full documentation at
  561.    *
  562.    * http://wiki.developers.facebook.com/index.php/Fbml.RegisterCustomTags
  563.    *
  564.    * IMPORTANT: This function overwrites the values of
  565.    * existing tags if the names match. Use this function with care because
  566.    * it may break the FBML of any application that is using the
  567.    * existing version of the tags.
  568.    *
  569.    * @param mixed $tags an array of tag objects (the full description is on the
  570.    *   wiki page)
  571.    *
  572.    * @return int  the number of tags that were registered
  573.    */
  574.   public function &fbml_registerCustomTags($tags) {
  575.     $tags = json_encode($tags);
  576.     return $this->call_method('facebook.fbml.registerCustomTags',
  577.                               array('tags' => $tags));
  578.   }
  579.  
  580.   /**
  581.    * Get the custom tags for an application. If $app_id
  582.    * is not specified, the calling app's tags are returned.
  583.    * If $app_id is different from the id of the calling app,
  584.    * only the app's public tags are returned.
  585.    * The return value is an array of the same type as
  586.    * the $tags parameter of fbml_registerCustomTags().
  587.    *
  588.    * @param int $app_id the application's id (optional)
  589.    *
  590.    * @return mixed  an array containing the custom tag  objects
  591.    */
  592.   public function &fbml_getCustomTags($app_id = null) {
  593.     return $this->call_method('facebook.fbml.getCustomTags',
  594.                               array('app_id' => $app_id));
  595.   }
  596.  
  597.  
  598.   /**
  599.    * Delete custom tags the application has registered. If
  600.    * $tag_names is null, all the application's custom tags will be
  601.    * deleted.
  602.    *
  603.    * IMPORTANT: If your application has registered public tags
  604.    * that other applications may be using, don't delete those tags!
  605.    * Doing so can break the FBML ofapplications that are using them.
  606.    *
  607.    * @param array $tag_names the names of the tags to delete (optinal)
  608.    * @return bool true on success
  609.    */
  610.   public function &fbml_deleteCustomTags($tag_names = null) {
  611.     return $this->call_method('facebook.fbml.deleteCustomTags',
  612.                               array('tag_names' => json_encode($tag_names)));
  613.   }
  614.  
  615.  
  616.  
  617.   /**
  618.    * This method is deprecated for calls made on behalf of users. This method
  619.    * works only for publishing stories on a Facebook Page that has installed
  620.    * your application. To publish stories to a user's profile, use
  621.    * feed.publishUserAction instead.
  622.    *
  623.    * For more details on this call, please visit the wiki page:
  624.    *
  625.    * http://wiki.developers.facebook.com/index.php/Feed.publishTemplatizedAction
  626.    */
  627.   public function &feed_publishTemplatizedAction($title_template,
  628.                                                  $title_data,
  629.                                                  $body_template,
  630.                                                  $body_data,
  631.                                                  $body_general,
  632.                                                  $image_1=null,
  633.                                                  $image_1_link=null,
  634.                                                  $image_2=null,
  635.                                                  $image_2_link=null,
  636.                                                  $image_3=null,
  637.                                                  $image_3_link=null,
  638.                                                  $image_4=null,
  639.                                                  $image_4_link=null,
  640.                                                  $target_ids='',
  641.                                                  $page_actor_id=null) {
  642.     return $this->call_method('facebook.feed.publishTemplatizedAction',
  643.       array('title_template' => $title_template,
  644.             'title_data' => $title_data,
  645.             'body_template' => $body_template,
  646.             'body_data' => $body_data,
  647.             'body_general' => $body_general,
  648.             'image_1' => $image_1,
  649.             'image_1_link' => $image_1_link,
  650.             'image_2' => $image_2,
  651.             'image_2_link' => $image_2_link,
  652.             'image_3' => $image_3,
  653.             'image_3_link' => $image_3_link,
  654.             'image_4' => $image_4,
  655.             'image_4_link' => $image_4_link,
  656.             'target_ids' => $target_ids,
  657.             'page_actor_id' => $page_actor_id));
  658.   }
  659.  
  660.   /**
  661.    * Registers a template bundle.  Template bundles are somewhat involved, so
  662.    * it's recommended you check out the wiki for more details:
  663.    *
  664.    *  http://wiki.developers.facebook.com/index.php/Feed.registerTemplateBundle
  665.    *
  666.    * @return string  A template bundle id
  667.    */
  668.   public function &feed_registerTemplateBundle($one_line_story_templates,
  669.                                                $short_story_templates = array(),
  670.                                                $full_story_template = null,
  671.                                                $action_links = array()) {
  672.  
  673.     $one_line_story_templates = json_encode($one_line_story_templates);
  674.  
  675.     if (!empty($short_story_templates)) {
  676.       $short_story_templates = json_encode($short_story_templates);
  677.     }
  678.  
  679.     if (isset($full_story_template)) {
  680.       $full_story_template = json_encode($full_story_template);
  681.     }
  682.  
  683.     if (isset($action_links)) {
  684.       $action_links = json_encode($action_links);
  685.     }
  686.  
  687.     return $this->call_method('facebook.feed.registerTemplateBundle',
  688.         array('one_line_story_templates' => $one_line_story_templates,
  689.               'short_story_templates' => $short_story_templates,
  690.               'full_story_template' => $full_story_template,
  691.               'action_links' => $action_links));
  692.   }
  693.  
  694.   /**
  695.    * Retrieves the full list of active template bundles registered by the
  696.    * requesting application.
  697.    *
  698.    * @return array  An array of template bundles
  699.    */
  700.   public function &feed_getRegisteredTemplateBundles() {
  701.     return $this->call_method('facebook.feed.getRegisteredTemplateBundles',
  702.         array());
  703.   }
  704.  
  705.   /**
  706.    * Retrieves information about a specified template bundle previously
  707.    * registered by the requesting application.
  708.    *
  709.    * @param string $template_bundle_id  The template bundle id
  710.    *
  711.    * @return array  Template bundle
  712.    */
  713.   public function &feed_getRegisteredTemplateBundleByID($template_bundle_id) {
  714.     return $this->call_method('facebook.feed.getRegisteredTemplateBundleByID',
  715.         array('template_bundle_id' => $template_bundle_id));
  716.   }
  717.  
  718.   /**
  719.    * Deactivates a previously registered template bundle.
  720.    *
  721.    * @param string $template_bundle_id  The template bundle id
  722.    *
  723.    * @return bool  true on success
  724.    */
  725.   public function &feed_deactivateTemplateBundleByID($template_bundle_id) {
  726.     return $this->call_method('facebook.feed.deactivateTemplateBundleByID',
  727.         array('template_bundle_id' => $template_bundle_id));
  728.   }
  729.  
  730.   const STORY_SIZE_ONE_LINE = 1;
  731.   const STORY_SIZE_SHORT = 2;
  732.   const STORY_SIZE_FULL = 4;
  733.  
  734.   /**
  735.    * Publishes a story on behalf of the user owning the session, using the
  736.    * specified template bundle. This method requires an active session key in
  737.    * order to be called.
  738.    *
  739.    * The parameters to this method ($templata_data in particular) are somewhat
  740.    * involved.  It's recommended you visit the wiki for details:
  741.    *
  742.    *  http://wiki.developers.facebook.com/index.php/Feed.publishUserAction
  743.    *
  744.    * @param int $template_bundle_id  A template bundle id previously registered
  745.    * @param array $template_data     See wiki article for syntax
  746.    * @param array $target_ids        (Optional) An array of friend uids of the
  747.    *                                 user who shared in this action.
  748.    * @param string $body_general     (Optional) Additional markup that extends
  749.    *                                 the body of a short story.
  750.    * @param int $story_size          (Optional) A story size (see above)
  751.    *
  752.    * @return bool  true on success
  753.    */
  754.   public function &feed_publishUserAction(
  755.       $template_bundle_id, $template_data, $target_ids='', $body_general='',
  756.       $story_size=FacebookRestClient::STORY_SIZE_ONE_LINE) {
  757.  
  758.     if (is_array($template_data)) {
  759.       $template_data = json_encode($template_data);
  760.     } // allow client to either pass in JSON or an assoc that we JSON for them
  761.  
  762.     if (is_array($target_ids)) {
  763.       $target_ids = json_encode($target_ids);
  764.       $target_ids = trim($target_ids, "[]"); // we don't want square brackets
  765.     }
  766.  
  767.     return $this->call_method('facebook.feed.publishUserAction',
  768.         array('template_bundle_id' => $template_bundle_id,
  769.               'template_data' => $template_data,
  770.               'target_ids' => $target_ids,
  771.               'body_general' => $body_general,
  772.               'story_size' => $story_size));
  773.   }
  774.  
  775.   /**
  776.    * For the current user, retrieves stories generated by the user's friends
  777.    * while using this application.  This can be used to easily create a
  778.    * "News Feed" like experience.
  779.    *
  780.    * @return array  An array of feed story objects.
  781.    */
  782.   public function &feed_getAppFriendStories() {
  783.     return $this->call_method('facebook.feed.getAppFriendStories');
  784.   }
  785.  
  786.   /**
  787.    * Makes an FQL query.  This is a generalized way of accessing all the data
  788.    * in the API, as an alternative to most of the other method calls.  More
  789.    * info at http://developers.facebook.com/documentation.php?v=1.0&doc=fql
  790.    *
  791.    * @param string $query  the query to evaluate
  792.    *
  793.    * @return array  generalized array representing the results
  794.    */
  795.   public function &fql_query($query) {
  796.     return $this->call_method('facebook.fql.query',
  797.       array('query' => $query));
  798.   }
  799.  
  800.   /**
  801.    * Returns whether or not pairs of users are friends.
  802.    * Note that the Facebook friend relationship is symmetric.
  803.    *
  804.    * @param string $uids1  comma-separated list of ids (id_1, id_2,...)
  805.    *                       of some length X
  806.    * @param string $uids2  comma-separated list of ids (id_A, id_B,...)
  807.    *                       of SAME length X
  808.    *
  809.    * @return array  An array with uid1, uid2, and bool if friends, e.g.:
  810.    *   array(0 => array('uid1' => id_1, 'uid2' => id_A, 'are_friends' => 1),
  811.    *         1 => array('uid1' => id_2, 'uid2' => id_B, 'are_friends' => 0)
  812.    *         ...)
  813.    * @error
  814.    *    API_EC_PARAM_USER_ID_LIST
  815.    */
  816.   public function &friends_areFriends($uids1, $uids2) {
  817.     return $this->call_method('facebook.friends.areFriends',
  818.         array('uids1' => $uids1, 'uids2' => $uids2));
  819.   }
  820.  
  821.   /**
  822.    * Returns the friends of the current session user.
  823.    *
  824.    * @param int $flid  (Optional) Only return friends on this friend list.
  825.    * @param int $uid   (Optional) Return friends for this user.
  826.    *
  827.    * @return array  An array of friends
  828.    */
  829.   public function &friends_get($flid=null, $uid = null) {
  830.     if (isset($this->friends_list)) {
  831.       return $this->friends_list;
  832.     }
  833.     $params = array();
  834.     if (!$uid && isset($this->canvas_user)) {
  835.       $uid = $this->canvas_user;
  836.     }
  837.     if ($uid) {
  838.       $params['uid'] = $uid;
  839.     }
  840.     if ($flid) {
  841.       $params['flid'] = $flid;
  842.     }
  843.     return $this->call_method('facebook.friends.get', $params);
  844.  
  845.   }
  846.  
  847.   /**
  848.    * Returns the set of friend lists for the current session user.
  849.    *
  850.    * @return array  An array of friend list objects
  851.    */
  852.   public function &friends_getLists() {
  853.     return $this->call_method('facebook.friends.getLists');
  854.   }
  855.  
  856.   /**
  857.    * Returns the friends of the session user, who are also users
  858.    * of the calling application.
  859.    *
  860.    * @return array  An array of friends also using the app
  861.    */
  862.   public function &friends_getAppUsers() {
  863.     return $this->call_method('facebook.friends.getAppUsers');
  864.   }
  865.  
  866.   /**
  867.    * Returns groups according to the filters specified.
  868.    *
  869.    * @param int $uid     (Optional) User associated with groups.  A null
  870.    *                     parameter will default to the session user.
  871.    * @param string $gids (Optional) Comma-separated group ids to query. A null
  872.    *                     parameter will get all groups for the user.
  873.    *
  874.    * @return array  An array of group objects
  875.    */
  876.   public function &groups_get($uid, $gids) {
  877.     return $this->call_method('facebook.groups.get',
  878.         array('uid' => $uid,
  879.               'gids' => $gids));
  880.   }
  881.  
  882.   /**
  883.    * Returns the membership list of a group.
  884.    *
  885.    * @param int $gid  Group id
  886.    *
  887.    * @return array  An array with four membership lists, with keys 'members',
  888.    *                'admins', 'officers', and 'not_replied'
  889.    */
  890.   public function &groups_getMembers($gid) {
  891.     return $this->call_method('facebook.groups.getMembers',
  892.       array('gid' => $gid));
  893.   }
  894.  
  895.   /**
  896.    * Returns cookies according to the filters specified.
  897.    *
  898.    * @param int $uid     User for which the cookies are needed.
  899.    * @param string $name (Optional) A null parameter will get all cookies
  900.    *                     for the user.
  901.    *
  902.    * @return array  Cookies!  Nom nom nom nom nom.
  903.    */
  904.   public function data_getCookies($uid, $name) {
  905.     return $this->call_method('facebook.data.getCookies',
  906.         array('uid' => $uid,
  907.               'name' => $name));
  908.   }
  909.  
  910.   /**
  911.    * Sets cookies according to the params specified.
  912.    *
  913.    * @param int $uid       User for which the cookies are needed.
  914.    * @param string $name   Name of the cookie
  915.    * @param string $value  (Optional) if expires specified and is in the past
  916.    * @param int $expires   (Optional) Expiry time
  917.    * @param string $path   (Optional) Url path to associate with (default is /)
  918.    *
  919.    * @return bool  true on success
  920.    */
  921.   public function data_setCookie($uid, $name, $value, $expires, $path) {
  922.     return $this->call_method('facebook.data.setCookie',
  923.         array('uid' => $uid,
  924.               'name' => $name,
  925.               'value' => $value,
  926.               'expires' => $expires,
  927.               'path' => $path));
  928.   }
  929.  
  930.   /**
  931.    * Retrieves links posted by the given user.
  932.    *
  933.    * @param int    $uid      The user whose links you wish to retrieve
  934.    * @param int    $limit    The maximimum number of links to retrieve
  935.    * @param array $link_ids (Optional) Array of specific link
  936.    *                          IDs to retrieve by this user
  937.    *
  938.    * @return array  An array of links.
  939.    */
  940.   public function &links_get($uid, $limit, $link_ids = null) {
  941.     return $this->call_method('links.get',
  942.         array('uid' => $uid,
  943.               'limit' => $limit,
  944.               'link_ids' => json_encode($link_ids)));
  945.   }
  946.  
  947.   /**
  948.    * Posts a link on Facebook.
  949.    *
  950.    * @param string $url     URL/link you wish to post
  951.    * @param string $comment (Optional) A comment about this link
  952.    * @param int    $uid     (Optional) User ID that is posting this link;
  953.    *                        defaults to current session user
  954.    *
  955.    * @return bool
  956.    */
  957.   public function &links_post($url, $comment='', $uid = null) {
  958.     return $this->call_method('links.post',
  959.         array('uid' => $uid,
  960.               'url' => $url,
  961.               'comment' => $comment));
  962.   }
  963.  
  964.   /**
  965.    * Permissions API
  966.    */
  967.  
  968.   /**
  969.    * Checks API-access granted by self to the specified application.
  970.    *
  971.    * @param string $permissions_apikey  Other application key
  972.    *
  973.    * @return array  API methods/namespaces which are allowed access
  974.    */
  975.   public function permissions_checkGrantedApiAccess($permissions_apikey) {
  976.     return $this->call_method('facebook.permissions.checkGrantedApiAccess',
  977.         array('permissions_apikey' => $permissions_apikey));
  978.   }
  979.  
  980.   /**
  981.    * Checks API-access granted to self by the specified application.
  982.    *
  983.    * @param string $permissions_apikey  Other application key
  984.    *
  985.    * @return array  API methods/namespaces which are allowed access
  986.    */
  987.   public function permissions_checkAvailableApiAccess($permissions_apikey) {
  988.     return $this->call_method('facebook.permissions.checkAvailableApiAccess',
  989.         array('permissions_apikey' => $permissions_apikey));
  990.   }
  991.  
  992.   /**
  993.    * Grant API-access to the specified methods/namespaces to the specified
  994.    * application.
  995.    *
  996.    * @param string $permissions_apikey  Other application key
  997.    * @param array(string) $method_arr   (Optional) API methods/namespaces
  998.    *                                    allowed
  999.    *
  1000.    * @return array  API methods/namespaces which are allowed access
  1001.    */
  1002.   public function permissions_grantApiAccess($permissions_apikey, $method_arr) {
  1003.     return $this->call_method('facebook.permissions.grantApiAccess',
  1004.         array('permissions_apikey' => $permissions_apikey,
  1005.               'method_arr' => $method_arr));
  1006.   }
  1007.  
  1008.   /**
  1009.    * Revoke API-access granted to the specified application.
  1010.    *
  1011.    * @param string $permissions_apikey  Other application key
  1012.    *
  1013.    * @return bool  true on success
  1014.    */
  1015.   public function permissions_revokeApiAccess($permissions_apikey) {
  1016.     return $this->call_method('facebook.permissions.revokeApiAccess',
  1017.         array('permissions_apikey' => $permissions_apikey));
  1018.   }
  1019.  
  1020.   /**
  1021.    * Creates a note with the specified title and content.
  1022.    *
  1023.    * @param string $title   Title of the note.
  1024.    * @param string $content Content of the note.
  1025.    * @param int    $uid     (Optional) The user for whom you are creating a
  1026.    *                        note; defaults to current session user
  1027.    *
  1028.    * @return int   The ID of the note that was just created.
  1029.    */
  1030.   public function &notes_create($title, $content, $uid = null) {
  1031.     return $this->call_method('notes.create',
  1032.         array('uid' => $uid,
  1033.               'title' => $title,
  1034.               'content' => $content));
  1035.   }
  1036.  
  1037.   /**
  1038.    * Deletes the specified note.
  1039.    *
  1040.    * @param int $note_id  ID of the note you wish to delete
  1041.    * @param int $uid      (Optional) Owner of the note you wish to delete;
  1042.    *                      defaults to current session user
  1043.    *
  1044.    * @return bool
  1045.    */
  1046.   public function &notes_delete($note_id, $uid = null) {
  1047.     return $this->call_method('notes.delete',
  1048.         array('uid' => $uid,
  1049.               'note_id' => $note_id));
  1050.   }
  1051.  
  1052.   /**
  1053.    * Edits a note, replacing its title and contents with the title
  1054.    * and contents specified.
  1055.    *
  1056.    * @param int    $note_id  ID of the note you wish to edit
  1057.    * @param string $title    Replacement title for the note
  1058.    * @param string $content  Replacement content for the note
  1059.    * @param int    $uid      (Optional) Owner of the note you wish to edit;
  1060.    *                         defaults to current session user
  1061.    *
  1062.    * @return bool
  1063.    */
  1064.   public function &notes_edit($note_id, $title, $content, $uid = null) {
  1065.     return $this->call_method('notes.edit',
  1066.         array('uid' => $uid,
  1067.               'note_id' => $note_id,
  1068.               'title' => $title,
  1069.               'content' => $content));
  1070.   }
  1071.  
  1072.   /**
  1073.    * Retrieves all notes by a user. If note_ids are specified,
  1074.    * retrieves only those specific notes by that user.
  1075.    *
  1076.    * @param int    $uid      User whose notes you wish to retrieve
  1077.    * @param array  $note_ids (Optional) List of specific note
  1078.    *                         IDs by this user to retrieve
  1079.    *
  1080.    * @return array A list of all of the given user's notes, or an empty list
  1081.    *               if the viewer lacks permissions or if there are no visible
  1082.    *               notes.
  1083.    */
  1084.   public function &notes_get($uid, $note_ids = null) {
  1085.     return $this->call_method('notes.get',
  1086.         array('uid' => $uid,
  1087.               'note_ids' => json_encode($note_ids)));
  1088.   }
  1089.  
  1090.  
  1091.   /**
  1092.    * Returns the outstanding notifications for the session user.
  1093.    *
  1094.    * @return array An assoc array of notification count objects for
  1095.    *               'messages', 'pokes' and 'shares', a uid list of
  1096.    *               'friend_requests', a gid list of 'group_invites',
  1097.    *               and an eid list of 'event_invites'
  1098.    */
  1099.   public function &notifications_get() {
  1100.     return $this->call_method('facebook.notifications.get');
  1101.   }
  1102.  
  1103.   /**
  1104.    * Sends a notification to the specified users.
  1105.    *
  1106.    * @return A comma separated list of successful recipients
  1107.    * @error
  1108.    *    API_EC_PARAM_USER_ID_LIST
  1109.    */
  1110.   public function &notifications_send($to_ids, $notification, $type) {
  1111.     return $this->call_method('facebook.notifications.send',
  1112.         array('to_ids' => $to_ids,
  1113.               'notification' => $notification,
  1114.               'type' => $type));
  1115.   }
  1116.  
  1117.   /**
  1118.    * Sends an email to the specified user of the application.
  1119.    *
  1120.    * @param string $recipients comma-separated ids of the recipients
  1121.    * @param string $subject    subject of the email
  1122.    * @param string $text       (plain text) body of the email
  1123.    * @param string $fbml       fbml markup for an html version of the email
  1124.    *
  1125.    * @return string  A comma separated list of successful recipients
  1126.    * @error
  1127.    *    API_EC_PARAM_USER_ID_LIST
  1128.    */
  1129.   public function &notifications_sendEmail($recipients,
  1130.                                            $subject,
  1131.                                            $text,
  1132.                                            $fbml) {
  1133.     return $this->call_method('facebook.notifications.sendEmail',
  1134.         array('recipients' => $recipients,
  1135.               'subject' => $subject,
  1136.               'text' => $text,
  1137.               'fbml' => $fbml));
  1138.   }
  1139.  
  1140.   /**
  1141.    * Returns the requested info fields for the requested set of pages.
  1142.    *
  1143.    * @param string  $page_ids  a comma-separated list of page ids
  1144.    * @param string  $fields    a comma-separated list of strings describing the
  1145.    *                           info fields desired
  1146.    * @param int    $uid       (Optional) limit results to pages of which this
  1147.    *                          user is a fan.
  1148.    * @param string type       limits results to a particular type of page.
  1149.    *
  1150.    * @return array  An array of pages
  1151.    */
  1152.   public function &pages_getInfo($page_ids, $fields, $uid, $type) {
  1153.     return $this->call_method('facebook.pages.getInfo',
  1154.         array('page_ids' => $page_ids,
  1155.               'fields' => $fields,
  1156.               'uid' => $uid,
  1157.               'type' => $type));
  1158.   }
  1159.  
  1160.   /**
  1161.    * Returns true if the given user is an admin for the passed page.
  1162.    *
  1163.    * @param int $page_id  target page id
  1164.    * @param int $uid      (Optional) user id (defaults to the logged-in user)
  1165.    *
  1166.    * @return bool  true on success
  1167.    */
  1168.   public function &pages_isAdmin($page_id, $uid = null) {
  1169.     return $this->call_method('facebook.pages.isAdmin',
  1170.         array('page_id' => $page_id,
  1171.               'uid' => $uid));
  1172.   }
  1173.  
  1174.   /**
  1175.    * Returns whether or not the given page has added the application.
  1176.    *
  1177.    * @param int $page_id  target page id
  1178.    *
  1179.    * @return bool  true on success
  1180.    */
  1181.   public function &pages_isAppAdded($page_id) {
  1182.     return $this->call_method('facebook.pages.isAppAdded',
  1183.         array('page_id' => $page_id));
  1184.   }
  1185.  
  1186.   /**
  1187.    * Returns true if logged in user is a fan for the passed page.
  1188.    *
  1189.    * @param int $page_id target page id
  1190.    * @param int $uid user to compare.  If empty, the logged in user.
  1191.    *
  1192.    * @return bool  true on success
  1193.    */
  1194.   public function &pages_isFan($page_id, $uid = null) {
  1195.     return $this->call_method('facebook.pages.isFan',
  1196.         array('page_id' => $page_id,
  1197.               'uid' => $uid));
  1198.   }
  1199.  
  1200.   /**
  1201.    * Adds a tag with the given information to a photo. See the wiki for details:
  1202.    *
  1203.    *  http://wiki.developers.facebook.com/index.php/Photos.addTag
  1204.    *
  1205.    * @param int $pid          The ID of the photo to be tagged
  1206.    * @param int $tag_uid      The ID of the user being tagged. You must specify
  1207.    *                          either the $tag_uid or the $tag_text parameter
  1208.    *                          (unless $tags is specified).
  1209.    * @param string $tag_text  Some text identifying the person being tagged.
  1210.    *                          You must specify either the $tag_uid or $tag_text
  1211.    *                          parameter (unless $tags is specified).
  1212.    * @param float $x          The horizontal position of the tag, as a
  1213.    *                          percentage from 0 to 100, from the left of the
  1214.    *                          photo.
  1215.    * @param float $y          The vertical position of the tag, as a percentage
  1216.    *                          from 0 to 100, from the top of the photo.
  1217.    * @param array $tags       (Optional) An array of maps, where each map
  1218.    *                          can contain the tag_uid, tag_text, x, and y
  1219.    *                          parameters defined above.  If specified, the
  1220.    *                          individual arguments are ignored.
  1221.    * @param int $owner_uid    (Optional)  The user ID of the user whose photo
  1222.    *                          you are tagging. If this parameter is not
  1223.    *                          specified, then it defaults to the session user.
  1224.    *
  1225.    * @return bool  true on success
  1226.    */
  1227.   public function &photos_addTag($pid,
  1228.                                  $tag_uid,
  1229.                                  $tag_text,
  1230.                                  $x,
  1231.                                  $y,
  1232.                                  $tags,
  1233.                                  $owner_uid=0) {
  1234.     return $this->call_method('facebook.photos.addTag',
  1235.         array('pid' => $pid,
  1236.               'tag_uid' => $tag_uid,
  1237.               'tag_text' => $tag_text,
  1238.               'x' => $x,
  1239.               'y' => $y,
  1240.               'tags' => (is_array($tags)) ? json_encode($tags) : null,
  1241.               'owner_uid' => $this->get_uid($owner_uid)));
  1242.   }
  1243.  
  1244.   /**
  1245.    * Creates and returns a new album owned by the specified user or the current
  1246.    * session user.
  1247.    *
  1248.    * @param string $name         The name of the album.
  1249.    * @param string $description  (Optional) A description of the album.
  1250.    * @param string $location     (Optional) A description of the location.
  1251.    * @param string $visible      (Optional) A privacy setting for the album.
  1252.    *                             One of 'friends', 'friends-of-friends',
  1253.    *                             'networks', or 'everyone'.  Default 'everyone'.
  1254.    * @param int $uid             (Optional) User id for creating the album; if
  1255.    *                             not specified, the session user is used.
  1256.    *
  1257.    * @return array  An album object
  1258.    */
  1259.   public function &photos_createAlbum($name,
  1260.                                       $description='',
  1261.                                       $location='',
  1262.                                       $visible='',
  1263.                                       $uid=0) {
  1264.     return $this->call_method('facebook.photos.createAlbum',
  1265.         array('name' => $name,
  1266.               'description' => $description,
  1267.               'location' => $location,
  1268.               'visible' => $visible,
  1269.               'uid' => $this->get_uid($uid)));
  1270.   }
  1271.  
  1272.   /**
  1273.    * Returns photos according to the filters specified.
  1274.    *
  1275.    * @param int $subj_id  (Optional) Filter by uid of user tagged in the photos.
  1276.    * @param int $aid      (Optional) Filter by an album, as returned by
  1277.    *                      photos_getAlbums.
  1278.    * @param string $pids   (Optional) Restrict to a comma-separated list of pids
  1279.    *
  1280.    * Note that at least one of these parameters needs to be specified, or an
  1281.    * error is returned.
  1282.    *
  1283.    * @return array  An array of photo objects.
  1284.    */
  1285.   public function &photos_get($subj_id, $aid, $pids) {
  1286.     return $this->call_method('facebook.photos.get',
  1287.       array('subj_id' => $subj_id, 'aid' => $aid, 'pids' => $pids));
  1288.   }
  1289.  
  1290.   /**
  1291.    * Returns the albums created by the given user.
  1292.    *
  1293.    * @param int $uid      (Optional) The uid of the user whose albums you want.
  1294.    *                       A null will return the albums of the session user.
  1295.    * @param string $aids  (Optional) A comma-separated list of aids to restricti
  1296.    *                       the query.
  1297.    *
  1298.    * Note that at least one of the (uid, aids) parameters must be specified.
  1299.    *
  1300.    * @returns an array of album objects.
  1301.    */
  1302.   public function &photos_getAlbums($uid, $aids) {
  1303.     return $this->call_method('facebook.photos.getAlbums',
  1304.       array('uid' => $uid,
  1305.             'aids' => $aids));
  1306.   }
  1307.  
  1308.   /**
  1309.    * Returns the tags on all photos specified.
  1310.    *
  1311.    * @param string $pids  A list of pids to query
  1312.    *
  1313.    * @return array  An array of photo tag objects, which include pid,
  1314.    *                subject uid, and two floating-point numbers (xcoord, ycoord)
  1315.    *                for tag pixel location.
  1316.    */
  1317.   public function &photos_getTags($pids) {
  1318.     return $this->call_method('facebook.photos.getTags',
  1319.       array('pids' => $pids));
  1320.   }
  1321.  
  1322.   /**
  1323.    * Uploads a photo.
  1324.    *
  1325.    * @param string $file     The location of the photo on the local filesystem.
  1326.    * @param int $aid         (Optional) The album into which to upload the
  1327.    *                         photo.
  1328.    * @param string $caption  (Optional) A caption for the photo.
  1329.    * @param int uid          (Optional) The user ID of the user whose photo you
  1330.    *                         are uploading
  1331.    *
  1332.    * @return array  An array of user objects
  1333.    */
  1334.   public function photos_upload($file, $aid=null, $caption=null, $uid=null) {
  1335.     return $this->call_upload_method('facebook.photos.upload',
  1336.                                      array('aid' => $aid,
  1337.                                            'caption' => $caption,
  1338.                                            'uid' => $uid),
  1339.                                      $file);
  1340.   }
  1341.  
  1342.  
  1343.   /**
  1344.    * Uploads a video.
  1345.    *
  1346.    * @param  string $file        The location of the video on the local filesystem.
  1347.    * @param  string $title       (Optional) A title for the video. Titles over 65 characters in length will be truncated.
  1348.    * @param  string $description (Optional) A description for the video.
  1349.    *
  1350.    * @return array  An array with the video's ID, title, description, and a link to view it on Facebook.
  1351.    */
  1352.   public function video_upload($file, $title=null, $description=null) {
  1353.     return $this->call_upload_method('facebook.video.upload',
  1354.                                      array('title' => $title,
  1355.                                            'description' => $description),
  1356.                                      $file,
  1357.                                      Facebook::get_facebook_url('api-video') . '/restserver.php');
  1358.   }
  1359.  
  1360.   /**
  1361.    * Returns an array with the video limitations imposed on the current session's
  1362.    * associated user. Maximum length is measured in seconds; maximum size is
  1363.    * measured in bytes.
  1364.    *
  1365.    * @return array  Array with "length" and "size" keys
  1366.    */
  1367.   public function &video_getUploadLimits() {
  1368.     return $this->call_method('facebook.video.getUploadLimits');
  1369.   }
  1370.  
  1371.   /**
  1372.    * Returns the requested info fields for the requested set of users.
  1373.    *
  1374.    * @param string $uids    A comma-separated list of user ids
  1375.    * @param string $fields  A comma-separated list of info field names desired
  1376.    *
  1377.    * @return array  An array of user objects
  1378.    */
  1379.   public function &users_getInfo($uids, $fields) {
  1380.     return $this->call_method('facebook.users.getInfo',
  1381.         array('uids' => $uids, 'fields' => $fields));
  1382.   }
  1383.  
  1384.   /**
  1385.    * Returns the requested info fields for the requested set of users. A
  1386.    * session key must not be specified. Only data about users that have
  1387.    * authorized your application will be returned.
  1388.    *
  1389.    * Check the wiki for fields that can be queried through this API call.
  1390.    * Data returned from here should not be used for rendering to application
  1391.    * users, use users.getInfo instead, so that proper privacy rules will be
  1392.    * applied.
  1393.    *
  1394.    * @param string $uids    A comma-separated list of user ids
  1395.    * @param string $fields  A comma-separated list of info field names desired
  1396.    *
  1397.    * @return array  An array of user objects
  1398.    */
  1399.   public function &users_getStandardInfo($uids, $fields) {
  1400.     return $this->call_method('facebook.users.getStandardInfo',
  1401.         array('uids' => $uids, 'fields' => $fields));
  1402.   }
  1403.  
  1404.   /**
  1405.    * Returns the user corresponding to the current session object.
  1406.    *
  1407.    * @return integer  User id
  1408.    */
  1409.   public function &users_getLoggedInUser() {
  1410.     return $this->call_method('facebook.users.getLoggedInUser');
  1411.   }
  1412.  
  1413.   /**
  1414.    * Returns 1 if the user has the specified permission, 0 otherwise.
  1415.    * http://wiki.developers.facebook.com/index.php/Users.hasAppPermission
  1416.    *
  1417.    * @return integer  1 or 0
  1418.    */
  1419.   public function &users_hasAppPermission($ext_perm, $uid=null) {
  1420.     return $this->call_method('facebook.users.hasAppPermission',
  1421.         array('ext_perm' => $ext_perm, 'uid' => $uid));
  1422.   }
  1423.  
  1424.   /**
  1425.    * Returns whether or not the user corresponding to the current
  1426.    * session object has the give the app basic authorization.
  1427.    *
  1428.    * @return boolean  true if the user has authorized the app
  1429.    */
  1430.   public function &users_isAppUser($uid=null) {
  1431.     if ($uid === null && isset($this->is_user)) {
  1432.       return $this->is_user;
  1433.     }
  1434.  
  1435.     return $this->call_method('facebook.users.isAppUser', array('uid' => $uid));
  1436.   }
  1437.  
  1438.   /**
  1439.    * Returns whether or not the user corresponding to the current
  1440.    * session object is verified by Facebook. See the documentation
  1441.    * for Users.isVerified for details.
  1442.    *
  1443.    * @return boolean  true if the user is verified
  1444.    */
  1445.   public function &users_isVerified() {
  1446.     return $this->call_method('facebook.users.isVerified');
  1447.   }
  1448.  
  1449.   /**
  1450.    * Sets the users' current status message. Message does NOT contain the
  1451.    * word "is" , so make sure to include a verb.
  1452.    *
  1453.    * Example: setStatus("is loving the API!")
  1454.    * will produce the status "Luke is loving the API!"
  1455.    *
  1456.    * @param string $status                text-only message to set
  1457.    * @param int    $uid                   user to set for (defaults to the
  1458.    *                                      logged-in user)
  1459.    * @param bool   $clear                 whether or not to clear the status,
  1460.    *                                      instead of setting it
  1461.    * @param bool   $status_includes_verb  if true, the word "is" will *not* be
  1462.    *                                      prepended to the status message
  1463.    *
  1464.    * @return boolean
  1465.    */
  1466.   public function &users_setStatus($status,
  1467.                                    $uid = null,
  1468.                                    $clear = false,
  1469.                                    $status_includes_verb = true) {
  1470.     $args = array(
  1471.       'status' => $status,
  1472.       'uid' => $uid,
  1473.       'clear' => $clear,
  1474.       'status_includes_verb' => $status_includes_verb,
  1475.     );
  1476.     return $this->call_method('facebook.users.setStatus', $args);
  1477.   }
  1478.  
  1479.   /**
  1480.    * Sets the FBML for the profile of the user attached to this session.
  1481.    *
  1482.    * @param   string   $markup           The FBML that describes the profile
  1483.    *                                     presence of this app for the user
  1484.    * @param   int      $uid              The user
  1485.    * @param   string   $profile          Profile FBML
  1486.    * @param   string   $profile_action   Profile action FBML (deprecated)
  1487.    * @param   string   $mobile_profile   Mobile profile FBML
  1488.    * @param   string   $profile_main     Main Tab profile FBML
  1489.    *
  1490.    * @return  array  A list of strings describing any compile errors for the
  1491.    *                 submitted FBML
  1492.    */
  1493.   function profile_setFBML($markup,
  1494.                            $uid=null,
  1495.                            $profile='',
  1496.                            $profile_action='',
  1497.                            $mobile_profile='',
  1498.                            $profile_main='') {
  1499.     return $this->call_method('facebook.profile.setFBML',
  1500.         array('markup' => $markup,
  1501.               'uid' => $uid,
  1502.               'profile' => $profile,
  1503.               'profile_action' => $profile_action,
  1504.               'mobile_profile' => $mobile_profile,
  1505.               'profile_main' => $profile_main));
  1506.   }
  1507.  
  1508.   /**
  1509.    * Gets the FBML for the profile box that is currently set for a user's
  1510.    * profile (your application set the FBML previously by calling the
  1511.    * profile.setFBML method).
  1512.    *
  1513.    * @param int $uid   (Optional) User id to lookup; defaults to session.
  1514.    * @param int $type  (Optional) 1 for original style, 2 for profile_main boxes
  1515.    *
  1516.    * @return string  The FBML
  1517.    */
  1518.   public function &profile_getFBML($uid=null, $type=null) {
  1519.     return $this->call_method('facebook.profile.getFBML',
  1520.         array('uid' => $uid,
  1521.               'type' => $type));
  1522.   }
  1523.  
  1524.   /**
  1525.    * Returns the specified user's application info section for the calling
  1526.    * application. These info sections have either been set via a previous
  1527.    * profile.setInfo call or by the user editing them directly.
  1528.    *
  1529.    * @param int $uid  (Optional) User id to lookup; defaults to session.
  1530.    *
  1531.    * @return array  Info fields for the current user.  See wiki for structure:
  1532.    *
  1533.    *  http://wiki.developers.facebook.com/index.php/Profile.getInfo
  1534.    *
  1535.    */
  1536.   public function &profile_getInfo($uid=null) {
  1537.     return $this->call_method('facebook.profile.getInfo',
  1538.         array('uid' => $uid));
  1539.   }
  1540.  
  1541.   /**
  1542.    * Returns the options associated with the specified info field for an
  1543.    * application info section.
  1544.    *
  1545.    * @param string $field  The title of the field
  1546.    *
  1547.    * @return array  An array of info options.
  1548.    */
  1549.   public function &profile_getInfoOptions($field) {
  1550.     return $this->call_method('facebook.profile.getInfoOptions',
  1551.         array('field' => $field));
  1552.   }
  1553.  
  1554.   /**
  1555.    * Configures an application info section that the specified user can install
  1556.    * on the Info tab of her profile.  For details on the structure of an info
  1557.    * field, please see:
  1558.    *
  1559.    *  http://wiki.developers.facebook.com/index.php/Profile.setInfo
  1560.    *
  1561.    * @param string $title       Title / header of the info section
  1562.    * @param int $type           1 for text-only, 5 for thumbnail views
  1563.    * @param array $info_fields  An array of info fields. See wiki for details.
  1564.    * @param int $uid            (Optional)
  1565.    *
  1566.    * @return bool  true on success
  1567.    */
  1568.   public function &profile_setInfo($title, $type, $info_fields, $uid=null) {
  1569.     return $this->call_method('facebook.profile.setInfo',
  1570.         array('uid' => $uid,
  1571.               'type' => $type,
  1572.               'title'   => $title,
  1573.               'info_fields' => json_encode($info_fields)));
  1574.   }
  1575.  
  1576.   /**
  1577.    * Specifies the objects for a field for an application info section. These
  1578.    * options populate the typeahead for a thumbnail.
  1579.    *
  1580.    * @param string $field   The title of the field
  1581.    * @param array $options  An array of items for a thumbnail, including
  1582.    *                        'label', 'link', and optionally 'image',
  1583.    *                        'description' and 'sublabel'
  1584.    *
  1585.    * @return bool  true on success
  1586.    */
  1587.   public function profile_setInfoOptions($field, $options) {
  1588.     return $this->call_method('facebook.profile.setInfoOptions',
  1589.         array('field'   => $field,
  1590.               'options' => json_encode($options)));
  1591.   }
  1592.  
  1593.   /**
  1594.    * Get all the marketplace categories.
  1595.    *
  1596.    * @return array  A list of category names
  1597.    */
  1598.   function marketplace_getCategories() {
  1599.     return $this->call_method('facebook.marketplace.getCategories',
  1600.         array());
  1601.   }
  1602.  
  1603.   /**
  1604.    * Get all the marketplace subcategories for a particular category.
  1605.    *
  1606.    * @param  category  The category for which we are pulling subcategories
  1607.    *
  1608.    * @return array A list of subcategory names
  1609.    */
  1610.   function marketplace_getSubCategories($category) {
  1611.     return $this->call_method('facebook.marketplace.getSubCategories',
  1612.         array('category' => $category));
  1613.   }
  1614.  
  1615.   /**
  1616.    * Get listings by either listing_id or user.
  1617.    *
  1618.    * @param listing_ids   An array of listing_ids (optional)
  1619.    * @param uids          An array of user ids (optional)
  1620.    *
  1621.    * @return array  The data for matched listings
  1622.    */
  1623.   function marketplace_getListings($listing_ids, $uids) {
  1624.     return $this->call_method('facebook.marketplace.getListings',
  1625.         array('listing_ids' => $listing_ids, 'uids' => $uids));
  1626.   }
  1627.  
  1628.   /**
  1629.    * Search for Marketplace listings.  All arguments are optional, though at
  1630.    * least one must be filled out to retrieve results.
  1631.    *
  1632.    * @param category     The category in which to search (optional)
  1633.    * @param subcategory  The subcategory in which to search (optional)
  1634.    * @param query        A query string (optional)
  1635.    *
  1636.    * @return array  The data for matched listings
  1637.    */
  1638.   function marketplace_search($category, $subcategory, $query) {
  1639.     return $this->call_method('facebook.marketplace.search',
  1640.         array('category' => $category,
  1641.               'subcategory' => $subcategory,
  1642.               'query' => $query));
  1643.   }
  1644.  
  1645.   /**
  1646.    * Remove a listing from Marketplace.
  1647.    *
  1648.    * @param listing_id  The id of the listing to be removed
  1649.    * @param status      'SUCCESS', 'NOT_SUCCESS', or 'DEFAULT'
  1650.    *
  1651.    * @return bool  True on success
  1652.    */
  1653.   function marketplace_removeListing($listing_id,
  1654.                                      $status='DEFAULT',
  1655.                                      $uid=null) {
  1656.     return $this->call_method('facebook.marketplace.removeListing',
  1657.         array('listing_id' => $listing_id,
  1658.               'status' => $status,
  1659.               'uid' => $uid));
  1660.   }
  1661.  
  1662.   /**
  1663.    * Create/modify a Marketplace listing for the loggedinuser.
  1664.    *
  1665.    * @param int              listing_id  The id of a listing to be modified, 0
  1666.    *                                     for a new listing.
  1667.    * @param show_on_profile  bool        Should we show this listing on the
  1668.    *                                     user's profile
  1669.    * @param listing_attrs    array       An array of the listing data
  1670.    *
  1671.    * @return int  The listing_id (unchanged if modifying an existing listing).
  1672.    */
  1673.   function marketplace_createListing($listing_id,
  1674.                                      $show_on_profile,
  1675.                                      $attrs,
  1676.                                      $uid=null) {
  1677.     return $this->call_method('facebook.marketplace.createListing',
  1678.         array('listing_id' => $listing_id,
  1679.               'show_on_profile' => $show_on_profile,
  1680.               'listing_attrs' => json_encode($attrs),
  1681.               'uid' => $uid));
  1682.   }
  1683.  
  1684.   /////////////////////////////////////////////////////////////////////////////
  1685.   // Data Store API
  1686.  
  1687.   /**
  1688.    * Set a user preference.
  1689.    *
  1690.    * @param  pref_id    preference identifier (0-200)
  1691.    * @param  value      preferece's value
  1692.    * @param  uid        the user id (defaults to current session user)
  1693.    * @error
  1694.    *    API_EC_DATA_DATABASE_ERROR
  1695.    *    API_EC_PARAM
  1696.    *    API_EC_DATA_QUOTA_EXCEEDED
  1697.    *    API_EC_DATA_UNKNOWN_ERROR
  1698.    *    API_EC_PERMISSION_OTHER_USER
  1699.    */
  1700.   public function &data_setUserPreference($pref_id, $value, $uid = null) {
  1701.     return $this->call_method('facebook.data.setUserPreference',
  1702.        array('pref_id' => $pref_id,
  1703.              'value' => $value,
  1704.              'uid' => $this->get_uid($uid)));
  1705.   }
  1706.  
  1707.   /**
  1708.    * Set a user's all preferences for this application.
  1709.    *
  1710.    * @param  values     preferece values in an associative arrays
  1711.    * @param  replace    whether to replace all existing preferences or
  1712.    *                    merge into them.
  1713.    * @param  uid        the user id (defaults to current session user)
  1714.    * @error
  1715.    *    API_EC_DATA_DATABASE_ERROR
  1716.    *    API_EC_PARAM
  1717.    *    API_EC_DATA_QUOTA_EXCEEDED
  1718.    *    API_EC_DATA_UNKNOWN_ERROR
  1719.    *    API_EC_PERMISSION_OTHER_USER
  1720.    */
  1721.   public function &data_setUserPreferences($values,
  1722.                                            $replace = false,
  1723.                                            $uid = null) {
  1724.     return $this->call_method('facebook.data.setUserPreferences',
  1725.        array('values' => json_encode($values),
  1726.              'replace' => $replace,
  1727.              'uid' => $this->get_uid($uid)));
  1728.   }
  1729.  
  1730.   /**
  1731.    * Get a user preference.
  1732.    *
  1733.    * @param  pref_id    preference identifier (0-200)
  1734.    * @param  uid        the user id (defaults to current session user)
  1735.    * @return            preference's value
  1736.    * @error
  1737.    *    API_EC_DATA_DATABASE_ERROR
  1738.    *    API_EC_PARAM
  1739.    *    API_EC_DATA_QUOTA_EXCEEDED
  1740.    *    API_EC_DATA_UNKNOWN_ERROR
  1741.    *    API_EC_PERMISSION_OTHER_USER
  1742.    */
  1743.   public function &data_getUserPreference($pref_id, $uid = null) {
  1744.     return $this->call_method('facebook.data.getUserPreference',
  1745.        array('pref_id' => $pref_id,
  1746.              'uid' => $this->get_uid($uid)));
  1747.   }
  1748.  
  1749.   /**
  1750.    * Get a user preference.
  1751.    *
  1752.    * @param  uid        the user id (defaults to current session user)
  1753.    * @return            preference values
  1754.    * @error
  1755.    *    API_EC_DATA_DATABASE_ERROR
  1756.    *    API_EC_DATA_QUOTA_EXCEEDED
  1757.    *    API_EC_DATA_UNKNOWN_ERROR
  1758.    *    API_EC_PERMISSION_OTHER_USER
  1759.    */
  1760.   public function &data_getUserPreferences($uid = null) {
  1761.     return $this->call_method('facebook.data.getUserPreferences',
  1762.        array('uid' => $this->get_uid($uid)));
  1763.   }
  1764.  
  1765.   /**
  1766.    * Create a new object type.
  1767.    *
  1768.    * @param  name       object type's name
  1769.    * @error
  1770.    *    API_EC_DATA_DATABASE_ERROR
  1771.    *    API_EC_DATA_OBJECT_ALREADY_EXISTS
  1772.    *    API_EC_PARAM
  1773.    *    API_EC_PERMISSION
  1774.    *    API_EC_DATA_INVALID_OPERATION
  1775.    *    API_EC_DATA_QUOTA_EXCEEDED
  1776.    *    API_EC_DATA_UNKNOWN_ERROR
  1777.    */
  1778.   public function &data_createObjectType($name) {
  1779.     return $this->call_method('facebook.data.createObjectType',
  1780.        array('name' => $name));
  1781.   }
  1782.  
  1783.   /**
  1784.    * Delete an object type.
  1785.    *
  1786.    * @param  obj_type       object type's name
  1787.    * @error
  1788.    *    API_EC_DATA_DATABASE_ERROR
  1789.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1790.    *    API_EC_PARAM
  1791.    *    API_EC_PERMISSION
  1792.    *    API_EC_DATA_INVALID_OPERATION
  1793.    *    API_EC_DATA_QUOTA_EXCEEDED
  1794.    *    API_EC_DATA_UNKNOWN_ERROR
  1795.    */
  1796.   public function &data_dropObjectType($obj_type) {
  1797.     return $this->call_method('facebook.data.dropObjectType',
  1798.        array('obj_type' => $obj_type));
  1799.   }
  1800.  
  1801.   /**
  1802.    * Rename an object type.
  1803.    *
  1804.    * @param  obj_type       object type's name
  1805.    * @param  new_name       new object type's name
  1806.    * @error
  1807.    *    API_EC_DATA_DATABASE_ERROR
  1808.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1809.    *    API_EC_DATA_OBJECT_ALREADY_EXISTS
  1810.    *    API_EC_PARAM
  1811.    *    API_EC_PERMISSION
  1812.    *    API_EC_DATA_INVALID_OPERATION
  1813.    *    API_EC_DATA_QUOTA_EXCEEDED
  1814.    *    API_EC_DATA_UNKNOWN_ERROR
  1815.    */
  1816.   public function &data_renameObjectType($obj_type, $new_name) {
  1817.     return $this->call_method('facebook.data.renameObjectType',
  1818.        array('obj_type' => $obj_type,
  1819.              'new_name' => $new_name));
  1820.   }
  1821.  
  1822.   /**
  1823.    * Add a new property to an object type.
  1824.    *
  1825.    * @param  obj_type       object type's name
  1826.    * @param  prop_name      name of the property to add
  1827.    * @param  prop_type      1: integer; 2: string; 3: text blob
  1828.    * @error
  1829.    *    API_EC_DATA_DATABASE_ERROR
  1830.    *    API_EC_DATA_OBJECT_ALREADY_EXISTS
  1831.    *    API_EC_PARAM
  1832.    *    API_EC_PERMISSION
  1833.    *    API_EC_DATA_INVALID_OPERATION
  1834.    *    API_EC_DATA_QUOTA_EXCEEDED
  1835.    *    API_EC_DATA_UNKNOWN_ERROR
  1836.    */
  1837.   public function &data_defineObjectProperty($obj_type,
  1838.                                              $prop_name,
  1839.                                              $prop_type) {
  1840.     return $this->call_method('facebook.data.defineObjectProperty',
  1841.        array('obj_type' => $obj_type,
  1842.              'prop_name' => $prop_name,
  1843.              'prop_type' => $prop_type));
  1844.   }
  1845.  
  1846.   /**
  1847.    * Remove a previously defined property from an object type.
  1848.    *
  1849.    * @param  obj_type      object type's name
  1850.    * @param  prop_name     name of the property to remove
  1851.    * @error
  1852.    *    API_EC_DATA_DATABASE_ERROR
  1853.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1854.    *    API_EC_PARAM
  1855.    *    API_EC_PERMISSION
  1856.    *    API_EC_DATA_INVALID_OPERATION
  1857.    *    API_EC_DATA_QUOTA_EXCEEDED
  1858.    *    API_EC_DATA_UNKNOWN_ERROR
  1859.    */
  1860.   public function &data_undefineObjectProperty($obj_type, $prop_name) {
  1861.     return $this->call_method('facebook.data.undefineObjectProperty',
  1862.        array('obj_type' => $obj_type,
  1863.              'prop_name' => $prop_name));
  1864.   }
  1865.  
  1866.   /**
  1867.    * Rename a previously defined property of an object type.
  1868.    *
  1869.    * @param  obj_type      object type's name
  1870.    * @param  prop_name     name of the property to rename
  1871.    * @param  new_name      new name to use
  1872.    * @error
  1873.    *    API_EC_DATA_DATABASE_ERROR
  1874.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1875.    *    API_EC_DATA_OBJECT_ALREADY_EXISTS
  1876.    *    API_EC_PARAM
  1877.    *    API_EC_PERMISSION
  1878.    *    API_EC_DATA_INVALID_OPERATION
  1879.    *    API_EC_DATA_QUOTA_EXCEEDED
  1880.    *    API_EC_DATA_UNKNOWN_ERROR
  1881.    */
  1882.   public function &data_renameObjectProperty($obj_type, $prop_name,
  1883.                                             $new_name) {
  1884.     return $this->call_method('facebook.data.renameObjectProperty',
  1885.        array('obj_type' => $obj_type,
  1886.              'prop_name' => $prop_name,
  1887.              'new_name' => $new_name));
  1888.   }
  1889.  
  1890.   /**
  1891.    * Retrieve a list of all object types that have defined for the application.
  1892.    *
  1893.    * @return               a list of object type names
  1894.    * @error
  1895.    *    API_EC_DATA_DATABASE_ERROR
  1896.    *    API_EC_PERMISSION
  1897.    *    API_EC_DATA_QUOTA_EXCEEDED
  1898.    *    API_EC_DATA_UNKNOWN_ERROR
  1899.    */
  1900.   public function &data_getObjectTypes() {
  1901.     return $this->call_method('facebook.data.getObjectTypes');
  1902.   }
  1903.  
  1904.   /**
  1905.    * Get definitions of all properties of an object type.
  1906.    *
  1907.    * @param obj_type       object type's name
  1908.    * @return               pairs of property name and property types
  1909.    * @error
  1910.    *    API_EC_DATA_DATABASE_ERROR
  1911.    *    API_EC_PARAM
  1912.    *    API_EC_PERMISSION
  1913.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1914.    *    API_EC_DATA_QUOTA_EXCEEDED
  1915.    *    API_EC_DATA_UNKNOWN_ERROR
  1916.    */
  1917.   public function &data_getObjectType($obj_type) {
  1918.     return $this->call_method('facebook.data.getObjectType',
  1919.        array('obj_type' => $obj_type));
  1920.   }
  1921.  
  1922.   /**
  1923.    * Create a new object.
  1924.    *
  1925.    * @param  obj_type      object type's name
  1926.    * @param  properties    (optional) properties to set initially
  1927.    * @return               newly created object's id
  1928.    * @error
  1929.    *    API_EC_DATA_DATABASE_ERROR
  1930.    *    API_EC_PARAM
  1931.    *    API_EC_PERMISSION
  1932.    *    API_EC_DATA_INVALID_OPERATION
  1933.    *    API_EC_DATA_QUOTA_EXCEEDED
  1934.    *    API_EC_DATA_UNKNOWN_ERROR
  1935.    */
  1936.   public function &data_createObject($obj_type, $properties = null) {
  1937.     return $this->call_method('facebook.data.createObject',
  1938.        array('obj_type' => $obj_type,
  1939.              'properties' => json_encode($properties)));
  1940.   }
  1941.  
  1942.   /**
  1943.    * Update an existing object.
  1944.    *
  1945.    * @param  obj_id        object's id
  1946.    * @param  properties    new properties
  1947.    * @param  replace       true for replacing existing properties;
  1948.    *                       false for merging
  1949.    * @error
  1950.    *    API_EC_DATA_DATABASE_ERROR
  1951.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1952.    *    API_EC_PARAM
  1953.    *    API_EC_PERMISSION
  1954.    *    API_EC_DATA_INVALID_OPERATION
  1955.    *    API_EC_DATA_QUOTA_EXCEEDED
  1956.    *    API_EC_DATA_UNKNOWN_ERROR
  1957.    */
  1958.   public function &data_updateObject($obj_id, $properties, $replace = false) {
  1959.     return $this->call_method('facebook.data.updateObject',
  1960.        array('obj_id' => $obj_id,
  1961.              'properties' => json_encode($properties),
  1962.              'replace' => $replace));
  1963.   }
  1964.  
  1965.   /**
  1966.    * Delete an existing object.
  1967.    *
  1968.    * @param  obj_id        object's id
  1969.    * @error
  1970.    *    API_EC_DATA_DATABASE_ERROR
  1971.    *    API_EC_DATA_OBJECT_NOT_FOUND
  1972.    *    API_EC_PARAM
  1973.    *    API_EC_PERMISSION
  1974.    *    API_EC_DATA_INVALID_OPERATION
  1975.    *    API_EC_DATA_QUOTA_EXCEEDED
  1976.    *    API_EC_DATA_UNKNOWN_ERROR
  1977.    */
  1978.   public function &data_deleteObject($obj_id) {
  1979.     return $this->call_method('facebook.data.deleteObject',
  1980.        array('obj_id' => $obj_id));
  1981.   }
  1982.  
  1983.   /**
  1984.    * Delete a list of objects.
  1985.    *
  1986.    * @param  obj_ids       objects to delete
  1987.    * @error
  1988.    *    API_EC_DATA_DATABASE_ERROR
  1989.    *    API_EC_PARAM
  1990.    *    API_EC_PERMISSION
  1991.    *    API_EC_DATA_INVALID_OPERATION
  1992.    *    API_EC_DATA_QUOTA_EXCEEDED
  1993.    *    API_EC_DATA_UNKNOWN_ERROR
  1994.    */
  1995.   public function &data_deleteObjects($obj_ids) {
  1996.     return $this->call_method('facebook.data.deleteObjects',
  1997.        array('obj_ids' => json_encode($obj_ids)));
  1998.   }
  1999.  
  2000.   /**
  2001.    * Get a single property value of an object.
  2002.    *
  2003.    * @param  obj_id        object's id
  2004.    * @param  prop_name     individual property's name
  2005.    * @return               individual property's value
  2006.    * @error
  2007.    *    API_EC_DATA_DATABASE_ERROR
  2008.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2009.    *    API_EC_PARAM
  2010.    *    API_EC_PERMISSION
  2011.    *    API_EC_DATA_INVALID_OPERATION
  2012.    *    API_EC_DATA_QUOTA_EXCEEDED
  2013.    *    API_EC_DATA_UNKNOWN_ERROR
  2014.    */
  2015.   public function &data_getObjectProperty($obj_id, $prop_name) {
  2016.     return $this->call_method('facebook.data.getObjectProperty',
  2017.        array('obj_id' => $obj_id,
  2018.              'prop_name' => $prop_name));
  2019.   }
  2020.  
  2021.   /**
  2022.    * Get properties of an object.
  2023.    *
  2024.    * @param  obj_id      object's id
  2025.    * @param  prop_names  (optional) properties to return; null for all.
  2026.    * @return             specified properties of an object
  2027.    * @error
  2028.    *    API_EC_DATA_DATABASE_ERROR
  2029.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2030.    *    API_EC_PARAM
  2031.    *    API_EC_PERMISSION
  2032.    *    API_EC_DATA_INVALID_OPERATION
  2033.    *    API_EC_DATA_QUOTA_EXCEEDED
  2034.    *    API_EC_DATA_UNKNOWN_ERROR
  2035.    */
  2036.   public function &data_getObject($obj_id, $prop_names = null) {
  2037.     return $this->call_method('facebook.data.getObject',
  2038.        array('obj_id' => $obj_id,
  2039.              'prop_names' => json_encode($prop_names)));
  2040.   }
  2041.  
  2042.   /**
  2043.    * Get properties of a list of objects.
  2044.    *
  2045.    * @param  obj_ids     object ids
  2046.    * @param  prop_names  (optional) properties to return; null for all.
  2047.    * @return             specified properties of an object
  2048.    * @error
  2049.    *    API_EC_DATA_DATABASE_ERROR
  2050.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2051.    *    API_EC_PARAM
  2052.    *    API_EC_PERMISSION
  2053.    *    API_EC_DATA_INVALID_OPERATION
  2054.    *    API_EC_DATA_QUOTA_EXCEEDED
  2055.    *    API_EC_DATA_UNKNOWN_ERROR
  2056.    */
  2057.   public function &data_getObjects($obj_ids, $prop_names = null) {
  2058.     return $this->call_method('facebook.data.getObjects',
  2059.        array('obj_ids' => json_encode($obj_ids),
  2060.              'prop_names' => json_encode($prop_names)));
  2061.   }
  2062.  
  2063.   /**
  2064.    * Set a single property value of an object.
  2065.    *
  2066.    * @param  obj_id        object's id
  2067.    * @param  prop_name     individual property's name
  2068.    * @param  prop_value    new value to set
  2069.    * @error
  2070.    *    API_EC_DATA_DATABASE_ERROR
  2071.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2072.    *    API_EC_PARAM
  2073.    *    API_EC_PERMISSION
  2074.    *    API_EC_DATA_INVALID_OPERATION
  2075.    *    API_EC_DATA_QUOTA_EXCEEDED
  2076.    *    API_EC_DATA_UNKNOWN_ERROR
  2077.    */
  2078.   public function &data_setObjectProperty($obj_id, $prop_name,
  2079.                                          $prop_value) {
  2080.     return $this->call_method('facebook.data.setObjectProperty',
  2081.        array('obj_id' => $obj_id,
  2082.              'prop_name' => $prop_name,
  2083.              'prop_value' => $prop_value));
  2084.   }
  2085.  
  2086.   /**
  2087.    * Read hash value by key.
  2088.    *
  2089.    * @param  obj_type      object type's name
  2090.    * @param  key           hash key
  2091.    * @param  prop_name     (optional) individual property's name
  2092.    * @return               hash value
  2093.    * @error
  2094.    *    API_EC_DATA_DATABASE_ERROR
  2095.    *    API_EC_PARAM
  2096.    *    API_EC_PERMISSION
  2097.    *    API_EC_DATA_INVALID_OPERATION
  2098.    *    API_EC_DATA_QUOTA_EXCEEDED
  2099.    *    API_EC_DATA_UNKNOWN_ERROR
  2100.    */
  2101.   public function &data_getHashValue($obj_type, $key, $prop_name = null) {
  2102.     return $this->call_method('facebook.data.getHashValue',
  2103.        array('obj_type' => $obj_type,
  2104.              'key' => $key,
  2105.              'prop_name' => $prop_name));
  2106.   }
  2107.  
  2108.   /**
  2109.    * Write hash value by key.
  2110.    *
  2111.    * @param  obj_type      object type's name
  2112.    * @param  key           hash key
  2113.    * @param  value         hash value
  2114.    * @param  prop_name     (optional) individual property's name
  2115.    * @error
  2116.    *    API_EC_DATA_DATABASE_ERROR
  2117.    *    API_EC_PARAM
  2118.    *    API_EC_PERMISSION
  2119.    *    API_EC_DATA_INVALID_OPERATION
  2120.    *    API_EC_DATA_QUOTA_EXCEEDED
  2121.    *    API_EC_DATA_UNKNOWN_ERROR
  2122.    */
  2123.   public function &data_setHashValue($obj_type,
  2124.                                      $key,
  2125.                                      $value,
  2126.                                      $prop_name = null) {
  2127.     return $this->call_method('facebook.data.setHashValue',
  2128.        array('obj_type' => $obj_type,
  2129.              'key' => $key,
  2130.              'value' => $value,
  2131.              'prop_name' => $prop_name));
  2132.   }
  2133.  
  2134.   /**
  2135.    * Increase a hash value by specified increment atomically.
  2136.    *
  2137.    * @param  obj_type      object type's name
  2138.    * @param  key           hash key
  2139.    * @param  prop_name     individual property's name
  2140.    * @param  increment     (optional) default is 1
  2141.    * @return               incremented hash value
  2142.    * @error
  2143.    *    API_EC_DATA_DATABASE_ERROR
  2144.    *    API_EC_PARAM
  2145.    *    API_EC_PERMISSION
  2146.    *    API_EC_DATA_INVALID_OPERATION
  2147.    *    API_EC_DATA_QUOTA_EXCEEDED
  2148.    *    API_EC_DATA_UNKNOWN_ERROR
  2149.    */
  2150.   public function &data_incHashValue($obj_type,
  2151.                                      $key,
  2152.                                      $prop_name,
  2153.                                      $increment = 1) {
  2154.     return $this->call_method('facebook.data.incHashValue',
  2155.        array('obj_type' => $obj_type,
  2156.              'key' => $key,
  2157.              'prop_name' => $prop_name,
  2158.              'increment' => $increment));
  2159.   }
  2160.  
  2161.   /**
  2162.    * Remove a hash key and its values.
  2163.    *
  2164.    * @param  obj_type    object type's name
  2165.    * @param  key         hash key
  2166.    * @error
  2167.    *    API_EC_DATA_DATABASE_ERROR
  2168.    *    API_EC_PARAM
  2169.    *    API_EC_PERMISSION
  2170.    *    API_EC_DATA_INVALID_OPERATION
  2171.    *    API_EC_DATA_QUOTA_EXCEEDED
  2172.    *    API_EC_DATA_UNKNOWN_ERROR
  2173.    */
  2174.   public function &data_removeHashKey($obj_type, $key) {
  2175.     return $this->call_method('facebook.data.removeHashKey',
  2176.        array('obj_type' => $obj_type,
  2177.              'key' => $key));
  2178.   }
  2179.  
  2180.   /**
  2181.    * Remove hash keys and their values.
  2182.    *
  2183.    * @param  obj_type    object type's name
  2184.    * @param  keys        hash keys
  2185.    * @error
  2186.    *    API_EC_DATA_DATABASE_ERROR
  2187.    *    API_EC_PARAM
  2188.    *    API_EC_PERMISSION
  2189.    *    API_EC_DATA_INVALID_OPERATION
  2190.    *    API_EC_DATA_QUOTA_EXCEEDED
  2191.    *    API_EC_DATA_UNKNOWN_ERROR
  2192.    */
  2193.   public function &data_removeHashKeys($obj_type, $keys) {
  2194.     return $this->call_method('facebook.data.removeHashKeys',
  2195.        array('obj_type' => $obj_type,
  2196.              'keys' => json_encode($keys)));
  2197.   }
  2198.  
  2199.   /**
  2200.    * Define an object association.
  2201.    *
  2202.    * @param  name        name of this association
  2203.    * @param  assoc_type  1: one-way 2: two-way symmetric 3: two-way asymmetric
  2204.    * @param  assoc_info1 needed info about first object type
  2205.    * @param  assoc_info2 needed info about second object type
  2206.    * @param  inverse     (optional) name of reverse association
  2207.    * @error
  2208.    *    API_EC_DATA_DATABASE_ERROR
  2209.    *    API_EC_DATA_OBJECT_ALREADY_EXISTS
  2210.    *    API_EC_PARAM
  2211.    *    API_EC_PERMISSION
  2212.    *    API_EC_DATA_INVALID_OPERATION
  2213.    *    API_EC_DATA_QUOTA_EXCEEDED
  2214.    *    API_EC_DATA_UNKNOWN_ERROR
  2215.    */
  2216.   public function &data_defineAssociation($name, $assoc_type, $assoc_info1,
  2217.                                          $assoc_info2, $inverse = null) {
  2218.     return $this->call_method('facebook.data.defineAssociation',
  2219.        array('name' => $name,
  2220.              'assoc_type' => $assoc_type,
  2221.              'assoc_info1' => json_encode($assoc_info1),
  2222.              'assoc_info2' => json_encode($assoc_info2),
  2223.              'inverse' => $inverse));
  2224.   }
  2225.  
  2226.   /**
  2227.    * Undefine an object association.
  2228.    *
  2229.    * @param  name        name of this association
  2230.    * @error
  2231.    *    API_EC_DATA_DATABASE_ERROR
  2232.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2233.    *    API_EC_PARAM
  2234.    *    API_EC_PERMISSION
  2235.    *    API_EC_DATA_INVALID_OPERATION
  2236.    *    API_EC_DATA_QUOTA_EXCEEDED
  2237.    *    API_EC_DATA_UNKNOWN_ERROR
  2238.    */
  2239.   public function &data_undefineAssociation($name) {
  2240.     return $this->call_method('facebook.data.undefineAssociation',
  2241.        array('name' => $name));
  2242.   }
  2243.  
  2244.   /**
  2245.    * Rename an object association or aliases.
  2246.    *
  2247.    * @param  name        name of this association
  2248.    * @param  new_name    (optional) new name of this association
  2249.    * @param  new_alias1  (optional) new alias for object type 1
  2250.    * @param  new_alias2  (optional) new alias for object type 2
  2251.    * @error
  2252.    *    API_EC_DATA_DATABASE_ERROR
  2253.    *    API_EC_DATA_OBJECT_ALREADY_EXISTS
  2254.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2255.    *    API_EC_PARAM
  2256.    *    API_EC_PERMISSION
  2257.    *    API_EC_DATA_INVALID_OPERATION
  2258.    *    API_EC_DATA_QUOTA_EXCEEDED
  2259.    *    API_EC_DATA_UNKNOWN_ERROR
  2260.    */
  2261.   public function &data_renameAssociation($name, $new_name, $new_alias1 = null,
  2262.                                          $new_alias2 = null) {
  2263.     return $this->call_method('facebook.data.renameAssociation',
  2264.        array('name' => $name,
  2265.              'new_name' => $new_name,
  2266.              'new_alias1' => $new_alias1,
  2267.              'new_alias2' => $new_alias2));
  2268.   }
  2269.  
  2270.   /**
  2271.    * Get definition of an object association.
  2272.    *
  2273.    * @param  name        name of this association
  2274.    * @return             specified association
  2275.    * @error
  2276.    *    API_EC_DATA_DATABASE_ERROR
  2277.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2278.    *    API_EC_PARAM
  2279.    *    API_EC_PERMISSION
  2280.    *    API_EC_DATA_QUOTA_EXCEEDED
  2281.    *    API_EC_DATA_UNKNOWN_ERROR
  2282.    */
  2283.   public function &data_getAssociationDefinition($name) {
  2284.     return $this->call_method('facebook.data.getAssociationDefinition',
  2285.        array('name' => $name));
  2286.   }
  2287.  
  2288.   /**
  2289.    * Get definition of all associations.
  2290.    *
  2291.    * @return             all defined associations
  2292.    * @error
  2293.    *    API_EC_DATA_DATABASE_ERROR
  2294.    *    API_EC_PERMISSION
  2295.    *    API_EC_DATA_QUOTA_EXCEEDED
  2296.    *    API_EC_DATA_UNKNOWN_ERROR
  2297.    */
  2298.   public function &data_getAssociationDefinitions() {
  2299.     return $this->call_method('facebook.data.getAssociationDefinitions',
  2300.        array());
  2301.   }
  2302.  
  2303.   /**
  2304.    * Create or modify an association between two objects.
  2305.    *
  2306.    * @param  name        name of association
  2307.    * @param  obj_id1     id of first object
  2308.    * @param  obj_id2     id of second object
  2309.    * @param  data        (optional) extra string data to store
  2310.    * @param  assoc_time  (optional) extra time data; default to creation time
  2311.    * @error
  2312.    *    API_EC_DATA_DATABASE_ERROR
  2313.    *    API_EC_PARAM
  2314.    *    API_EC_PERMISSION
  2315.    *    API_EC_DATA_INVALID_OPERATION
  2316.    *    API_EC_DATA_QUOTA_EXCEEDED
  2317.    *    API_EC_DATA_UNKNOWN_ERROR
  2318.    */
  2319.   public function &data_setAssociation($name, $obj_id1, $obj_id2, $data = null,
  2320.                                       $assoc_time = null) {
  2321.     return $this->call_method('facebook.data.setAssociation',
  2322.        array('name' => $name,
  2323.              'obj_id1' => $obj_id1,
  2324.              'obj_id2' => $obj_id2,
  2325.              'data' => $data,
  2326.              'assoc_time' => $assoc_time));
  2327.   }
  2328.  
  2329.   /**
  2330.    * Create or modify associations between objects.
  2331.    *
  2332.    * @param  assocs      associations to set
  2333.    * @param  name        (optional) name of association
  2334.    * @error
  2335.    *    API_EC_DATA_DATABASE_ERROR
  2336.    *    API_EC_PARAM
  2337.    *    API_EC_PERMISSION
  2338.    *    API_EC_DATA_INVALID_OPERATION
  2339.    *    API_EC_DATA_QUOTA_EXCEEDED
  2340.    *    API_EC_DATA_UNKNOWN_ERROR
  2341.    */
  2342.   public function &data_setAssociations($assocs, $name = null) {
  2343.     return $this->call_method('facebook.data.setAssociations',
  2344.        array('assocs' => json_encode($assocs),
  2345.              'name' => $name));
  2346.   }
  2347.  
  2348.   /**
  2349.    * Remove an association between two objects.
  2350.    *
  2351.    * @param  name        name of association
  2352.    * @param  obj_id1     id of first object
  2353.    * @param  obj_id2     id of second object
  2354.    * @error
  2355.    *    API_EC_DATA_DATABASE_ERROR
  2356.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2357.    *    API_EC_PARAM
  2358.    *    API_EC_PERMISSION
  2359.    *    API_EC_DATA_QUOTA_EXCEEDED
  2360.    *    API_EC_DATA_UNKNOWN_ERROR
  2361.    */
  2362.   public function &data_removeAssociation($name, $obj_id1, $obj_id2) {
  2363.     return $this->call_method('facebook.data.removeAssociation',
  2364.        array('name' => $name,
  2365.              'obj_id1' => $obj_id1,
  2366.              'obj_id2' => $obj_id2));
  2367.   }
  2368.  
  2369.   /**
  2370.    * Remove associations between objects by specifying pairs of object ids.
  2371.    *
  2372.    * @param  assocs      associations to remove
  2373.    * @param  name        (optional) name of association
  2374.    * @error
  2375.    *    API_EC_DATA_DATABASE_ERROR
  2376.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2377.    *    API_EC_PARAM
  2378.    *    API_EC_PERMISSION
  2379.    *    API_EC_DATA_QUOTA_EXCEEDED
  2380.    *    API_EC_DATA_UNKNOWN_ERROR
  2381.    */
  2382.   public function &data_removeAssociations($assocs, $name = null) {
  2383.     return $this->call_method('facebook.data.removeAssociations',
  2384.        array('assocs' => json_encode($assocs),
  2385.              'name' => $name));
  2386.   }
  2387.  
  2388.   /**
  2389.    * Remove associations between objects by specifying one object id.
  2390.    *
  2391.    * @param  name        name of association
  2392.    * @param  obj_id      who's association to remove
  2393.    * @error
  2394.    *    API_EC_DATA_DATABASE_ERROR
  2395.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2396.    *    API_EC_PARAM
  2397.    *    API_EC_PERMISSION
  2398.    *    API_EC_DATA_INVALID_OPERATION
  2399.    *    API_EC_DATA_QUOTA_EXCEEDED
  2400.    *    API_EC_DATA_UNKNOWN_ERROR
  2401.    */
  2402.   public function &data_removeAssociatedObjects($name, $obj_id) {
  2403.     return $this->call_method('facebook.data.removeAssociatedObjects',
  2404.        array('name' => $name,
  2405.              'obj_id' => $obj_id));
  2406.   }
  2407.  
  2408.   /**
  2409.    * Retrieve a list of associated objects.
  2410.    *
  2411.    * @param  name        name of association
  2412.    * @param  obj_id      who's association to retrieve
  2413.    * @param  no_data     only return object ids
  2414.    * @return             associated objects
  2415.    * @error
  2416.    *    API_EC_DATA_DATABASE_ERROR
  2417.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2418.    *    API_EC_PARAM
  2419.    *    API_EC_PERMISSION
  2420.    *    API_EC_DATA_INVALID_OPERATION
  2421.    *    API_EC_DATA_QUOTA_EXCEEDED
  2422.    *    API_EC_DATA_UNKNOWN_ERROR
  2423.    */
  2424.   public function &data_getAssociatedObjects($name, $obj_id, $no_data = true) {
  2425.     return $this->call_method('facebook.data.getAssociatedObjects',
  2426.        array('name' => $name,
  2427.              'obj_id' => $obj_id,
  2428.              'no_data' => $no_data));
  2429.   }
  2430.  
  2431.   /**
  2432.    * Count associated objects.
  2433.    *
  2434.    * @param  name        name of association
  2435.    * @param  obj_id      who's association to retrieve
  2436.    * @return             associated object's count
  2437.    * @error
  2438.    *    API_EC_DATA_DATABASE_ERROR
  2439.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2440.    *    API_EC_PARAM
  2441.    *    API_EC_PERMISSION
  2442.    *    API_EC_DATA_INVALID_OPERATION
  2443.    *    API_EC_DATA_QUOTA_EXCEEDED
  2444.    *    API_EC_DATA_UNKNOWN_ERROR
  2445.    */
  2446.   public function &data_getAssociatedObjectCount($name, $obj_id) {
  2447.     return $this->call_method('facebook.data.getAssociatedObjectCount',
  2448.        array('name' => $name,
  2449.              'obj_id' => $obj_id));
  2450.   }
  2451.  
  2452.   /**
  2453.    * Get a list of associated object counts.
  2454.    *
  2455.    * @param  name        name of association
  2456.    * @param  obj_ids     whose association to retrieve
  2457.    * @return             associated object counts
  2458.    * @error
  2459.    *    API_EC_DATA_DATABASE_ERROR
  2460.    *    API_EC_DATA_OBJECT_NOT_FOUND
  2461.    *    API_EC_PARAM
  2462.    *    API_EC_PERMISSION
  2463.    *    API_EC_DATA_INVALID_OPERATION
  2464.    *    API_EC_DATA_QUOTA_EXCEEDED
  2465.    *    API_EC_DATA_UNKNOWN_ERROR
  2466.    */
  2467.   public function &data_getAssociatedObjectCounts($name, $obj_ids) {
  2468.     return $this->call_method('facebook.data.getAssociatedObjectCounts',
  2469.        array('name' => $name,
  2470.              'obj_ids' => json_encode($obj_ids)));
  2471.   }
  2472.  
  2473.   /**
  2474.    * Find all associations between two objects.
  2475.    *
  2476.    * @param  obj_id1     id of first object
  2477.    * @param  obj_id2     id of second object
  2478.    * @param  no_data     only return association names without data
  2479.    * @return             all associations between objects
  2480.    * @error
  2481.    *    API_EC_DATA_DATABASE_ERROR
  2482.    *    API_EC_PARAM
  2483.    *    API_EC_PERMISSION
  2484.    *    API_EC_DATA_QUOTA_EXCEEDED
  2485.    *    API_EC_DATA_UNKNOWN_ERROR
  2486.    */
  2487.   public function &data_getAssociations($obj_id1, $obj_id2, $no_data = true) {
  2488.     return $this->call_method('facebook.data.getAssociations',
  2489.        array('obj_id1' => $obj_id1,
  2490.              'obj_id2' => $obj_id2,
  2491.              'no_data' => $no_data));
  2492.   }
  2493.  
  2494.   /**
  2495.    * Get the properties that you have set for an app.
  2496.    *
  2497.    * @param properties  List of properties names to fetch
  2498.    *
  2499.    * @return array  A map from property name to value
  2500.    */
  2501.   public function admin_getAppProperties($properties) {
  2502.     return json_decode(
  2503.         $this->call_method('facebook.admin.getAppProperties',
  2504.             array('properties' => json_encode($properties))), true);
  2505.   }
  2506.  
  2507.   /**
  2508.    * Set properties for an app.
  2509.    *
  2510.    * @param properties  A map from property names to values
  2511.    *
  2512.    * @return bool  true on success
  2513.    */
  2514.   public function admin_setAppProperties($properties) {
  2515.     return $this->call_method('facebook.admin.setAppProperties',
  2516.        array('properties' => json_encode($properties)));
  2517.   }
  2518.  
  2519.   /**
  2520.    * Returns the allocation limit value for a specified integration point name
  2521.    * Integration point names are defined in lib/api/karma/constants.php in the
  2522.    * limit_map.
  2523.    *
  2524.    * @param string $integration_point_name  Name of an integration point
  2525.    *                                        (see developer wiki for list).
  2526.    *
  2527.    * @return int  Integration point allocation value
  2528.    */
  2529.   public function &admin_getAllocation($integration_point_name) {
  2530.     return $this->call_method('facebook.admin.getAllocation',
  2531.         array('integration_point_name' => $integration_point_name));
  2532.   }
  2533.  
  2534.   /**
  2535.    * Returns values for the specified metrics for the current application, in
  2536.    * the given time range.  The metrics are collected for fixed-length periods,
  2537.    * and the times represent midnight at the end of each period.
  2538.    *
  2539.    * @param start_time  unix time for the start of the range
  2540.    * @param end_time    unix time for the end of the range
  2541.    * @param period      number of seconds in the desired period
  2542.    * @param metrics     list of metrics to look up
  2543.    *
  2544.    * @return array  A map of the names and values for those metrics
  2545.    */
  2546.   public function &admin_getMetrics($start_time, $end_time, $period, $metrics) {
  2547.     return $this->call_method('admin.getMetrics',
  2548.         array('start_time' => $start_time,
  2549.               'end_time' => $end_time,
  2550.               'period' => $period,
  2551.               'metrics' => json_encode($metrics)));
  2552.   }
  2553.  
  2554.   /**
  2555.    * Sets application restriction info.
  2556.    *
  2557.    * Applications can restrict themselves to only a limited user demographic
  2558.    * based on users' age and/or location or based on static predefined types
  2559.    * specified by facebook for specifying diff age restriction for diff
  2560.    * locations.
  2561.    *
  2562.    * @param array $restriction_info  The age restriction settings to set.
  2563.    *
  2564.    * @return bool  true on success
  2565.    */
  2566.   public function admin_setRestrictionInfo($restriction_info = null) {
  2567.     $restriction_str = null;
  2568.     if (!empty($restriction_info)) {
  2569.       $restriction_str = json_encode($restriction_info);
  2570.     }
  2571.     return $this->call_method('admin.setRestrictionInfo',
  2572.         array('restriction_str' => $restriction_str));
  2573.   }
  2574.  
  2575.   /**
  2576.    * Gets application restriction info.
  2577.    *
  2578.    * Applications can restrict themselves to only a limited user demographic
  2579.    * based on users' age and/or location or based on static predefined types
  2580.    * specified by facebook for specifying diff age restriction for diff
  2581.    * locations.
  2582.    *
  2583.    * @return array  The age restriction settings for this application.
  2584.    */
  2585.   public function admin_getRestrictionInfo() {
  2586.     return json_decode(
  2587.         $this->call_method('admin.getRestrictionInfo'),
  2588.         true);
  2589.   }
  2590.  
  2591.   /* UTILITY FUNCTIONS */
  2592.  
  2593.   /**
  2594.    * Calls the specified normal POST method with the specified parameters.
  2595.    *
  2596.    * @param string $method  Name of the Facebook method to invoke
  2597.    * @param array $params   A map of param names => param values
  2598.    *
  2599.    * @return mixed  Result of method call; this returns a reference to support
  2600.    *                'delayed returns' when in a batch context.
  2601.    *     See: http://wiki.developers.facebook.com/index.php/Using_batching_API
  2602.    */
  2603.   public function &call_method($method, $params = array()) {
  2604.     //Check if we are in batch mode
  2605.     if($this->batch_queue === null) {
  2606.       if ($this->call_as_apikey) {
  2607.         $params['call_as_apikey'] = $this->call_as_apikey;
  2608.       }
  2609.       $data = $this->post_request($method, $params);
  2610.       if (empty($params['format']) || strtolower($params['format']) != 'json') {
  2611.         $result = $this->convert_xml_to_result($data, $method, $params);
  2612.       }
  2613.       else {
  2614.         $result = json_decode($data, true);
  2615.       }
  2616.  
  2617.       if (is_array($result) && isset($result['error_code'])) {
  2618.         throw new FacebookRestClientException($result['error_msg'],
  2619.                                               $result['error_code']);
  2620.       }
  2621.     }
  2622.     else {
  2623.       $result = null;
  2624.       $batch_item = array('m' => $method, 'p' => $params, 'r' => & $result);
  2625.       $this->batch_queue[] = $batch_item;
  2626.     }
  2627.  
  2628.     return $result;
  2629.   }
  2630.  
  2631.   /**
  2632.    * Calls the specified file-upload POST method with the specified parameters
  2633.    *
  2634.    * @param string $method Name of the Facebook method to invoke
  2635.    * @param array  $params A map of param names => param values
  2636.    * @param string $file   A path to the file to upload (required)
  2637.    *
  2638.    * @return array A dictionary representing the response.
  2639.    */
  2640.   public function call_upload_method($method, $params, $file, $server_addr = null) {
  2641.     if ($this->batch_queue === null) {
  2642.       if (!file_exists($file)) {
  2643.         $code =
  2644.           FacebookAPIErrorCodes::API_EC_PARAM;
  2645.         $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
  2646.         throw new FacebookRestClientException($description, $code);
  2647.       }
  2648.  
  2649.       $xml = $this->post_upload_request($method, $params, $file, $server_addr);
  2650.       $result = $this->convert_xml_to_result($xml, $method, $params);
  2651.  
  2652.       if (is_array($result) && isset($result['error_code'])) {
  2653.         throw new FacebookRestClientException($result['error_msg'],
  2654.                                               $result['error_code']);
  2655.       }
  2656.     }
  2657.     else {
  2658.       $code =
  2659.         FacebookAPIErrorCodes::API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE;
  2660.       $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
  2661.       throw new FacebookRestClientException($description, $code);
  2662.     }
  2663.  
  2664.     return $result;
  2665.   }
  2666.  
  2667.   private function convert_xml_to_result($xml, $method, $params) {
  2668.     $sxml = simplexml_load_string($xml);
  2669.     $result = self::convert_simplexml_to_array($sxml);
  2670.  
  2671.     if (!empty($GLOBALS['facebook_config']['debug'])) {
  2672.       // output the raw xml and its corresponding php object, for debugging:
  2673.       print '<div style="margin: 10px 30px; padding: 5px; border: 2px solid black; background: gray; color: white; font-size: 12px; font-weight: bold;">';
  2674.       $this->cur_id++;
  2675.       print $this->cur_id . ': Called ' . $method . ', show ' .
  2676.             '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'params\');">Params</a> | '.
  2677.             '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'xml\');">XML</a> | '.
  2678.             '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'sxml\');">SXML</a> | '.
  2679.             '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'php\');">PHP</a>';
  2680.       print '<pre id="params'.$this->cur_id.'" style="display: none; overflow: auto;">'.print_r($params, true).'</pre>';
  2681.       print '<pre id="xml'.$this->cur_id.'" style="display: none; overflow: auto;">'.htmlspecialchars($xml).'</pre>';
  2682.       print '<pre id="php'.$this->cur_id.'" style="display: none; overflow: auto;">'.print_r($result, true).'</pre>';
  2683.       print '<pre id="sxml'.$this->cur_id.'" style="display: none; overflow: auto;">'.print_r($sxml, true).'</pre>';
  2684.       print '</div>';
  2685.     }
  2686.     return $result;
  2687.   }
  2688.  
  2689.   private function finalize_params($method, &$params) {
  2690.     $this->add_standard_params($method, $params);
  2691.     // we need to do this before signing the params
  2692.     $this->convert_array_values_to_csv($params);
  2693.     $params['sig'] = Facebook::generate_sig($params, $this->secret);
  2694.   }
  2695.  
  2696.   private function convert_array_values_to_csv(&$params) {
  2697.     foreach ($params as $key => &$val) {
  2698.       if (is_array($val)) {
  2699.         $val = implode(',', $val);
  2700.       }
  2701.     }
  2702.   }
  2703.  
  2704.   private function add_standard_params($method, &$params) {
  2705.     if ($this->call_as_apikey) {
  2706.       $params['call_as_apikey'] = $this->call_as_apikey;
  2707.     }
  2708.     $params['method'] = $method;
  2709.     $params['session_key'] = $this->session_key;
  2710.     $params['api_key'] = $this->api_key;
  2711.     $params['call_id'] = microtime(true);
  2712.     if ($params['call_id'] <= $this->last_call_id) {
  2713.       $params['call_id'] = $this->last_call_id + 0.001;
  2714.     }
  2715.     $this->last_call_id = $params['call_id'];
  2716.     if (!isset($params['v'])) {
  2717.       $params['v'] = '1.0';
  2718.     }
  2719.     if (isset($this->use_ssl_resources) &&
  2720.         $this->use_ssl_resources) {
  2721.       $params['return_ssl_resources'] = true;
  2722.     }
  2723.   }
  2724.  
  2725.   private function create_post_string($method, $params) {
  2726.     $post_params = array();
  2727.     foreach ($params as $key => &$val) {
  2728.       $post_params[] = $key.'='.urlencode($val);
  2729.     }
  2730.     return implode('&', $post_params);
  2731.   }
  2732.  
  2733.   private function run_multipart_http_transaction($method, $params, $file, $server_addr) {
  2734.  
  2735.     // the format of this message is specified in RFC1867/RFC1341.
  2736.     // we add twenty pseudo-random digits to the end of the boundary string.
  2737.     $boundary = '--------------------------FbMuLtIpArT' .
  2738.                 sprintf("%010d", mt_rand()) .
  2739.                 sprintf("%010d", mt_rand());
  2740.     $content_type = 'multipart/form-data; boundary=' . $boundary;
  2741.     // within the message, we prepend two extra hyphens.
  2742.     $delimiter = '--' . $boundary;
  2743.     $close_delimiter = $delimiter . '--';
  2744.     $content_lines = array();
  2745.     foreach ($params as $key => &$val) {
  2746.       $content_lines[] = $delimiter;
  2747.       $content_lines[] = 'Content-Disposition: form-data; name="' . $key . '"';
  2748.       $content_lines[] = '';
  2749.       $content_lines[] = $val;
  2750.     }
  2751.     // now add the file data
  2752.     $content_lines[] = $delimiter;
  2753.     $content_lines[] =
  2754.       'Content-Disposition: form-data; filename="' . $file . '"';
  2755.     $content_lines[] = 'Content-Type: application/octet-stream';
  2756.     $content_lines[] = '';
  2757.     $content_lines[] = file_get_contents($file);
  2758.     $content_lines[] = $close_delimiter;
  2759.     $content_lines[] = '';
  2760.     $content = implode("\r\n", $content_lines);
  2761.     return $this->run_http_post_transaction($content_type, $content, $server_addr);
  2762.   }
  2763.  
  2764.   public function post_request($method, $params) {
  2765.     $this->finalize_params($method, $params);
  2766.     $post_string = $this->create_post_string($method, $params);
  2767.     if ($this->use_curl_if_available && function_exists('curl_init')) {
  2768.       $useragent = 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion();
  2769.       $ch = curl_init();
  2770.       curl_setopt($ch, CURLOPT_URL, $this->server_addr);
  2771.       curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);
  2772.       curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  2773.       curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
  2774.       $result = curl_exec($ch);
  2775.       curl_close($ch);
  2776.     } else {
  2777.       $content_type = 'application/x-www-form-urlencoded';
  2778.       $content = $post_string;
  2779.       $result = $this->run_http_post_transaction($content_type,
  2780.                                                  $content,
  2781.                                                  $this->server_addr);
  2782.     }
  2783.     return $result;
  2784.   }
  2785.  
  2786.   private function post_upload_request($method, $params, $file, $server_addr = null) {
  2787.     $server_addr = $server_addr ? $server_addr : $this->server_addr;
  2788.     $this->finalize_params($method, $params);
  2789.     if ($this->use_curl_if_available && function_exists('curl_init')) {
  2790.       // prepending '@' causes cURL to upload the file; the key is ignored.
  2791.       $params['_file'] = '@' . $file;
  2792.       $useragent = 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion();
  2793.       $ch = curl_init();
  2794.       curl_setopt($ch, CURLOPT_URL, $server_addr);
  2795.       // this has to come before the POSTFIELDS set!
  2796.       curl_setopt($ch, CURLOPT_POST, 1 );
  2797.       // passing an array gets curl to use the multipart/form-data content type
  2798.       curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
  2799.       curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  2800.       curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
  2801.       $result = curl_exec($ch);
  2802.       curl_close($ch);
  2803.     } else {
  2804.       $result = $this->run_multipart_http_transaction($method, $params, $file, $server_addr);
  2805.     }
  2806.     return $result;
  2807.   }
  2808.  
  2809.   private function run_http_post_transaction($content_type, $content, $server_addr) {
  2810.  
  2811.     $user_agent = 'Facebook API PHP5 Client 1.1 (non-curl) ' . phpversion();
  2812.     $content_length = strlen($content);
  2813.     $context =
  2814.       array('http' =>
  2815.               array('method' => 'POST',
  2816.                     'user_agent' => $user_agent,
  2817.                     'header' => 'Content-Type: ' . $content_type . "\r\n" .
  2818.                                 'Content-Length: ' . $content_length,
  2819.                     'content' => $content));
  2820.     $context_id = stream_context_create($context);
  2821.     $sock = fopen($server_addr, 'r', false, $context_id);
  2822.  
  2823.     $result = '';
  2824.     if ($sock) {
  2825.       while (!feof($sock)) {
  2826.         $result .= fgets($sock, 4096);
  2827.       }
  2828.       fclose($sock);
  2829.     }
  2830.     return $result;
  2831.   }
  2832.  
  2833.   public static function convert_simplexml_to_array($sxml) {
  2834.     $arr = array();
  2835.     if ($sxml) {
  2836.       foreach ($sxml as $k => $v) {
  2837.         if ($sxml['list']) {
  2838.           $arr[] = self::convert_simplexml_to_array($v);
  2839.         } else {
  2840.           $arr[$k] = self::convert_simplexml_to_array($v);
  2841.         }
  2842.       }
  2843.     }
  2844.     if (sizeof($arr) > 0) {
  2845.       return $arr;
  2846.     } else {
  2847.       return (string)$sxml;
  2848.     }
  2849.   }
  2850.  
  2851.   private function get_uid($uid) {
  2852.     return $uid ? $uid : $this->user;
  2853.   }
  2854. }
  2855.  
  2856.  
  2857. class FacebookRestClientException extends Exception {
  2858. }
  2859.  
  2860. // Supporting methods and values------
  2861.  
  2862. /**
  2863.  * Error codes and descriptions for the Facebook API.
  2864.  */
  2865.  
  2866. class FacebookAPIErrorCodes {
  2867.  
  2868.   const API_EC_SUCCESS = 0;
  2869.  
  2870.   /*
  2871.    * GENERAL ERRORS
  2872.    */
  2873.   const API_EC_UNKNOWN = 1;
  2874.   const API_EC_SERVICE = 2;
  2875.   const API_EC_METHOD = 3;
  2876.   const API_EC_TOO_MANY_CALLS = 4;
  2877.   const API_EC_BAD_IP = 5;
  2878.   const API_EC_HOST_API = 6;
  2879.   const API_EC_HOST_UP = 7;
  2880.   const API_EC_SECURE = 8;
  2881.   const API_EC_RATE = 9;
  2882.   const API_EC_PERMISSION_DENIED = 10;
  2883.   const API_EC_DEPRECATED = 11;
  2884.   const API_EC_VERSION = 12;
  2885.  
  2886.   /*
  2887.    * PARAMETER ERRORS
  2888.    */
  2889.   const API_EC_PARAM = 100;
  2890.   const API_EC_PARAM_API_KEY = 101;
  2891.   const API_EC_PARAM_SESSION_KEY = 102;
  2892.   const API_EC_PARAM_CALL_ID = 103;
  2893.   const API_EC_PARAM_SIGNATURE = 104;
  2894.   const API_EC_PARAM_TOO_MANY = 105;
  2895.   const API_EC_PARAM_USER_ID = 110;
  2896.   const API_EC_PARAM_USER_FIELD = 111;
  2897.   const API_EC_PARAM_SOCIAL_FIELD = 112;
  2898.   const API_EC_PARAM_EMAIL = 113;
  2899.   const API_EC_PARAM_USER_ID_LIST = 114;
  2900.   const API_EC_PARAM_ALBUM_ID = 120;
  2901.   const API_EC_PARAM_PHOTO_ID = 121;
  2902.   const API_EC_PARAM_FEED_PRIORITY = 130;
  2903.   const API_EC_PARAM_CATEGORY = 140;
  2904.   const API_EC_PARAM_SUBCATEGORY = 141;
  2905.   const API_EC_PARAM_TITLE = 142;
  2906.   const API_EC_PARAM_DESCRIPTION = 143;
  2907.   const API_EC_PARAM_BAD_JSON = 144;
  2908.   const API_EC_PARAM_BAD_EID = 150;
  2909.   const API_EC_PARAM_UNKNOWN_CITY = 151;
  2910.   const API_EC_PARAM_BAD_PAGE_TYPE = 152;
  2911.  
  2912.   /*
  2913.    * USER PERMISSIONS ERRORS
  2914.    */
  2915.   const API_EC_PERMISSION = 200;
  2916.   const API_EC_PERMISSION_USER = 210;
  2917.   const API_EC_PERMISSION_ALBUM = 220;
  2918.   const API_EC_PERMISSION_PHOTO = 221;
  2919.   const API_EC_PERMISSION_MESSAGE = 230;
  2920.   const API_EC_PERMISSION_OTHER_USER = 240;
  2921.   const API_EC_PERMISSION_STATUS_UPDATE = 250;
  2922.   const API_EC_PERMISSION_PHOTO_UPLOAD = 260;
  2923.   const API_EC_PERMISSION_VIDEO_UPLOAD = 261;
  2924.   const API_EC_PERMISSION_SMS = 270;
  2925.   const API_EC_PERMISSION_CREATE_LISTING = 280;
  2926.   const API_EC_PERMISSION_CREATE_NOTE = 281;
  2927.   const API_EC_PERMISSION_SHARE_ITEM = 282;
  2928.   const API_EC_PERMISSION_EVENT = 290;
  2929.   const API_EC_PERMISSION_LARGE_FBML_TEMPLATE = 291;
  2930.   const API_EC_PERMISSION_LIVEMESSAGE = 292;
  2931.   const API_EC_PERMISSION_RSVP_EVENT = 299;
  2932.  
  2933.   /*
  2934.    * DATA EDIT ERRORS
  2935.    */
  2936.   const API_EC_EDIT = 300;
  2937.   const API_EC_EDIT_USER_DATA = 310;
  2938.   const API_EC_EDIT_PHOTO = 320;
  2939.   const API_EC_EDIT_ALBUM_SIZE = 321;
  2940.   const API_EC_EDIT_PHOTO_TAG_SUBJECT = 322;
  2941.   const API_EC_EDIT_PHOTO_TAG_PHOTO = 323;
  2942.   const API_EC_EDIT_PHOTO_FILE = 324;
  2943.   const API_EC_EDIT_PHOTO_PENDING_LIMIT = 325;
  2944.   const API_EC_EDIT_PHOTO_TAG_LIMIT = 326;
  2945.   const API_EC_EDIT_ALBUM_REORDER_PHOTO_NOT_IN_ALBUM = 327;
  2946.   const API_EC_EDIT_ALBUM_REORDER_TOO_FEW_PHOTOS = 328;
  2947.  
  2948.   const API_EC_MALFORMED_MARKUP = 329;
  2949.   const API_EC_EDIT_MARKUP = 330;
  2950.  
  2951.   const API_EC_EDIT_FEED_TOO_MANY_USER_CALLS = 340;
  2952.   const API_EC_EDIT_FEED_TOO_MANY_USER_ACTION_CALLS = 341;
  2953.   const API_EC_EDIT_FEED_TITLE_LINK = 342;
  2954.   const API_EC_EDIT_FEED_TITLE_LENGTH = 343;
  2955.   const API_EC_EDIT_FEED_TITLE_NAME = 344;
  2956.   const API_EC_EDIT_FEED_TITLE_BLANK = 345;
  2957.   const API_EC_EDIT_FEED_BODY_LENGTH = 346;
  2958.   const API_EC_EDIT_FEED_PHOTO_SRC = 347;
  2959.   const API_EC_EDIT_FEED_PHOTO_LINK = 348;
  2960.  
  2961.   const API_EC_EDIT_VIDEO_SIZE = 350;
  2962.   const API_EC_EDIT_VIDEO_INVALID_FILE = 351;
  2963.   const API_EC_EDIT_VIDEO_INVALID_TYPE = 352;
  2964.   const API_EC_EDIT_VIDEO_FILE = 353;
  2965.  
  2966.   const API_EC_EDIT_FEED_TITLE_ARRAY = 360;
  2967.   const API_EC_EDIT_FEED_TITLE_PARAMS = 361;
  2968.   const API_EC_EDIT_FEED_BODY_ARRAY = 362;
  2969.   const API_EC_EDIT_FEED_BODY_PARAMS = 363;
  2970.   const API_EC_EDIT_FEED_PHOTO = 364;
  2971.   const API_EC_EDIT_FEED_TEMPLATE = 365;
  2972.   const API_EC_EDIT_FEED_TARGET = 366;
  2973.   const API_EC_EDIT_FEED_MARKUP = 367;
  2974.  
  2975.   /**
  2976.    * SESSION ERRORS
  2977.    */
  2978.   const API_EC_SESSION_TIMED_OUT = 450;
  2979.   const API_EC_SESSION_METHOD = 451;
  2980.   const API_EC_SESSION_INVALID = 452;
  2981.   const API_EC_SESSION_REQUIRED = 453;
  2982.   const API_EC_SESSION_REQUIRED_FOR_SECRET = 454;
  2983.   const API_EC_SESSION_CANNOT_USE_SESSION_SECRET = 455;
  2984.  
  2985.  
  2986.   /**
  2987.    * FQL ERRORS
  2988.    */
  2989.   const FQL_EC_UNKNOWN_ERROR = 600;
  2990.   const FQL_EC_PARSER = 601; // backwards compatibility
  2991.   const FQL_EC_PARSER_ERROR = 601;
  2992.   const FQL_EC_UNKNOWN_FIELD = 602;
  2993.   const FQL_EC_UNKNOWN_TABLE = 603;
  2994.   const FQL_EC_NOT_INDEXABLE = 604; // backwards compatibility
  2995.   const FQL_EC_NO_INDEX = 604;
  2996.   const FQL_EC_UNKNOWN_FUNCTION = 605;
  2997.   const FQL_EC_INVALID_PARAM = 606;
  2998.   const FQL_EC_INVALID_FIELD = 607;
  2999.   const FQL_EC_INVALID_SESSION = 608;
  3000.   const FQL_EC_UNSUPPORTED_APP_TYPE = 609;
  3001.   const FQL_EC_SESSION_SECRET_NOT_ALLOWED = 610;
  3002.  
  3003.   const API_EC_REF_SET_FAILED = 700;
  3004.  
  3005.   /**
  3006.    * DATA STORE API ERRORS
  3007.    */
  3008.   const API_EC_DATA_UNKNOWN_ERROR = 800;
  3009.   const API_EC_DATA_INVALID_OPERATION = 801;
  3010.   const API_EC_DATA_QUOTA_EXCEEDED = 802;
  3011.   const API_EC_DATA_OBJECT_NOT_FOUND = 803;
  3012.   const API_EC_DATA_OBJECT_ALREADY_EXISTS = 804;
  3013.   const API_EC_DATA_DATABASE_ERROR = 805;
  3014.   const API_EC_DATA_CREATE_TEMPLATE_ERROR = 806;
  3015.   const API_EC_DATA_TEMPLATE_EXISTS_ERROR = 807;
  3016.   const API_EC_DATA_TEMPLATE_HANDLE_TOO_LONG = 808;
  3017.   const API_EC_DATA_TEMPLATE_HANDLE_ALREADY_IN_USE = 809;
  3018.   const API_EC_DATA_TOO_MANY_TEMPLATE_BUNDLES = 810;
  3019.   const API_EC_DATA_MALFORMED_ACTION_LINK = 811;
  3020.   const API_EC_DATA_TEMPLATE_USES_RESERVED_TOKEN = 812;
  3021.  
  3022.   /*
  3023.    * APPLICATION INFO ERRORS
  3024.    */
  3025.   const API_EC_NO_SUCH_APP = 900;
  3026.  
  3027.   /*
  3028.    * BATCH ERRORS
  3029.    */
  3030.   const API_EC_BATCH_TOO_MANY_ITEMS = 950;
  3031.   const API_EC_BATCH_ALREADY_STARTED = 951;
  3032.   const API_EC_BATCH_NOT_STARTED = 952;
  3033.   const API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE = 953;
  3034.  
  3035.   /*
  3036.    * EVENT API ERRORS
  3037.    */
  3038.   const API_EC_EVENT_INVALID_TIME = 1000;
  3039.  
  3040.   /*
  3041.    * INFO BOX ERRORS
  3042.    */
  3043.   const API_EC_INFO_NO_INFORMATION = 1050;
  3044.   const API_EC_INFO_SET_FAILED = 1051;
  3045.  
  3046.   /*
  3047.    * LIVEMESSAGE API ERRORS
  3048.    */
  3049.   const API_EC_LIVEMESSAGE_SEND_FAILED = 1100;
  3050.   const API_EC_LIVEMESSAGE_EVENT_NAME_TOO_LONG = 1101;
  3051.   const API_EC_LIVEMESSAGE_MESSAGE_TOO_LONG = 1102;
  3052.  
  3053.   /*
  3054.    * CONNECT SESSION ERRORS
  3055.    */
  3056.   const API_EC_CONNECT_FEED_DISABLED = 1300;
  3057.  
  3058.   /*
  3059.    * Platform tag bundles errors
  3060.    */
  3061.   const API_EC_TAG_BUNDLE_QUOTA = 1400;
  3062.  
  3063.   /*
  3064.    * SHARE
  3065.    */
  3066.   const API_EC_SHARE_BAD_URL = 1500;
  3067.  
  3068.   /*
  3069.    * NOTES
  3070.    */
  3071.   const API_EC_NOTE_CANNOT_MODIFY = 1600;
  3072.  
  3073.   /*
  3074.    * COMMENTS
  3075.    */
  3076.   const API_EC_COMMENTS_UNKNOWN = 1700;
  3077.   const API_EC_COMMENTS_POST_TOO_LONG = 1701;
  3078.   const API_EC_COMMENTS_DB_DOWN = 1702;
  3079.   const API_EC_COMMENTS_INVALID_XID = 1703;
  3080.   const API_EC_COMMENTS_INVALID_UID = 1704;
  3081.   const API_EC_COMMENTS_INVALID_POST = 1705;
  3082.  
  3083.   /**
  3084.    * This array is no longer maintained; to view the description of an error
  3085.    * code, please look at the message element of the API response or visit
  3086.    * the developer wiki at http://wiki.developers.facebook.com/.
  3087.    */
  3088.   public static $api_error_descriptions = array(
  3089.       self::API_EC_SUCCESS           => 'Success',
  3090.       self::API_EC_UNKNOWN           => 'An unknown error occurred',
  3091.       self::API_EC_SERVICE           => 'Service temporarily unavailable',
  3092.       self::API_EC_METHOD            => 'Unknown method',
  3093.       self::API_EC_TOO_MANY_CALLS    => 'Application request limit reached',
  3094.       self::API_EC_BAD_IP            => 'Unauthorized source IP address',
  3095.       self::API_EC_PARAM             => 'Invalid parameter',
  3096.       self::API_EC_PARAM_API_KEY     => 'Invalid API key',
  3097.       self::API_EC_PARAM_SESSION_KEY => 'Session key invalid or no longer valid',
  3098.       self::API_EC_PARAM_CALL_ID     => 'Call_id must be greater than previous',
  3099.       self::API_EC_PARAM_SIGNATURE   => 'Incorrect signature',
  3100.       self::API_EC_PARAM_USER_ID     => 'Invalid user id',
  3101.       self::API_EC_PARAM_USER_FIELD  => 'Invalid user info field',
  3102.       self::API_EC_PARAM_SOCIAL_FIELD => 'Invalid user field',
  3103.       self::API_EC_PARAM_ALBUM_ID    => 'Invalid album id',
  3104.       self::API_EC_PARAM_BAD_EID     => 'Invalid eid',
  3105.       self::API_EC_PARAM_UNKNOWN_CITY => 'Unknown city',
  3106.       self::API_EC_PERMISSION        => 'Permissions error',
  3107.       self::API_EC_PERMISSION_USER   => 'User not visible',
  3108.       self::API_EC_PERMISSION_ALBUM  => 'Album not visible',
  3109.       self::API_EC_PERMISSION_PHOTO  => 'Photo not visible',
  3110.       self::API_EC_PERMISSION_EVENT  => 'Creating and modifying events required the extended permission create_event',
  3111.       self::API_EC_PERMISSION_RSVP_EVENT => 'RSVPing to events required the extended permission rsvp_event',
  3112.       self::API_EC_EDIT_ALBUM_SIZE   => 'Album is full',
  3113.       self::FQL_EC_PARSER            => 'FQL: Parser Error',
  3114.       self::FQL_EC_UNKNOWN_FIELD     => 'FQL: Unknown Field',
  3115.       self::FQL_EC_UNKNOWN_TABLE     => 'FQL: Unknown Table',
  3116.       self::FQL_EC_NOT_INDEXABLE     => 'FQL: Statement not indexable',
  3117.       self::FQL_EC_UNKNOWN_FUNCTION  => 'FQL: Attempted to call unknown function',
  3118.       self::FQL_EC_INVALID_PARAM     => 'FQL: Invalid parameter passed in',
  3119.       self::API_EC_DATA_UNKNOWN_ERROR => 'Unknown data store API error',
  3120.       self::API_EC_DATA_INVALID_OPERATION => 'Invalid operation',
  3121.       self::API_EC_DATA_QUOTA_EXCEEDED => 'Data store allowable quota was exceeded',
  3122.       self::API_EC_DATA_OBJECT_NOT_FOUND => 'Specified object cannot be found',
  3123.       self::API_EC_DATA_OBJECT_ALREADY_EXISTS => 'Specified object already exists',
  3124.       self::API_EC_DATA_DATABASE_ERROR => 'A database error occurred. Please try again',
  3125.       self::API_EC_BATCH_ALREADY_STARTED => 'begin_batch already called, please make sure to call end_batch first',
  3126.       self::API_EC_BATCH_NOT_STARTED => 'end_batch called before begin_batch',
  3127.       self::API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE => 'This method is not allowed in batch mode'
  3128.   );
  3129. }
  3130.  
  3131. // Copyright 2004-2009 Facebook. All Rights Reserved.
  3132. //
  3133. // +---------------------------------------------------------------------------+
  3134. // | Facebook Platform PHP5 client                                             |
  3135. // +---------------------------------------------------------------------------+
  3136. // | Copyright (c) 2007 Facebook, Inc.                                         |
  3137. // | All rights reserved.                                                      |
  3138. // |                                                                           |
  3139. // | Redistribution and use in source and binary forms, with or without        |
  3140. // | modification, are permitted provided that the following conditions        |
  3141. // | are met:                                                                  |
  3142. // |                                                                           |
  3143. // | 1. Redistributions of source code must retain the above copyright         |
  3144. // |    notice, this list of conditions and the following disclaimer.          |
  3145. // | 2. Redistributions in binary form must reproduce the above copyright      |
  3146. // |    notice, this list of conditions and the following disclaimer in the    |
  3147. // |    documentation and/or other materials provided with the distribution.   |
  3148. // |                                                                           |
  3149. // | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
  3150. // | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
  3151. // | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
  3152. // | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  3153. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
  3154. // | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  3155. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
  3156. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
  3157. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
  3158. // | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
  3159. // +---------------------------------------------------------------------------+
  3160. // | For help with this library, contact developers-help@facebook.com          |
  3161. // +---------------------------------------------------------------------------+
  3162.  
  3163. #include_once 'facebookapi_php5_restlib.php';
  3164.  
  3165. define('FACEBOOK_API_VALIDATION_ERROR', 1);
  3166. class Facebook {
  3167.   public $api_client;
  3168.   public $api_key;
  3169.   public $secret;
  3170.   public $generate_session_secret;
  3171.   public $session_expires;
  3172.  
  3173.   public $fb_params;
  3174.   public $user;
  3175.   public $profile_user;
  3176.   public $canvas_user;
  3177.   protected $base_domain;
  3178.   /*
  3179.    * Create a Facebook client like this:
  3180.    *
  3181.    * $fb = new Facebook(API_KEY, SECRET);
  3182.    *
  3183.    * This will automatically pull in any parameters, validate them against the
  3184.    * session signature, and chuck them in the public $fb_params member variable.
  3185.    *
  3186.    * @param api_key                  your Developer API key
  3187.    * @param secret                   your Developer API secret
  3188.    * @param generate_session_secret  whether to automatically generate a session
  3189.    *                                 if the user doesn't have one, but
  3190.    *                                 there is an auth token present in the url,
  3191.    */
  3192.   public function __construct($api_key, $secret, $generate_session_secret=false) {
  3193.     $this->api_key                 = $api_key;
  3194.     $this->secret                  = $secret;
  3195.     $this->generate_session_secret = $generate_session_secret;
  3196.     $this->api_client = new FacebookRestClient($api_key, $secret, null);
  3197.     $this->validate_fb_params();
  3198.  
  3199.     // Set the default user id for methods that allow the caller to
  3200.     // pass an explicit uid instead of using a session key.
  3201.     $defaultUser = null;
  3202.     if ($this->user) {
  3203.       $defaultUser = $this->user;
  3204.     } else if ($this->profile_user) {
  3205.       $defaultUser = $this->profile_user;
  3206.     } else if ($this->canvas_user) {
  3207.       $defaultUser = $this->canvas_user;
  3208.     }
  3209.  
  3210.     $this->api_client->set_user($defaultUser);
  3211.  
  3212.  
  3213.     if (isset($this->fb_params['friends'])) {
  3214.       $this->api_client->friends_list = explode(',', $this->fb_params['friends']);
  3215.     }
  3216.     if (isset($this->fb_params['added'])) {
  3217.       $this->api_client->added = $this->fb_params['added'];
  3218.     }
  3219.     if (isset($this->fb_params['canvas_user'])) {
  3220.       $this->api_client->canvas_user = $this->fb_params['canvas_user'];
  3221.     }
  3222.   }
  3223.  
  3224.   /*
  3225.    * Validates that the parameters passed in were sent from Facebook. It does so
  3226.    * by validating that the signature matches one that could only be generated
  3227.    * by using your application's secret key.
  3228.    *
  3229.    * Facebook-provided parameters will come from $_POST, $_GET, or $_COOKIE,
  3230.    * in that order. $_POST and $_GET are always more up-to-date than cookies,
  3231.    * so we prefer those if they are available.
  3232.    *
  3233.    * For nitty-gritty details of when each of these is used, check out
  3234.    * http://wiki.developers.facebook.com/index.php/Verifying_The_Signature
  3235.    *
  3236.    * @param bool  resolve_auth_token  convert an auth token into a session
  3237.    */
  3238.   public function validate_fb_params($resolve_auth_token=true) {
  3239.     $this->fb_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_sig');
  3240.  
  3241.     // note that with preload FQL, it's possible to receive POST params in
  3242.     // addition to GET, so use a different prefix to differentiate them
  3243.     if (!$this->fb_params) {
  3244.       $fb_params = $this->get_valid_fb_params($_GET, 48*3600, 'fb_sig');
  3245.       $fb_post_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_post_sig');
  3246.       $this->fb_params = array_merge($fb_params, $fb_post_params);
  3247.     }
  3248.  
  3249.     // Okay, something came in via POST or GET
  3250.     if ($this->fb_params) {
  3251.       $user               = isset($this->fb_params['user']) ?
  3252.                             $this->fb_params['user'] : null;
  3253.       $this->profile_user = isset($this->fb_params['profile_user']) ?
  3254.                             $this->fb_params['profile_user'] : null;
  3255.       $this->canvas_user  = isset($this->fb_params['canvas_user']) ?
  3256.                             $this->fb_params['canvas_user'] : null;
  3257.       $this->base_domain  = isset($this->fb_params['base_domain']) ?
  3258.                             $this->fb_params['base_domain'] : null;
  3259.  
  3260.       if (isset($this->fb_params['session_key'])) {
  3261.         $session_key =  $this->fb_params['session_key'];
  3262.       } else if (isset($this->fb_params['profile_session_key'])) {
  3263.         $session_key =  $this->fb_params['profile_session_key'];
  3264.       } else {
  3265.         $session_key = null;
  3266.       }
  3267.       $expires     = isset($this->fb_params['expires']) ?
  3268.                      $this->fb_params['expires'] : null;
  3269.       $this->set_user($user,
  3270.                       $session_key,
  3271.                       $expires);
  3272.     }
  3273.     // if no Facebook parameters were found in the GET or POST variables,
  3274.     // then fall back to cookies, which may have cached user information
  3275.     // Cookies are also used to receive session data via the Javascript API
  3276.     else if ($cookies =
  3277.              $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) {
  3278.  
  3279.       $base_domain_cookie = 'base_domain_' . $this->api_key;
  3280.       if (isset($_COOKIE[$base_domain_cookie])) {
  3281.         $this->base_domain = $_COOKIE[$base_domain_cookie];
  3282.       }
  3283.  
  3284.       // use $api_key . '_' as a prefix for the cookies in case there are
  3285.       // multiple facebook clients on the same domain.
  3286.       $expires = isset($cookies['expires']) ? $cookies['expires'] : null;
  3287.       $this->set_user($cookies['user'],
  3288.                       $cookies['session_key'],
  3289.                       $expires);
  3290.     }
  3291.     // finally, if we received no parameters, but the 'auth_token' GET var
  3292.     // is present, then we are in the middle of auth handshake,
  3293.     // so go ahead and create the session
  3294.     else if ($resolve_auth_token && isset($_GET['auth_token']) &&
  3295.              $session = $this->do_get_session($_GET['auth_token'])) {
  3296.       if ($this->generate_session_secret &&
  3297.           !empty($session['secret'])) {
  3298.         $session_secret = $session['secret'];
  3299.       }
  3300.  
  3301.       if (isset($session['base_domain'])) {
  3302.         $this->base_domain = $session['base_domain'];
  3303.       }
  3304.  
  3305.       $this->set_user($session['uid'],
  3306.                       $session['session_key'],
  3307.                       $session['expires'],
  3308.                       isset($session_secret) ? $session_secret : null);
  3309.     }
  3310.  
  3311.     return !empty($this->fb_params);
  3312.   }
  3313.  
  3314.   // Store a temporary session secret for the current session
  3315.   // for use with the JS client library
  3316.   public function promote_session() {
  3317.     try {
  3318.       $session_secret = $this->api_client->auth_promoteSession();
  3319.       if (!$this->in_fb_canvas()) {
  3320.         $this->set_cookies($this->user, $this->api_client->session_key, $this->session_expires, $session_secret);
  3321.       }
  3322.       return $session_secret;
  3323.     } catch (FacebookRestClientException $e) {
  3324.       // API_EC_PARAM means we don't have a logged in user, otherwise who
  3325.       // knows what it means, so just throw it.
  3326.       if ($e->getCode() != FacebookAPIErrorCodes::API_EC_PARAM) {
  3327.         throw $e;
  3328.       }
  3329.     }
  3330.   }
  3331.  
  3332.   public function do_get_session($auth_token) {
  3333.     try {
  3334.       return $this->api_client->auth_getSession($auth_token, $this->generate_session_secret);
  3335.     } catch (FacebookRestClientException $e) {
  3336.       // API_EC_PARAM means we don't have a logged in user, otherwise who
  3337.       // knows what it means, so just throw it.
  3338.       if ($e->getCode() != FacebookAPIErrorCodes::API_EC_PARAM) {
  3339.         throw $e;
  3340.       }
  3341.     }
  3342.   }
  3343.  
  3344.   // Invalidate the session currently being used, and clear any state associated with it
  3345.   public function expire_session() {
  3346.     if ($this->api_client->auth_expireSession()) {
  3347.       if (!$this->in_fb_canvas() && isset($_COOKIE[$this->api_key . '_user'])) {
  3348.         $cookies = array('user', 'session_key', 'expires', 'ss');
  3349.         foreach ($cookies as $name) {
  3350.           setcookie($this->api_key . '_' . $name, false, time() - 3600);
  3351.           unset($_COOKIE[$this->api_key . '_' . $name]);
  3352.         }
  3353.         setcookie($this->api_key, false, time() - 3600);
  3354.         unset($_COOKIE[$this->api_key]);
  3355.       }
  3356.  
  3357.       // now, clear the rest of the stored state
  3358.       $this->user = 0;
  3359.       $this->api_client->session_key = 0;
  3360.       return true;
  3361.     } else {
  3362.       return false;
  3363.     }
  3364.   }
  3365.  
  3366.   public function redirect($url) {
  3367.     if ($this->in_fb_canvas()) {
  3368.       echo '<fb:redirect url="' . $url . '"/>';
  3369.     } else if (preg_match('/^https?:\/\/([^\/]*\.)?facebook\.com(:\d+)?/i', $url)) {
  3370.       // make sure facebook.com url's load in the full frame so that we don't
  3371.       // get a frame within a frame.
  3372.       echo "<script type=\"text/javascript\">\ntop.location.href = \"$url\";\n</script>";
  3373.     } else {
  3374.       header('Location: ' . $url);
  3375.     }
  3376.     exit;
  3377.   }
  3378.  
  3379.   public function in_frame() {
  3380.     return isset($this->fb_params['in_canvas']) || isset($this->fb_params['in_iframe']);
  3381.   }
  3382.   public function in_fb_canvas() {
  3383.     return isset($this->fb_params['in_canvas']);
  3384.   }
  3385.  
  3386.   public function get_loggedin_user() {
  3387.     return $this->user;
  3388.   }
  3389.  
  3390.   public function get_canvas_user() {
  3391.     return $this->canvas_user;
  3392.   }
  3393.  
  3394.   public function get_profile_user() {
  3395.     return $this->profile_user;
  3396.   }
  3397.  
  3398.   public static function current_url() {
  3399.     return 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  3400.   }
  3401.  
  3402.   // require_add and require_install have been removed.
  3403.   // see http://developer.facebook.com/news.php?blog=1&story=116 for more details
  3404.   public function require_login() {
  3405.     if ($user = $this->get_loggedin_user()) {
  3406.       return $user;
  3407.     }
  3408.     $this->redirect($this->get_login_url(self::current_url(), $this->in_frame()));
  3409.   }
  3410.  
  3411.   public function require_frame() {
  3412.     if (!$this->in_frame()) {
  3413.       $this->redirect($this->get_login_url(self::current_url(), true));
  3414.     }
  3415.   }
  3416.  
  3417.   public static function get_facebook_url($subdomain='www') {
  3418.     return 'http://' . $subdomain . '.facebook.com';
  3419.   }
  3420.  
  3421.   public function get_install_url($next=null) {
  3422.     // this was renamed, keeping for compatibility's sake
  3423.     return $this->get_add_url($next);
  3424.   }
  3425.  
  3426.   public function get_add_url($next=null) {
  3427.     return self::get_facebook_url().'/add.php?api_key='.$this->api_key .
  3428.       ($next ? '&next=' . urlencode($next) : '');
  3429.   }
  3430.  
  3431.   public function get_login_url($next, $canvas) {
  3432.     return self::get_facebook_url().'/login.php?v=1.0&api_key=' . $this->api_key .
  3433.       ($next ? '&next=' . urlencode($next)  : '') .
  3434.       ($canvas ? '&canvas' : '');
  3435.   }
  3436.  
  3437.   public function set_user($user, $session_key, $expires=null, $session_secret=null) {
  3438.     if (!$this->in_fb_canvas() && (!isset($_COOKIE[$this->api_key . '_user'])
  3439.                                    || $_COOKIE[$this->api_key . '_user'] != $user)) {
  3440.       $this->set_cookies($user, $session_key, $expires, $session_secret);
  3441.     }
  3442.     $this->user = $user;
  3443.     $this->api_client->session_key = $session_key;
  3444.     $this->session_expires = $expires;
  3445.   }
  3446.  
  3447.   public function set_cookies($user, $session_key, $expires=null, $session_secret=null) {
  3448.     $cookies = array();
  3449.     $cookies['user'] = $user;
  3450.     $cookies['session_key'] = $session_key;
  3451.     if ($expires != null) {
  3452.       $cookies['expires'] = $expires;
  3453.     }
  3454.     if ($session_secret != null) {
  3455.       $cookies['ss'] = $session_secret;
  3456.     }
  3457.  
  3458.     foreach ($cookies as $name => $val) {
  3459.       setcookie($this->api_key . '_' . $name, $val, (int)$expires, '', $this->base_domain);
  3460.       $_COOKIE[$this->api_key . '_' . $name] = $val;
  3461.     }
  3462.     $sig = self::generate_sig($cookies, $this->secret);
  3463.     setcookie($this->api_key, $sig, (int)$expires, '', $this->base_domain);
  3464.     $_COOKIE[$this->api_key] = $sig;
  3465.  
  3466.     if ($this->base_domain != null) {
  3467.       $base_domain_cookie = 'base_domain_' . $this->api_key;
  3468.       setcookie($base_domain_cookie, $this->base_domain, (int)$expires, '', $this->base_domain);
  3469.       $_COOKIE[$base_domain_cookie] = $this->base_domain;
  3470.     }
  3471.   }
  3472.  
  3473.   /**
  3474.    * Tries to undo the badness of magic quotes as best we can
  3475.    * @param     string   $val   Should come directly from $_GET, $_POST, etc.
  3476.    * @return    string   val without added slashes
  3477.    */
  3478.   public static function no_magic_quotes($val) {
  3479.     if (get_magic_quotes_gpc()) {
  3480.       return stripslashes($val);
  3481.     } else {
  3482.       return $val;
  3483.     }
  3484.   }
  3485.  
  3486.   /*
  3487.    * Get the signed parameters that were sent from Facebook. Validates the set
  3488.    * of parameters against the included signature.
  3489.    *
  3490.    * Since Facebook sends data to your callback URL via unsecured means, the
  3491.    * signature is the only way to make sure that the data actually came from
  3492.    * Facebook. So if an app receives a request at the callback URL, it should
  3493.    * always verify the signature that comes with against your own secret key.
  3494.    * Otherwise, it's possible for someone to spoof a request by
  3495.    * pretending to be someone else, i.e.:
  3496.    *      www.your-callback-url.com/?fb_user=10101
  3497.    *
  3498.    * This is done automatically by verify_fb_params.
  3499.    *
  3500.    * @param  assoc  $params     a full array of external parameters.
  3501.    *                            presumed $_GET, $_POST, or $_COOKIE
  3502.    * @param  int    $timeout    number of seconds that the args are good for.
  3503.    *                            Specifically good for forcing cookies to expire.
  3504.    * @param  string $namespace  prefix string for the set of parameters we want
  3505.    *                            to verify. i.e., fb_sig or fb_post_sig
  3506.    *
  3507.    * @return  assoc the subset of parameters containing the given prefix,
  3508.    *                and also matching the signature associated with them.
  3509.    *          OR    an empty array if the params do not validate
  3510.    */
  3511.   public function get_valid_fb_params($params, $timeout=null, $namespace='fb_sig') {
  3512.     $prefix = $namespace . '_';
  3513.     $prefix_len = strlen($prefix);
  3514.     $fb_params = array();
  3515.     if (empty($params)) {
  3516.       return array();
  3517.     }
  3518.  
  3519.     foreach ($params as $name => $val) {
  3520.       // pull out only those parameters that match the prefix
  3521.       // note that the signature itself ($params[$namespace]) is not in the list
  3522.       if (strpos($name, $prefix) === 0) {
  3523.         $fb_params[substr($name, $prefix_len)] = self::no_magic_quotes($val);
  3524.       }
  3525.     }
  3526.  
  3527.     // validate that the request hasn't expired. this is most likely
  3528.     // for params that come from $_COOKIE
  3529.     if ($timeout && (!isset($fb_params['time']) || time() - $fb_params['time'] > $timeout)) {
  3530.       return array();
  3531.     }
  3532.  
  3533.     // validate that the params match the signature
  3534.     $signature = isset($params[$namespace]) ? $params[$namespace] : null;
  3535.     if (!$signature || (!$this->verify_signature($fb_params, $signature))) {
  3536.       return array();
  3537.     }
  3538.     return $fb_params;
  3539.   }
  3540.  
  3541.   /*
  3542.    * Validates that a given set of parameters match their signature.
  3543.    * Parameters all match a given input prefix, such as "fb_sig".
  3544.    *
  3545.    * @param $fb_params     an array of all Facebook-sent parameters,
  3546.    *                       not including the signature itself
  3547.    * @param $expected_sig  the expected result to check against
  3548.    */
  3549.   public function verify_signature($fb_params, $expected_sig) {
  3550.     return self::generate_sig($fb_params, $this->secret) == $expected_sig;
  3551.   }
  3552.  
  3553.   /*
  3554.    * Generate a signature using the application secret key.
  3555.    *
  3556.    * The only two entities that know your secret key are you and Facebook,
  3557.    * according to the Terms of Service. Since nobody else can generate
  3558.    * the signature, you can rely on it to verify that the information
  3559.    * came from Facebook.
  3560.    *
  3561.    * @param $params_array   an array of all Facebook-sent parameters,
  3562.    *                        NOT INCLUDING the signature itself
  3563.    * @param $secret         your app's secret key
  3564.    *
  3565.    * @return a hash to be checked against the signature provided by Facebook
  3566.    */
  3567.   public static function generate_sig($params_array, $secret) {
  3568.     $str = '';
  3569.  
  3570.     ksort($params_array);
  3571.     // Note: make sure that the signature parameter is not already included in
  3572.     //       $params_array.
  3573.     foreach ($params_array as $k=>$v) {
  3574.       $str .= "$k=$v";
  3575.     }
  3576.     $str .= $secret;
  3577.  
  3578.     return md5($str);
  3579.   }
  3580.  
  3581.   public function encode_validationError($summary, $message) {
  3582.     return json_encode(
  3583.                array('errorCode'    => FACEBOOK_API_VALIDATION_ERROR,
  3584.                      'errorTitle'   => $summary,
  3585.                      'errorMessage' => $message));
  3586.   }
  3587.  
  3588.   public function encode_multiFeedStory($feed, $next) {
  3589.     return json_encode(
  3590.                array('method'   => 'multiFeedStory',
  3591.                      'content'  =>
  3592.                      array('next' => $next,
  3593.                            'feed' => $feed)));
  3594.   }
  3595.  
  3596.   public function encode_feedStory($feed, $next) {
  3597.     return json_encode(
  3598.                array('method'   => 'feedStory',
  3599.                      'content'  =>
  3600.                      array('next' => $next,
  3601.                            'feed' => $feed)));
  3602.   }
  3603.  
  3604.   public function create_templatizedFeedStory($title_template, $title_data=array(),
  3605.                                     $body_template='', $body_data = array(), $body_general=null,
  3606.                                     $image_1=null, $image_1_link=null,
  3607.                                     $image_2=null, $image_2_link=null,
  3608.                                     $image_3=null, $image_3_link=null,
  3609.                                     $image_4=null, $image_4_link=null) {
  3610.     return array('title_template'=> $title_template,
  3611.                  'title_data'   => $title_data,
  3612.                  'body_template'=> $body_template,
  3613.                  'body_data'    => $body_data,
  3614.                  'body_general' => $body_general,
  3615.                  'image_1'      => $image_1,
  3616.                  'image_1_link' => $image_1_link,
  3617.                  'image_2'      => $image_2,
  3618.                  'image_2_link' => $image_2_link,
  3619.                  'image_3'      => $image_3,
  3620.                  'image_3_link' => $image_3_link,
  3621.                  'image_4'      => $image_4,
  3622.                  'image_4_link' => $image_4_link);
  3623.   }
  3624.  
  3625.  
  3626. }
  3627.  
  3628. // Copyright 2004-2009 Facebook. All Rights Reserved.
  3629. //
  3630. // +---------------------------------------------------------------------------+
  3631. // | Facebook Platform PHP5 client                                             |
  3632. // +---------------------------------------------------------------------------+
  3633. // | Copyright (c) 2007 Facebook, Inc.                                         |
  3634. // | All rights reserved.                                                      |
  3635. // |                                                                           |
  3636. // | Redistribution and use in source and binary forms, with or without        |
  3637. // | modification, are permitted provided that the following conditions        |
  3638. // | are met:                                                                  |
  3639. // |                                                                           |
  3640. // | 1. Redistributions of source code must retain the above copyright         |
  3641. // |    notice, this list of conditions and the following disclaimer.          |
  3642. // | 2. Redistributions in binary form must reproduce the above copyright      |
  3643. // |    notice, this list of conditions and the following disclaimer in the    |
  3644. // |    documentation and/or other materials provided with the distribution.   |
  3645. // |                                                                           |
  3646. // | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
  3647. // | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
  3648. // | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
  3649. // | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  3650. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
  3651. // | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  3652. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
  3653. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
  3654. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
  3655. // | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
  3656. // +---------------------------------------------------------------------------+
  3657. // | For help with this library, contact developers-help@facebook.com          |
  3658. // +---------------------------------------------------------------------------+
  3659. //
  3660.  
  3661. /**
  3662.  *  This class extends and modifies the "Facebook" class to better
  3663.  *  suit desktop apps.
  3664.  */
  3665. class FacebookDesktop extends Facebook {
  3666.   // the application secret, which differs from the session secret
  3667.   public $app_secret;
  3668.   public $verify_sig;
  3669.  
  3670.   public function __construct($api_key, $secret) {
  3671.     $this->app_secret = $secret;
  3672.     $this->verify_sig = false;
  3673.     parent::__construct($api_key, $secret);
  3674.   }
  3675.  
  3676.   public function do_get_session($auth_token) {
  3677.     $this->api_client->secret = $this->app_secret;
  3678.     $this->api_client->session_key = null;
  3679.     $session_info = parent::do_get_session($auth_token);
  3680.     if (!empty($session_info['secret'])) {
  3681.       // store the session secret
  3682.       $this->set_session_secret($session_info['secret']);
  3683.     }
  3684.     return $session_info;
  3685.   }
  3686.  
  3687.   public function set_session_secret($session_secret) {
  3688.     $this->secret = $session_secret;
  3689.     $this->api_client->secret = $session_secret;
  3690.   }
  3691.  
  3692.   public function require_login() {
  3693.     if ($this->get_loggedin_user()) {
  3694.       try {
  3695.         // try a session-based API call to ensure that we have the correct
  3696.         // session secret
  3697.         $user = $this->api_client->users_getLoggedInUser();
  3698.  
  3699.         // now that we have a valid session secret, verify the signature
  3700.         $this->verify_sig = true;
  3701.         if ($this->validate_fb_params(false)) {
  3702.           return $user;
  3703.         } else {
  3704.           // validation failed
  3705.           return null;
  3706.         }
  3707.       } catch (FacebookRestClientException $ex) {
  3708.         if (isset($_GET['auth_token'])) {
  3709.           // if we have an auth_token, use it to establish a session
  3710.           $session_info = $this->do_get_session($_GET['auth_token']);
  3711.           if ($session_info) {
  3712.             return $session_info['uid'];
  3713.           }
  3714.         }
  3715.       }
  3716.     }
  3717.     // if we get here, we need to redirect the user to log in
  3718.     $this->redirect($this->get_login_url(self::current_url(), $this->in_fb_canvas()));
  3719.   }
  3720.  
  3721.   public function verify_signature($fb_params, $expected_sig) {
  3722.     // we don't want to verify the signature until we have a valid
  3723.     // session secret
  3724.     if ($this->verify_sig) {
  3725.       return parent::verify_signature($fb_params, $expected_sig);
  3726.     } else {
  3727.       return true;
  3728.     }
  3729.   }
  3730. }
  3731.  
  3732. if (!function_exists('json_encode')) {
  3733.  
  3734. /**
  3735.  * Converts to and from JSON format.
  3736.  *
  3737.  * JSON (JavaScript Object Notation) is a lightweight data-interchange
  3738.  * format. It is easy for humans to read and write. It is easy for machines
  3739.  * to parse and generate. It is based on a subset of the JavaScript
  3740.  * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
  3741.  * This feature can also be found in  Python. JSON is a text format that is
  3742.  * completely language independent but uses conventions that are familiar
  3743.  * to programmers of the C-family of languages, including C, C++, C#, Java,
  3744.  * JavaScript, Perl, TCL, and many others. These properties make JSON an
  3745.  * ideal data-interchange language.
  3746.  *
  3747.  * This package provides a simple encoder and decoder for JSON notation. It
  3748.  * is intended for use with client-side Javascript applications that make
  3749.  * use of HTTPRequest to perform server communication functions - data can
  3750.  * be encoded into JSON notation for use in a client-side javascript, or
  3751.  * decoded from incoming Javascript requests. JSON format is native to
  3752.  * Javascript, and can be directly eval()'ed with no further parsing
  3753.  * overhead
  3754.  *
  3755.  * All strings should be in ASCII or UTF-8 format!
  3756.  *
  3757.  * LICENSE: Redistribution and use in source and binary forms, with or
  3758.  * without modification, are permitted provided that the following
  3759.  * conditions are met: Redistributions of source code must retain the
  3760.  * above copyright notice, this list of conditions and the following
  3761.  * disclaimer. Redistributions in binary form must reproduce the above
  3762.  * copyright notice, this list of conditions and the following disclaimer
  3763.  * in the documentation and/or other materials provided with the
  3764.  * distribution.
  3765.  *
  3766.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  3767.  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  3768.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  3769.  * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  3770.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  3771.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  3772.  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  3773.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  3774.  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  3775.  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  3776.  * DAMAGE.
  3777.  *
  3778.  * @category
  3779.  * @package     Services_JSON
  3780.  * @author      Michal Migurski <mike-json@teczno.com>
  3781.  * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
  3782.  * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
  3783.  * @copyright   2005 Michal Migurski
  3784.  * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
  3785.  * @license     http://www.opensource.org/licenses/bsd-license.php
  3786.  * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
  3787.  */
  3788.  
  3789. /**
  3790.  * Marker constant for Services_JSON::decode(), used to flag stack state
  3791.  */
  3792. define('SERVICES_JSON_SLICE',   1);
  3793.  
  3794. /**
  3795.  * Marker constant for Services_JSON::decode(), used to flag stack state
  3796.  */
  3797. define('SERVICES_JSON_IN_STR',  2);
  3798.  
  3799. /**
  3800.  * Marker constant for Services_JSON::decode(), used to flag stack state
  3801.  */
  3802. define('SERVICES_JSON_IN_ARR',  3);
  3803.  
  3804. /**
  3805.  * Marker constant for Services_JSON::decode(), used to flag stack state
  3806.  */
  3807. define('SERVICES_JSON_IN_OBJ',  4);
  3808.  
  3809. /**
  3810.  * Marker constant for Services_JSON::decode(), used to flag stack state
  3811.  */
  3812. define('SERVICES_JSON_IN_CMT', 5);
  3813.  
  3814. /**
  3815.  * Behavior switch for Services_JSON::decode()
  3816.  */
  3817. define('SERVICES_JSON_LOOSE_TYPE', 16);
  3818.  
  3819. /**
  3820.  * Behavior switch for Services_JSON::decode()
  3821.  */
  3822. define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
  3823.  
  3824. /**
  3825.  * Converts to and from JSON format.
  3826.  *
  3827.  * Brief example of use:
  3828.  *
  3829.  * <code>
  3830.  * // create a new instance of Services_JSON
  3831.  * $json = new Services_JSON();
  3832.  *
  3833.  * // convert a complexe value to JSON notation, and send it to the browser
  3834.  * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
  3835.  * $output = $json->encode($value);
  3836.  *
  3837.  * print($output);
  3838.  * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
  3839.  *
  3840.  * // accept incoming POST data, assumed to be in JSON notation
  3841.  * $input = file_get_contents('php://input', 1000000);
  3842.  * $value = $json->decode($input);
  3843.  * </code>
  3844.  */
  3845. class Services_JSON
  3846. {
  3847.    /**
  3848.     * constructs a new JSON instance
  3849.     *
  3850.     * @param    int     $use    object behavior flags; combine with boolean-OR
  3851.     *
  3852.     *                           possible values:
  3853.     *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
  3854.     *                                   "{...}" syntax creates associative arrays
  3855.     *                                   instead of objects in decode().
  3856.     *                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
  3857.     *                                   Values which can't be encoded (e.g. resources)
  3858.     *                                   appear as NULL instead of throwing errors.
  3859.     *                                   By default, a deeply-nested resource will
  3860.     *                                   bubble up with an error, so all return values
  3861.     *                                   from encode() should be checked with isError()
  3862.     */
  3863.     function Services_JSON($use = 0)
  3864.     {
  3865.         $this->use = $use;
  3866.     }
  3867.  
  3868.    /**
  3869.     * convert a string from one UTF-16 char to one UTF-8 char
  3870.     *
  3871.     * Normally should be handled by mb_convert_encoding, but
  3872.     * provides a slower PHP-only method for installations
  3873.     * that lack the multibye string extension.
  3874.     *
  3875.     * @param    string  $utf16  UTF-16 character
  3876.     * @return   string  UTF-8 character
  3877.     * @access   private
  3878.     */
  3879.     function utf162utf8($utf16)
  3880.     {
  3881.         // oh please oh please oh please oh please oh please
  3882.         if(function_exists('mb_convert_encoding')) {
  3883.             return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
  3884.         }
  3885.  
  3886.         $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
  3887.  
  3888.         switch(true) {
  3889.             case ((0x7F & $bytes) == $bytes):
  3890.                 // this case should never be reached, because we are in ASCII range
  3891.                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  3892.                 return chr(0x7F & $bytes);
  3893.  
  3894.             case (0x07FF & $bytes) == $bytes:
  3895.                 // return a 2-byte UTF-8 character
  3896.                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  3897.                 return chr(0xC0 | (($bytes >> 6) & 0x1F))
  3898.                      . chr(0x80 | ($bytes & 0x3F));
  3899.  
  3900.             case (0xFFFF & $bytes) == $bytes:
  3901.                 // return a 3-byte UTF-8 character
  3902.                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  3903.                 return chr(0xE0 | (($bytes >> 12) & 0x0F))
  3904.                      . chr(0x80 | (($bytes >> 6) & 0x3F))
  3905.                      . chr(0x80 | ($bytes & 0x3F));
  3906.         }
  3907.  
  3908.         // ignoring UTF-32 for now, sorry
  3909.         return '';
  3910.     }
  3911.  
  3912.    /**
  3913.     * convert a string from one UTF-8 char to one UTF-16 char
  3914.     *
  3915.     * Normally should be handled by mb_convert_encoding, but
  3916.     * provides a slower PHP-only method for installations
  3917.     * that lack the multibye string extension.
  3918.     *
  3919.     * @param    string  $utf8   UTF-8 character
  3920.     * @return   string  UTF-16 character
  3921.     * @access   private
  3922.     */
  3923.     function utf82utf16($utf8)
  3924.     {
  3925.         // oh please oh please oh please oh please oh please
  3926.         if(function_exists('mb_convert_encoding')) {
  3927.             return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
  3928.         }
  3929.  
  3930.         switch(strlen($utf8)) {
  3931.             case 1:
  3932.                 // this case should never be reached, because we are in ASCII range
  3933.                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  3934.                 return $utf8;
  3935.  
  3936.             case 2:
  3937.                 // return a UTF-16 character from a 2-byte UTF-8 char
  3938.                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  3939.                 return chr(0x07 & (ord($utf8{0}) >> 2))
  3940.                      . chr((0xC0 & (ord($utf8{0}) << 6))
  3941.                          | (0x3F & ord($utf8{1})));
  3942.  
  3943.             case 3:
  3944.                 // return a UTF-16 character from a 3-byte UTF-8 char
  3945.                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  3946.                 return chr((0xF0 & (ord($utf8{0}) << 4))
  3947.                          | (0x0F & (ord($utf8{1}) >> 2)))
  3948.                      . chr((0xC0 & (ord($utf8{1}) << 6))
  3949.                          | (0x7F & ord($utf8{2})));
  3950.         }
  3951.  
  3952.         // ignoring UTF-32 for now, sorry
  3953.         return '';
  3954.     }
  3955.  
  3956.    /**
  3957.     * encodes an arbitrary variable into JSON format
  3958.     *
  3959.     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
  3960.     *                           see argument 1 to Services_JSON() above for array-parsing behavior.
  3961.     *                           if var is a strng, note that encode() always expects it
  3962.     *                           to be in ASCII or UTF-8 format!
  3963.     *
  3964.     * @return   mixed   JSON string representation of input var or an error if a problem occurs
  3965.     * @access   public
  3966.     */
  3967.     function encode($var)
  3968.     {
  3969.         switch (gettype($var)) {
  3970.             case 'boolean':
  3971.                 return $var ? 'true' : 'false';
  3972.  
  3973.             case 'NULL':
  3974.                 return 'null';
  3975.  
  3976.             case 'integer':
  3977.                 return (int) $var;
  3978.  
  3979.             case 'double':
  3980.             case 'float':
  3981.                 return (float) $var;
  3982.  
  3983.             case 'string':
  3984.                 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
  3985.                 $ascii = '';
  3986.                 $strlen_var = strlen($var);
  3987.  
  3988.                /*
  3989.                 * Iterate over every character in the string,
  3990.                 * escaping with a slash or encoding to UTF-8 where necessary
  3991.                 */
  3992.                 for ($c = 0; $c < $strlen_var; ++$c) {
  3993.  
  3994.                     $ord_var_c = ord($var{$c});
  3995.  
  3996.                     switch (true) {
  3997.                         case $ord_var_c == 0x08:
  3998.                             $ascii .= '\b';
  3999.                             break;
  4000.                         case $ord_var_c == 0x09:
  4001.                             $ascii .= '\t';
  4002.                             break;
  4003.                         case $ord_var_c == 0x0A:
  4004.                             $ascii .= '\n';
  4005.                             break;
  4006.                         case $ord_var_c == 0x0C:
  4007.                             $ascii .= '\f';
  4008.                             break;
  4009.                         case $ord_var_c == 0x0D:
  4010.                             $ascii .= '\r';
  4011.                             break;
  4012.  
  4013.                         case $ord_var_c == 0x22:
  4014.                         case $ord_var_c == 0x2F:
  4015.                         case $ord_var_c == 0x5C:
  4016.                             // double quote, slash, slosh
  4017.                             $ascii .= '\\'.$var{$c};
  4018.                             break;
  4019.  
  4020.                         case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
  4021.                             // characters U-00000000 - U-0000007F (same as ASCII)
  4022.                             $ascii .= $var{$c};
  4023.                             break;
  4024.  
  4025.                         case (($ord_var_c & 0xE0) == 0xC0):
  4026.                             // characters U-00000080 - U-000007FF, mask 110XXXXX
  4027.                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4028.                             $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
  4029.                             $c += 1;
  4030.                             $utf16 = $this->utf82utf16($char);
  4031.                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
  4032.                             break;
  4033.  
  4034.                         case (($ord_var_c & 0xF0) == 0xE0):
  4035.                             // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  4036.                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4037.                             $char = pack('C*', $ord_var_c,
  4038.                                          ord($var{$c + 1}),
  4039.                                          ord($var{$c + 2}));
  4040.                             $c += 2;
  4041.                             $utf16 = $this->utf82utf16($char);
  4042.                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
  4043.                             break;
  4044.  
  4045.                         case (($ord_var_c & 0xF8) == 0xF0):
  4046.                             // characters U-00010000 - U-001FFFFF, mask 11110XXX
  4047.                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4048.                             $char = pack('C*', $ord_var_c,
  4049.                                          ord($var{$c + 1}),
  4050.                                          ord($var{$c + 2}),
  4051.                                          ord($var{$c + 3}));
  4052.                             $c += 3;
  4053.                             $utf16 = $this->utf82utf16($char);
  4054.                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
  4055.                             break;
  4056.  
  4057.                         case (($ord_var_c & 0xFC) == 0xF8):
  4058.                             // characters U-00200000 - U-03FFFFFF, mask 111110XX
  4059.                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4060.                             $char = pack('C*', $ord_var_c,
  4061.                                          ord($var{$c + 1}),
  4062.                                          ord($var{$c + 2}),
  4063.                                          ord($var{$c + 3}),
  4064.                                          ord($var{$c + 4}));
  4065.                             $c += 4;
  4066.                             $utf16 = $this->utf82utf16($char);
  4067.                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
  4068.                             break;
  4069.  
  4070.                         case (($ord_var_c & 0xFE) == 0xFC):
  4071.                             // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  4072.                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4073.                             $char = pack('C*', $ord_var_c,
  4074.                                          ord($var{$c + 1}),
  4075.                                          ord($var{$c + 2}),
  4076.                                          ord($var{$c + 3}),
  4077.                                          ord($var{$c + 4}),
  4078.                                          ord($var{$c + 5}));
  4079.                             $c += 5;
  4080.                             $utf16 = $this->utf82utf16($char);
  4081.                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
  4082.                             break;
  4083.                     }
  4084.                 }
  4085.  
  4086.                 return '"'.$ascii.'"';
  4087.  
  4088.             case 'array':
  4089.                /*
  4090.                 * As per JSON spec if any array key is not an integer
  4091.                 * we must treat the the whole array as an object. We
  4092.                 * also try to catch a sparsely populated associative
  4093.                 * array with numeric keys here because some JS engines
  4094.                 * will create an array with empty indexes up to
  4095.                 * max_index which can cause memory issues and because
  4096.                 * the keys, which may be relevant, will be remapped
  4097.                 * otherwise.
  4098.                 *
  4099.                 * As per the ECMA and JSON specification an object may
  4100.                 * have any string as a property. Unfortunately due to
  4101.                 * a hole in the ECMA specification if the key is a
  4102.                 * ECMA reserved word or starts with a digit the
  4103.                 * parameter is only accessible using ECMAScript's
  4104.                 * bracket notation.
  4105.                 */
  4106.  
  4107.                 // treat as a JSON object
  4108.                 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
  4109.                     $properties = array_map(array($this, 'name_value'),
  4110.                                             array_keys($var),
  4111.                                             array_values($var));
  4112.  
  4113.                     foreach($properties as $property) {
  4114.                         if(Services_JSON::isError($property)) {
  4115.                             return $property;
  4116.                         }
  4117.                     }
  4118.  
  4119.                     return '{' . join(',', $properties) . '}';
  4120.                 }
  4121.  
  4122.                 // treat it like a regular array
  4123.                 $elements = array_map(array($this, 'encode'), $var);
  4124.  
  4125.                 foreach($elements as $element) {
  4126.                     if(Services_JSON::isError($element)) {
  4127.                         return $element;
  4128.                     }
  4129.                 }
  4130.  
  4131.                 return '[' . join(',', $elements) . ']';
  4132.  
  4133.             case 'object':
  4134.                 $vars = get_object_vars($var);
  4135.  
  4136.                 $properties = array_map(array($this, 'name_value'),
  4137.                                         array_keys($vars),
  4138.                                         array_values($vars));
  4139.  
  4140.                 foreach($properties as $property) {
  4141.                     if(Services_JSON::isError($property)) {
  4142.                         return $property;
  4143.                     }
  4144.                 }
  4145.  
  4146.                 return '{' . join(',', $properties) . '}';
  4147.  
  4148.             default:
  4149.                 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
  4150.                     ? 'null'
  4151.                     : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
  4152.         }
  4153.     }
  4154.  
  4155.    /**
  4156.     * array-walking function for use in generating JSON-formatted name-value pairs
  4157.     *
  4158.     * @param    string  $name   name of key to use
  4159.     * @param    mixed   $value  reference to an array element to be encoded
  4160.     *
  4161.     * @return   string  JSON-formatted name-value pair, like '"name":value'
  4162.     * @access   private
  4163.     */
  4164.     function name_value($name, $value)
  4165.     {
  4166.         $encoded_value = $this->encode($value);
  4167.  
  4168.         if(Services_JSON::isError($encoded_value)) {
  4169.             return $encoded_value;
  4170.         }
  4171.  
  4172.         return $this->encode(strval($name)) . ':' . $encoded_value;
  4173.     }
  4174.  
  4175.    /**
  4176.     * reduce a string by removing leading and trailing comments and whitespace
  4177.     *
  4178.     * @param    $str    string      string value to strip of comments and whitespace
  4179.     *
  4180.     * @return   string  string value stripped of comments and whitespace
  4181.     * @access   private
  4182.     */
  4183.     function reduce_string($str)
  4184.     {
  4185.         $str = preg_replace(array(
  4186.  
  4187.                 // eliminate single line comments in '// ...' form
  4188.                 '#^\s*//(.+)$#m',
  4189.  
  4190.                 // eliminate multi-line comments in '/* ... */' form, at start of string
  4191.                 '#^\s*/\*(.+)\*/#Us',
  4192.  
  4193.                 // eliminate multi-line comments in '/* ... */' form, at end of string
  4194.                 '#/\*(.+)\*/\s*$#Us'
  4195.  
  4196.             ), '', $str);
  4197.  
  4198.         // eliminate extraneous space
  4199.         return trim($str);
  4200.     }
  4201.  
  4202.    /**
  4203.     * decodes a JSON string into appropriate variable
  4204.     *
  4205.     * @param    string  $str    JSON-formatted string
  4206.     *
  4207.     * @return   mixed   number, boolean, string, array, or object
  4208.     *                   corresponding to given JSON input string.
  4209.     *                   See argument 1 to Services_JSON() above for object-output behavior.
  4210.     *                   Note that decode() always returns strings
  4211.     *                   in ASCII or UTF-8 format!
  4212.     * @access   public
  4213.     */
  4214.     function decode($str)
  4215.     {
  4216.         $str = $this->reduce_string($str);
  4217.  
  4218.         switch (strtolower($str)) {
  4219.             case 'true':
  4220.                 return true;
  4221.  
  4222.             case 'false':
  4223.                 return false;
  4224.  
  4225.             case 'null':
  4226.                 return null;
  4227.  
  4228.             default:
  4229.                 $m = array();
  4230.  
  4231.                 if (is_numeric($str)) {
  4232.                     // Lookie-loo, it's a number
  4233.  
  4234.                     // This would work on its own, but I'm trying to be
  4235.                     // good about returning integers where appropriate:
  4236.                     // return (float)$str;
  4237.  
  4238.                     // Return float or int, as appropriate
  4239.                     return ((float)$str == (integer)$str)
  4240.                         ? (integer)$str
  4241.                         : (float)$str;
  4242.  
  4243.                 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
  4244.                     // STRINGS RETURNED IN UTF-8 FORMAT
  4245.                     $delim = substr($str, 0, 1);
  4246.                     $chrs = substr($str, 1, -1);
  4247.                     $utf8 = '';
  4248.                     $strlen_chrs = strlen($chrs);
  4249.  
  4250.                     for ($c = 0; $c < $strlen_chrs; ++$c) {
  4251.  
  4252.                         $substr_chrs_c_2 = substr($chrs, $c, 2);
  4253.                         $ord_chrs_c = ord($chrs{$c});
  4254.  
  4255.                         switch (true) {
  4256.                             case $substr_chrs_c_2 == '\b':
  4257.                                 $utf8 .= chr(0x08);
  4258.                                 ++$c;
  4259.                                 break;
  4260.                             case $substr_chrs_c_2 == '\t':
  4261.                                 $utf8 .= chr(0x09);
  4262.                                 ++$c;
  4263.                                 break;
  4264.                             case $substr_chrs_c_2 == '\n':
  4265.                                 $utf8 .= chr(0x0A);
  4266.                                 ++$c;
  4267.                                 break;
  4268.                             case $substr_chrs_c_2 == '\f':
  4269.                                 $utf8 .= chr(0x0C);
  4270.                                 ++$c;
  4271.                                 break;
  4272.                             case $substr_chrs_c_2 == '\r':
  4273.                                 $utf8 .= chr(0x0D);
  4274.                                 ++$c;
  4275.                                 break;
  4276.  
  4277.                             case $substr_chrs_c_2 == '\\"':
  4278.                             case $substr_chrs_c_2 == '\\\'':
  4279.                             case $substr_chrs_c_2 == '\\\\':
  4280.                             case $substr_chrs_c_2 == '\\/':
  4281.                                 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
  4282.                                    ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
  4283.                                     $utf8 .= $chrs{++$c};
  4284.                                 }
  4285.                                 break;
  4286.  
  4287.                             case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
  4288.                                 // single, escaped unicode character
  4289.                                 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
  4290.                                        . chr(hexdec(substr($chrs, ($c + 4), 2)));
  4291.                                 $utf8 .= $this->utf162utf8($utf16);
  4292.                                 $c += 5;
  4293.                                 break;
  4294.  
  4295.                             case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
  4296.                                 $utf8 .= $chrs{$c};
  4297.                                 break;
  4298.  
  4299.                             case ($ord_chrs_c & 0xE0) == 0xC0:
  4300.                                 // characters U-00000080 - U-000007FF, mask 110XXXXX
  4301.                                 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4302.                                 $utf8 .= substr($chrs, $c, 2);
  4303.                                 ++$c;
  4304.                                 break;
  4305.  
  4306.                             case ($ord_chrs_c & 0xF0) == 0xE0:
  4307.                                 // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  4308.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4309.                                 $utf8 .= substr($chrs, $c, 3);
  4310.                                 $c += 2;
  4311.                                 break;
  4312.  
  4313.                             case ($ord_chrs_c & 0xF8) == 0xF0:
  4314.                                 // characters U-00010000 - U-001FFFFF, mask 11110XXX
  4315.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4316.                                 $utf8 .= substr($chrs, $c, 4);
  4317.                                 $c += 3;
  4318.                                 break;
  4319.  
  4320.                             case ($ord_chrs_c & 0xFC) == 0xF8:
  4321.                                 // characters U-00200000 - U-03FFFFFF, mask 111110XX
  4322.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4323.                                 $utf8 .= substr($chrs, $c, 5);
  4324.                                 $c += 4;
  4325.                                 break;
  4326.  
  4327.                             case ($ord_chrs_c & 0xFE) == 0xFC:
  4328.                                 // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  4329.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  4330.                                 $utf8 .= substr($chrs, $c, 6);
  4331.                                 $c += 5;
  4332.                                 break;
  4333.  
  4334.                         }
  4335.  
  4336.                     }
  4337.  
  4338.                     return $utf8;
  4339.  
  4340.                 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
  4341.                     // array, or object notation
  4342.  
  4343.                     if ($str{0} == '[') {
  4344.                         $stk = array(SERVICES_JSON_IN_ARR);
  4345.                         $arr = array();
  4346.                     } else {
  4347.                         if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
  4348.                             $stk = array(SERVICES_JSON_IN_OBJ);
  4349.                             $obj = array();
  4350.                         } else {
  4351.                             $stk = array(SERVICES_JSON_IN_OBJ);
  4352.                             $obj = new stdClass();
  4353.                         }
  4354.                     }
  4355.  
  4356.                     array_push($stk, array('what'  => SERVICES_JSON_SLICE,
  4357.                                            'where' => 0,
  4358.                                            'delim' => false));
  4359.  
  4360.                     $chrs = substr($str, 1, -1);
  4361.                     $chrs = $this->reduce_string($chrs);
  4362.  
  4363.                     if ($chrs == '') {
  4364.                         if (reset($stk) == SERVICES_JSON_IN_ARR) {
  4365.                             return $arr;
  4366.  
  4367.                         } else {
  4368.                             return $obj;
  4369.  
  4370.                         }
  4371.                     }
  4372.  
  4373.                     //print("\nparsing {$chrs}\n");
  4374.  
  4375.                     $strlen_chrs = strlen($chrs);
  4376.  
  4377.                     for ($c = 0; $c <= $strlen_chrs; ++$c) {
  4378.  
  4379.                         $top = end($stk);
  4380.                         $substr_chrs_c_2 = substr($chrs, $c, 2);
  4381.  
  4382.                         if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
  4383.                             // found a comma that is not inside a string, array, etc.,
  4384.                             // OR we've reached the end of the character list
  4385.                             $slice = substr($chrs, $top['where'], ($c - $top['where']));
  4386.                             array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
  4387.                             //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  4388.  
  4389.                             if (reset($stk) == SERVICES_JSON_IN_ARR) {
  4390.                                 // we are in an array, so just push an element onto the stack
  4391.                                 array_push($arr, $this->decode($slice));
  4392.  
  4393.                             } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
  4394.                                 // we are in an object, so figure
  4395.                                 // out the property name and set an
  4396.                                 // element in an associative array,
  4397.                                 // for now
  4398.                                 $parts = array();
  4399.                                
  4400.                                 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
  4401.                                     // "name":value pair
  4402.                                     $key = $this->decode($parts[1]);
  4403.                                     $val = $this->decode($parts[2]);
  4404.  
  4405.                                     if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
  4406.                                         $obj[$key] = $val;
  4407.                                     } else {
  4408.                                         $obj->$key = $val;
  4409.                                     }
  4410.                                 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
  4411.                                     // name:value pair, where name is unquoted
  4412.                                     $key = $parts[1];
  4413.                                     $val = $this->decode($parts[2]);
  4414.  
  4415.                                     if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
  4416.                                         $obj[$key] = $val;
  4417.                                     } else {
  4418.                                         $obj->$key = $val;
  4419.                                     }
  4420.                                 }
  4421.  
  4422.                             }
  4423.  
  4424.                         } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
  4425.                             // found a quote, and we are not inside a string
  4426.                             array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
  4427.                             //print("Found start of string at {$c}\n");
  4428.  
  4429.                         } elseif (($chrs{$c} == $top['delim']) &&
  4430.                                  ($top['what'] == SERVICES_JSON_IN_STR) &&
  4431.                                  ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
  4432.                             // found a quote, we're in a string, and it's not escaped
  4433.                             // we know that it's not escaped becase there is _not_ an
  4434.                             // odd number of backslashes at the end of the string so far
  4435.                             array_pop($stk);
  4436.                             //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
  4437.  
  4438.                         } elseif (($chrs{$c} == '[') &&
  4439.                                  in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
  4440.                             // found a left-bracket, and we are in an array, object, or slice
  4441.                             array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
  4442.                             //print("Found start of array at {$c}\n");
  4443.  
  4444.                         } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
  4445.                             // found a right-bracket, and we're in an array
  4446.                             array_pop($stk);
  4447.                             //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  4448.  
  4449.                         } elseif (($chrs{$c} == '{') &&
  4450.                                  in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
  4451.                             // found a left-brace, and we are in an array, object, or slice
  4452.                             array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
  4453.                             //print("Found start of object at {$c}\n");
  4454.  
  4455.                         } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
  4456.                             // found a right-brace, and we're in an object
  4457.                             array_pop($stk);
  4458.                             //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  4459.  
  4460.                         } elseif (($substr_chrs_c_2 == '/*') &&
  4461.                                  in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
  4462.                             // found a comment start, and we are in an array, object, or slice
  4463.                             array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
  4464.                             $c++;
  4465.                             //print("Found start of comment at {$c}\n");
  4466.  
  4467.                         } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
  4468.                             // found a comment end, and we're in one now
  4469.                             array_pop($stk);
  4470.                             $c++;
  4471.  
  4472.                             for ($i = $top['where']; $i <= $c; ++$i)
  4473.                                 $chrs = substr_replace($chrs, ' ', $i, 1);
  4474.  
  4475.                             //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  4476.  
  4477.                         }
  4478.  
  4479.                     }
  4480.  
  4481.                     if (reset($stk) == SERVICES_JSON_IN_ARR) {
  4482.                         return $arr;
  4483.  
  4484.                     } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
  4485.                         return $obj;
  4486.  
  4487.                     }
  4488.  
  4489.                 }
  4490.         }
  4491.     }
  4492.  
  4493.     /**
  4494.      * @todo Ultimately, this should just call PEAR::isError()
  4495.      */
  4496.     function isError($data, $code = null)
  4497.     {
  4498.         if (class_exists('pear')) {
  4499.             return PEAR::isError($data, $code);
  4500.         } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
  4501.                                  is_subclass_of($data, 'services_json_error'))) {
  4502.             return true;
  4503.         }
  4504.  
  4505.         return false;
  4506.     }
  4507. }
  4508.  
  4509. if (class_exists('PEAR_Error')) {
  4510.  
  4511.     class Services_JSON_Error extends PEAR_Error
  4512.     {
  4513.         function Services_JSON_Error($message = 'unknown error', $code = null,
  4514.                                      $mode = null, $options = null, $userinfo = null)
  4515.         {
  4516.             parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
  4517.         }
  4518.     }
  4519.  
  4520. } else {
  4521.  
  4522.     /**
  4523.      * @todo Ultimately, this class shall be descended from PEAR_Error
  4524.      */
  4525.     class Services_JSON_Error
  4526.     {
  4527.         function Services_JSON_Error($message = 'unknown error', $code = null,
  4528.                                      $mode = null, $options = null, $userinfo = null)
  4529.         {
  4530.  
  4531.         }
  4532.     }
  4533.  
  4534. }
  4535.  
  4536. function json_encode($arg)
  4537. {
  4538.         global $services_json;
  4539.         if (!isset($services_json)) {
  4540.                 $services_json = new Services_JSON();
  4541.         }
  4542.         return $services_json->encode($arg);
  4543. }
  4544.  
  4545. function json_decode($arg)
  4546. {
  4547.         global $services_json;
  4548.         if (!isset($services_json)) {
  4549.                 $services_json = new Services_JSON();
  4550.         }
  4551.         return $services_json->decode($arg);
  4552. }
  4553. }
  4554.  
  4555. ?>
RAW Paste Data