5 * https://github.com/jdolitsky/AppDotNetPHP
7 * This class handles a lower level type of access to App.net. It's ideal
8 * for command line scripts and other places where you want full control
9 * over what's happening, and you're at least a little familiar with oAuth.
11 * Alternatively you can use the EZAppDotNet class which automatically takes
12 * care of a lot of the details like logging in, keeping track of tokens,
13 * etc. EZAppDotNet assumes you're accessing App.net via a browser, whereas
14 * this class tries to make no assumptions at all.
18 protected $_baseUrl = 'https://alpha-api.app.net/stream/0/';
19 protected $_authUrl = 'https://account.app.net/oauth/';
21 private $_authPostParams=array();
23 // stores the access token after login
24 private $_accessToken = null;
26 // stores the App access token if we have it
27 private $_appAccessToken = null;
29 // stores the user ID returned when fetching the auth token
30 private $_user_id = null;
32 // stores the username returned when fetching the auth token
33 private $_username = null;
35 // The total number of requests you're allowed within the alloted time period
36 private $_rateLimit = null;
38 // The number of requests you have remaining within the alloted time period
39 private $_rateLimitRemaining = null;
41 // The number of seconds remaining in the alloted time period
42 private $_rateLimitReset = null;
44 // The scope the user has
45 private $_scope = null;
48 private $_scopes=array();
51 private $_last_request = null;
52 private $_last_response = null;
55 private $_sslCA = null;
57 // the callback function to be called when an event is received from the stream
58 private $_streamCallback = null;
61 private $_streamBuffer = '';
63 // stores the curl handler for the current stream
64 private $_currentStream = null;
66 // stores the curl multi handler for the current stream
67 private $_multiStream = null;
69 // stores the number of failed connects, so we can back off multiple failures
70 private $_connectFailCounter = 0;
72 // stores the most recent stream url, so we can re-connect when needed
73 private $_streamUrl = null;
75 // keeps track of the last time we've received a packet from the api, if it's too long we'll reconnect
76 private $_lastStreamActivity = null;
78 // stores the headers received when connecting to the stream
79 private $_streamHeaders = null;
81 // response meta max_id data
82 private $_maxid = null;
84 // response meta min_id data
85 private $_minid = null;
87 // response meta more data
88 private $_more = null;
90 // response stream marker data
91 private $_last_marker = null;
93 // strip envelope response from returned value
94 private $_stripResponseEnvelope=true;
96 // if processing stream_markers or any fast stream, decrease $sleepFor
97 public $streamingSleepFor=20000;
100 * Constructs an AppDotNet PHP object with the specified client ID and
102 * @param string $client_id The client ID you received from App.net when
104 * @param string $client_secret The client secret you received from
105 * App.net when creating your app.
107 public function __construct($client_id,$client_secret) {
108 $this->_clientId = $client_id;
109 $this->_clientSecret = $client_secret;
111 // if the digicert certificate exists in the same folder as this file,
112 // remember that fact for later
113 if (file_exists(dirname(__FILE__).'/DigiCertHighAssuranceEVRootCA.pem')) {
114 $this->_sslCA = dirname(__FILE__).'/DigiCertHighAssuranceEVRootCA.pem';
119 * Set whether or not to strip Envelope Response (meta) information
120 * This option will be deprecated in the future. Is it to allow
121 * a stepped migration path between code expecting the old behavior
122 * and new behavior. When not stripped, you still can use the proper
123 * method to pull the meta information. Please start converting your code ASAP
125 public function includeResponseEnvelope() {
126 $this->_stripResponseEnvelope=false;
130 * Construct the proper Auth URL for the user to visit and either grant
131 * or not access to your app. Usually you would place this as a link for
132 * the user to client, or a redirect to send them to the auth URL.
133 * Also can be called after authentication for additional scopes
134 * @param string $callbackUri Where you want the user to be directed
135 * after authenticating with App.net. This must be one of the URIs
136 * allowed by your App.net application settings.
137 * @param array $scope An array of scopes (permissions) you wish to obtain
138 * from the user. Currently options are stream, email, write_post, follow,
139 * messages, and export. If you don't specify anything, you'll only receive
140 * access to the user's basic profile (the default).
142 public function getAuthUrl($callback_uri,$scope=null) {
144 // construct an authorization url based on our client id and other data
146 'client_id'=>$this->_clientId,
147 'response_type'=>'code',
148 'redirect_uri'=>$callback_uri,
151 $url = $this->_authUrl;
152 if ($this->_accessToken) {
153 $url .= 'authorize?';
155 $url .= 'authenticate?';
157 $url .= $this->buildQueryString($data);
160 $url .= '&scope='.implode('+',$scope);
163 // return the constructed url
168 * Call this after they return from the auth page, or anytime you need the
169 * token. For example, you could store it in a database and use
170 * setAccessToken() later on to return on behalf of the user.
172 public function getAccessToken($callback_uri) {
173 // if there's no access token set, and they're returning from
174 // the auth page with a code, use the code to get a token
175 if (!$this->_accessToken && isset($_GET['code']) && $_GET['code']) {
177 // construct the necessary elements to get a token
179 'client_id'=>$this->_clientId,
180 'client_secret'=>$this->_clientSecret,
181 'grant_type'=>'authorization_code',
182 'redirect_uri'=>$callback_uri,
183 'code'=>$_GET['code']
186 // try and fetch the token with the above data
187 $res = $this->httpReq('post',$this->_authUrl.'access_token', $data);
189 // store it for later
190 $this->_accessToken = $res['access_token'];
191 $this->_username = $res['username'];
192 $this->_user_id = $res['user_id'];
195 // return what we have (this may be a token, or it may be nothing)
196 return $this->_accessToken;
200 * Check the scope of current token to see if it has required scopes
201 * has to be done after a check
203 public function checkScopes($app_scopes) {
204 if (!count($this->_scopes)) {
205 return -1; // _scope is empty
208 foreach($app_scopes as $scope) {
209 if (!in_array($scope,$this->_scopes)) {
210 if ($scope=='public_messages') {
211 // messages works for public_messages
212 if (in_array('messages',$this->_scopes)) {
213 // if we have messages in our scopes
220 // identify the ones missing
221 if (count($missing)) {
225 return 0; // 0 missing
229 * Set the access token (eg: after retrieving it from offline storage)
230 * @param string $token A valid access token you're previously received
231 * from calling getAccessToken().
233 public function setAccessToken($token) {
234 $this->_accessToken = $token;
238 * Deauthorize the current token (delete your authorization from the API)
239 * Generally this is useful for logging users out from a web app, so they
240 * don't get automatically logged back in the next time you redirect them
241 * to the authorization URL.
243 public function deauthorizeToken() {
244 return $this->httpReq('delete',$this->_baseUrl.'token');
248 * Retrieve an app access token from the app.net API. This allows you
249 * to access the API without going through the user access flow if you
250 * just want to (eg) consume global. App access tokens are required for
251 * some actions (like streaming global). DO NOT share the return value
252 * of this function with any user (or save it in a cookie, etc). This
253 * is considered secret info for your app only.
254 * @return string The app access token
256 public function getAppAccessToken() {
258 // construct the necessary elements to get a token
260 'client_id'=>$this->_clientId,
261 'client_secret'=>$this->_clientSecret,
262 'grant_type'=>'client_credentials',
265 // try and fetch the token with the above data
266 $res = $this->httpReq('post',$this->_authUrl.'access_token', $data);
268 // store it for later
269 $this->_appAccessToken = $res['access_token'];
270 $this->_accessToken = $res['access_token'];
271 $this->_username = null;
272 $this->_user_id = null;
274 return $this->_accessToken;
278 * Returns the total number of requests you're allowed within the
279 * alloted time period.
280 * @see getRateLimitReset()
282 public function getRateLimit() {
283 return $this->_rateLimit;
287 * The number of requests you have remaining within the alloted time period
288 * @see getRateLimitReset()
290 public function getRateLimitRemaining() {
291 return $this->_rateLimitRemaining;
295 * The number of seconds remaining in the alloted time period.
296 * When this time is up you'll have getRateLimit() available again.
298 public function getRateLimitReset() {
299 return $this->_rateLimitReset;
303 * The scope the user has
305 public function getScope() {
306 return $this->_scope;
310 * Internal function, parses out important information App.net adds
313 protected function parseHeaders($response) {
314 // take out the headers
315 // set internal variables
316 // return the body/content
317 $this->_rateLimit = null;
318 $this->_rateLimitRemaining = null;
319 $this->_rateLimitReset = null;
320 $this->_scope = null;
322 $response = explode("\r\n\r\n",$response,2);
323 $headers = $response[0];
325 if($headers == 'HTTP/1.1 100 Continue') {
326 $response = explode("\r\n\r\n",$response[1],2);
327 $headers = $response[0];
330 if (isset($response[1])) {
331 $content = $response[1];
337 // this is not a good way to parse http headers
338 // it will not (for example) take into account multiline headers
339 // but what we're looking for is pretty basic, so we can ignore those shortcomings
340 $headers = explode("\r\n",$headers);
341 foreach ($headers as $header) {
342 $header = explode(': ',$header,2);
343 if (count($header)<2) {
346 list($k,$v) = $header;
348 case 'X-RateLimit-Remaining':
349 $this->_rateLimitRemaining = $v;
351 case 'X-RateLimit-Limit':
352 $this->_rateLimit = $v;
354 case 'X-RateLimit-Reset':
355 $this->_rateLimitReset = $v;
357 case 'X-OAuth-Scopes':
359 $this->_scopes=explode(',',$v);
367 * Internal function. Used to turn things like TRUE into 1, and then
368 * calls http_build_query.
370 protected function buildQueryString($array) {
371 foreach ($array as $k=>&$v) {
375 elseif ($v===false) {
380 return http_build_query($array);
385 * Internal function to handle all
386 * HTTP requests (POST,PUT,GET,DELETE)
388 protected function httpReq($act, $req, $params=array(),$contentType='application/x-www-form-urlencoded') {
389 $ch = curl_init($req);
392 curl_setopt($ch, CURLOPT_POST, true);
393 // if they passed an array, build a list of parameters from it
394 if (is_array($params) && $act != 'post-raw') {
395 $params = $this->buildQueryString($params);
397 curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
398 $headers[] = "Content-Type: ".$contentType;
400 if($act != 'post' && $act != 'post-raw') {
401 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($act));
403 if($act == 'get' && isset($params['access_token'])) {
404 $headers[] = 'Authorization: Bearer '.$params['access_token'];
406 else if ($this->_accessToken) {
407 $headers[] = 'Authorization: Bearer '.$this->_accessToken;
409 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
410 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
411 curl_setopt($ch, CURLINFO_HEADER_OUT, true);
412 curl_setopt($ch, CURLOPT_HEADER, true);
414 curl_setopt($ch, CURLOPT_CAINFO, $this->_sslCA);
416 $this->_last_response = curl_exec($ch);
417 $this->_last_request = curl_getinfo($ch,CURLINFO_HEADER_OUT);
418 $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
420 if ($http_status==0) {
421 throw new AppDotNetException('Unable to connect to '.$req);
423 if ($http_status<200 || $http_status>=300) {
424 throw new AppDotNetException('HTTP error '.$this->_last_response);
426 if ($this->_last_request===false) {
427 if (!curl_getinfo($ch,CURLINFO_SSL_VERIFYRESULT)) {
428 throw new AppDotNetException('SSL verification failed, connection terminated.');
431 $response = $this->parseHeaders($this->_last_response);
432 $response = json_decode($response,true);
434 if (isset($response['meta'])) {
435 if (isset($response['meta']['max_id'])) {
436 $this->_maxid=$response['meta']['max_id'];
437 $this->_minid=$response['meta']['min_id'];
439 if (isset($response['meta']['more'])) {
440 $this->_more=$response['meta']['more'];
442 if (isset($response['meta']['marker'])) {
443 $this->_last_marker=$response['meta']['marker'];
448 if (isset($response['error'])) {
449 if (is_array($response['error'])) {
450 throw new AppDotNetException($response['error']['message'],
451 $response['error']['code']);
454 throw new AppDotNetException($response['error']);
458 // look for response migration errors
459 elseif (isset($response['meta']) && isset($response['meta']['error_message'])) {
460 throw new AppDotNetException($response['meta']['error_message'],$response['meta']['code']);
463 // if we've received a migration response, handle it and return data only
464 elseif ($this->_stripResponseEnvelope && isset($response['meta']) && isset($response['data'])) {
465 return $response['data'];
468 // else non response migration response, just return it
476 * Get max_id from last meta response data envelope
478 public function getResponseMaxID() {
479 return $this->_maxid;
483 * Get min_id from last meta response data envelope
485 public function getResponseMinID() {
486 return $this->_minid;
490 * Get more from last meta response data envelope
492 public function getResponseMore() {
497 * Get marker from last meta response data envelope
499 public function getResponseMarker() {
500 return $this->_last_marker;
504 * Fetch API configuration object
506 public function getConfig() {
507 return $this->httpReq('get',$this->_baseUrl.'config');
511 * Return the Filters for the current user.
513 public function getAllFilters() {
514 return $this->httpReq('get',$this->_baseUrl.'filters');
518 * Create a Filter for the current user.
519 * @param string $name The name of the new filter
520 * @param array $filters An associative array of filters to be applied.
521 * This may change as the API evolves, as of this writing possible
522 * values are: user_ids, hashtags, link_domains, and mention_user_ids.
523 * You will need to provide at least one filter name=>value pair.
525 public function createFilter($name='New filter', $filters=array()) {
526 $filters['name'] = $name;
527 return $this->httpReq('post',$this->_baseUrl.'filters',$filters);
531 * Returns a specific Filter object.
532 * @param integer $filter_id The ID of the filter you wish to retrieve.
534 public function getFilter($filter_id=null) {
535 return $this->httpReq('get',$this->_baseUrl.'filters/'.urlencode($filter_id));
539 * Delete a Filter. The Filter must belong to the current User.
540 * @return object Returns the deleted Filter on success.
542 public function deleteFilter($filter_id=null) {
543 return $this->httpReq('delete',$this->_baseUrl.'filters/'.urlencode($filter_id));
547 * Process user description, message or post text.
548 * Mentions and hashtags will be parsed out of the
549 * text, as will bare URLs. To create a link in the text without using a
550 * bare URL, include the anchor text in the object text and include a link
551 * entity in the function call.
552 * @param string $text The text of the description/message/post
553 * @param array $data An associative array of optional post data. This
554 * will likely change as the API evolves, as of this writing allowed keys are:
555 * reply_to, and annotations. "annotations" may be a complex object represented
556 * by an associative array.
557 * @param array $params An associative array of optional data to be included
558 * in the URL (such as 'include_annotations' and 'include_machine')
559 * @return array An associative array representing the post.
561 public function processText($text=null, $data = array(), $params = array()) {
562 $data['text'] = $text;
563 $json = json_encode($data);
565 if (!empty($params)) {
566 $qs = '?'.$this->buildQueryString($params);
568 return $this->httpReq('post',$this->_baseUrl.'text/process'.$qs, $json, 'application/json');
572 * Create a new Post object. Mentions and hashtags will be parsed out of the
573 * post text, as will bare URLs. To create a link in a post without using a
574 * bare URL, include the anchor text in the post's text and include a link
575 * entity in the post creation call.
576 * @param string $text The text of the post
577 * @param array $data An associative array of optional post data. This
578 * will likely change as the API evolves, as of this writing allowed keys are:
579 * reply_to, and annotations. "annotations" may be a complex object represented
580 * by an associative array.
581 * @param array $params An associative array of optional data to be included
582 * in the URL (such as 'include_annotations' and 'include_machine')
583 * @return array An associative array representing the post.
585 public function createPost($text=null, $data = array(), $params = array()) {
586 $data['text'] = $text;
588 $json = json_encode($data);
590 if (!empty($params)) {
591 $qs = '?'.$this->buildQueryString($params);
593 return $this->httpReq('post',$this->_baseUrl.'posts'.$qs, $json, 'application/json');
597 * Returns a specific Post.
598 * @param integer $post_id The ID of the post to retrieve
599 * @param array $params An associative array of optional general parameters.
600 * This will likely change as the API evolves, as of this writing allowed keys
601 * are: include_annotations.
602 * @return array An associative array representing the post
604 public function getPost($post_id=null,$params = array()) {
605 return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id)
606 .'?'.$this->buildQueryString($params));
610 * Delete a Post. The current user must be the same user who created the Post.
611 * It returns the deleted Post on success.
612 * @param integer $post_id The ID of the post to delete
613 * @param array An associative array representing the post that was deleted
615 public function deletePost($post_id=null) {
616 return $this->httpReq('delete',$this->_baseUrl.'posts/'.urlencode($post_id));
620 * Retrieve the Posts that are 'in reply to' a specific Post.
621 * @param integer $post_id The ID of the post you want to retrieve replies for.
622 * @param array $params An associative array of optional general parameters.
623 * This will likely change as the API evolves, as of this writing allowed keys
624 * are: count, before_id, since_id, include_muted, include_deleted,
625 * include_directed_posts, and include_annotations.
626 * @return An array of associative arrays, each representing a single post.
628 public function getPostReplies($post_id=null,$params = array()) {
629 return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id)
630 .'/replies?'.$this->buildQueryString($params));
634 * Get the most recent Posts created by a specific User in reverse
635 * chronological order (most recent first).
636 * @param mixed $user_id Either the ID of the user you wish to retrieve posts by,
637 * or the string "me", which will retrieve posts for the user you're authenticated
639 * @param array $params An associative array of optional general parameters.
640 * This will likely change as the API evolves, as of this writing allowed keys
641 * are: count, before_id, since_id, include_muted, include_deleted,
642 * include_directed_posts, and include_annotations.
643 * @return An array of associative arrays, each representing a single post.
645 public function getUserPosts($user_id='me', $params = array()) {
646 return $this->httpReq('get',$this->_baseUrl.'users/'.urlencode($user_id)
647 .'/posts?'.$this->buildQueryString($params));
651 * Get the most recent Posts mentioning by a specific User in reverse
652 * chronological order (newest first).
653 * @param mixed $user_id Either the ID of the user who is being mentioned, or
654 * the string "me", which will retrieve posts for the user you're authenticated
656 * @param array $params An associative array of optional general parameters.
657 * This will likely change as the API evolves, as of this writing allowed keys
658 * are: count, before_id, since_id, include_muted, include_deleted,
659 * include_directed_posts, and include_annotations.
660 * @return An array of associative arrays, each representing a single post.
662 public function getUserMentions($user_id='me',$params = array()) {
663 return $this->httpReq('get',$this->_baseUrl.'users/'
664 .urlencode($user_id).'/mentions?'.$this->buildQueryString($params));
668 * Return the 20 most recent posts from the current User and
669 * the Users they follow.
670 * @param array $params An associative array of optional general parameters.
671 * This will likely change as the API evolves, as of this writing allowed keys
672 * are: count, before_id, since_id, include_muted, include_deleted,
673 * include_directed_posts, and include_annotations.
674 * @return An array of associative arrays, each representing a single post.
676 public function getUserStream($params = array()) {
677 return $this->httpReq('get',$this->_baseUrl.'posts/stream?'.$this->buildQueryString($params));
681 * Returns a specific user object.
682 * @param mixed $user_id The ID of the user you want to retrieve, or the string
683 * "me" to retrieve data for the users you're currently authenticated as.
684 * @param array $params An associative array of optional general parameters.
685 * This will likely change as the API evolves, as of this writing allowed keys
686 * are: include_annotations|include_user_annotations.
687 * @return array An associative array representing the user data.
689 public function getUser($user_id='me', $params = array()) {
690 return $this->httpReq('get',$this->_baseUrl.'users/'.urlencode($user_id)
691 .'?'.$this->buildQueryString($params));
695 * Returns multiple users request by an array of user ids
696 * @param array $params An associative array of optional general parameters.
697 * This will likely change as the API evolves, as of this writing allowed keys
698 * are: include_annotations|include_user_annotations.
699 * @return array An associative array representing the users data.
701 public function getUsers($user_arr, $params = array()) {
702 return $this->httpReq('get',$this->_baseUrl.'users?ids='.join(',',$user_arr)
703 .'&'.$this->buildQueryString($params));
707 * Add the specified user ID to the list of users followed.
708 * Returns the User object of the user being followed.
709 * @param integer $user_id The user ID of the user to follow.
710 * @return array An associative array representing the user you just followed.
712 public function followUser($user_id=null) {
713 return $this->httpReq('post',$this->_baseUrl.'users/'.urlencode($user_id).'/follow');
717 * Removes the specified user ID to the list of users followed.
718 * Returns the User object of the user being unfollowed.
719 * @param integer $user_id The user ID of the user to unfollow.
720 * @return array An associative array representing the user you just unfollowed.
722 public function unfollowUser($user_id=null) {
723 return $this->httpReq('delete',$this->_baseUrl.'users/'.urlencode($user_id).'/follow');
727 * Returns an array of User objects the specified user is following.
728 * @param mixed $user_id Either the ID of the user being followed, or
729 * the string "me", which will retrieve posts for the user you're authenticated
731 * @return array An array of associative arrays, each representing a single
732 * user following $user_id
734 public function getFollowing($user_id='me') {
735 return $this->httpReq('get',$this->_baseUrl.'users/'.$user_id.'/following');
739 * Returns an array of User objects for users following the specified user.
740 * @param mixed $user_id Either the ID of the user being followed, or
741 * the string "me", which will retrieve posts for the user you're authenticated
743 * @return array An array of associative arrays, each representing a single
744 * user following $user_id
746 public function getFollowers($user_id='me') {
747 return $this->httpReq('get',$this->_baseUrl.'users/'.$user_id.'/followers');
751 * Return Posts matching a specific #hashtag.
752 * @param string $hashtag The hashtag you're looking for.
753 * @param array $params An associative array of optional general parameters.
754 * This will likely change as the API evolves, as of this writing allowed keys
755 * are: count, before_id, since_id, include_muted, include_deleted,
756 * include_directed_posts, and include_annotations.
757 * @return An array of associative arrays, each representing a single post.
759 public function searchHashtags($hashtag=null, $params = array()) {
760 return $this->httpReq('get',$this->_baseUrl.'posts/tag/'
761 .urlencode($hashtag).'?'.$this->buildQueryString($params));
765 * Retrieve a list of all public Posts on App.net, often referred to as the
767 * @param array $params An associative array of optional general parameters.
768 * This will likely change as the API evolves, as of this writing allowed keys
769 * are: count, before_id, since_id, include_muted, include_deleted,
770 * include_directed_posts, and include_annotations.
771 * @return An array of associative arrays, each representing a single post.
773 public function getPublicPosts($params = array()) {
774 return $this->httpReq('get',$this->_baseUrl.'posts/stream/global?'.$this->buildQueryString($params));
778 * List User interactions
780 public function getMyInteractions($params = array()) {
781 return $this->httpReq('get',$this->_baseUrl.'users/me/interactions?'.$this->buildQueryString($params));
785 * Retrieve a user's user ID by specifying their username.
786 * Now supported by the API. We use the API if we have a token
787 * Otherwise we scrape the alpha.app.net site for the info.
788 * @param string $username The username of the user you want the ID of, without
789 * an @ symbol at the beginning.
790 * @return integer The user's user ID
792 public function getIdByUsername($username=null) {
793 if ($this->_accessToken) {
794 $res=$this->httpReq('get',$this->_baseUrl.'users/@'.$username);
795 $user_id=$res['data']['id'];
797 $ch = curl_init('https://alpha.app.net/'.urlencode(strtolower($username)));
798 curl_setopt($ch, CURLOPT_POST, false);
799 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
800 curl_setopt($ch,CURLOPT_USERAGENT,
801 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1');
802 $response = curl_exec($ch);
804 $temp = explode('title="User Id ',$response);
805 $temp2 = explode('"',$temp[1]);
806 $user_id = $temp2[0];
813 * @param integer $user_id The user ID to mute
815 public function muteUser($user_id=null) {
816 return $this->httpReq('post',$this->_baseUrl.'users/'.urlencode($user_id).'/mute');
821 * @param integer $user_id The user ID to unmute
823 public function unmuteUser($user_id=null) {
824 return $this->httpReq('delete',$this->_baseUrl.'users/'.urlencode($user_id).'/mute');
828 * List the users muted by the current user
829 * @return array An array of associative arrays, each representing one muted user.
831 public function getMuted() {
832 return $this->httpReq('get',$this->_baseUrl.'users/me/muted');
837 * @param integer $post_id The post ID to star
839 public function starPost($post_id=null) {
840 return $this->httpReq('post',$this->_baseUrl.'posts/'.urlencode($post_id).'/star');
845 * @param integer $post_id The post ID to unstar
847 public function unstarPost($post_id=null) {
848 return $this->httpReq('delete',$this->_baseUrl.'posts/'.urlencode($post_id).'/star');
852 * List the posts starred by the current user
853 * @param array $params An associative array of optional general parameters.
854 * This will likely change as the API evolves, as of this writing allowed keys
855 * are: count, before_id, since_id, include_muted, include_deleted,
856 * include_directed_posts, and include_annotations.
857 * See https://github.com/appdotnet/api-spec/blob/master/resources/posts.md#general-parameters
858 * @return array An array of associative arrays, each representing a single
859 * user who has starred a post
861 public function getStarred($user_id='me', $params = array()) {
862 return $this->httpReq('get',$this->_baseUrl.'users/'.urlencode($user_id).'/stars'
863 .'?'.$this->buildQueryString($params));
867 * List the users who have starred a post
868 * @param integer $post_id the post ID to get stars from
869 * @return array An array of associative arrays, each representing one user.
871 public function getStars($post_id=null) {
872 return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id).'/stars');
876 * Returns an array of User objects of users who reposted the specified post.
877 * @param integer $post_id the post ID to
878 * @return array An array of associative arrays, each representing a single
879 * user who reposted $post_id
881 public function getReposters($post_id){
882 return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id).'/reposters');
886 * Repost an existing Post object.
887 * @param integer $post_id The id of the post
890 public function repost($post_id){
891 return $this->httpReq('post',$this->_baseUrl.'posts/'.urlencode($post_id).'/repost');
895 * Delete a post that the user has reposted.
896 * @param integer $post_id The id of the post
899 public function deleteRepost($post_id){
900 return $this->httpReq('delete',$this->_baseUrl.'posts/'.urlencode($post_id).'/repost');
904 * List the posts who match a specific search term
905 * @param array $params a list of filter, search query, and general Post parameters
906 * see: https://developers.app.net/reference/resources/post/search/
907 * @param string $query The search query. Supports
908 * normal search terms. Searches post text.
909 * @return array An array of associative arrays, each representing one post.
912 public function searchPosts($params = array(), $query='', $order='default') {
913 if (!is_array($params)) {
916 if (!empty($query)) {
917 $params['query']=$query;
919 if ($order=='default') {
920 if (!empty($query)) {
921 $params['order']='score';
923 $params['order']='id';
926 return $this->httpReq('get',$this->_baseUrl.'posts/search?'.$this->buildQueryString($params));
931 * List the users who match a specific search term
932 * @param string $search The search query. Supports @username or #tag searches as
933 * well as normal search terms. Searches username, display name, bio information.
934 * Does not search posts.
935 * @return array An array of associative arrays, each representing one user.
937 public function searchUsers($search="") {
938 return $this->httpReq('get',$this->_baseUrl.'users/search?q='.urlencode($search));
942 * Return the 20 most recent posts for a stream using a valid Token
943 * @param array $params An associative array of optional general parameters.
944 * This will likely change as the API evolves, as of this writing allowed keys
945 * are: count, before_id, since_id, include_muted, include_deleted,
946 * include_directed_posts, and include_annotations.
947 * @return An array of associative arrays, each representing a single post.
949 public function getTokenStream($params = array()) {
950 if ($params['access_token']) {
951 return $this->httpReq('get',$this->_baseUrl.'posts/stream?'.$this->buildQueryString($params),$params);
953 return $this->httpReq('get',$this->_baseUrl.'posts/stream?'.$this->buildQueryString($params));
958 * Get a user object by username
959 * @param string $name the @name to get
960 * @return array representing one user
962 public function getUserByName($name=null) {
963 return $this->httpReq('get',$this->_baseUrl.'users/@'.$name);
967 * Return the 20 most recent Posts from the current User's personalized stream
968 * and mentions stream merged into one stream.
969 * @param array $params An associative array of optional general parameters.
970 * This will likely change as the API evolves, as of this writing allowed keys
971 * are: count, before_id, since_id, include_muted, include_deleted,
972 * include_directed_posts, and include_annotations.
973 * @return An array of associative arrays, each representing a single post.
975 public function getUserUnifiedStream($params = array()) {
976 return $this->httpReq('get',$this->_baseUrl.'posts/stream/unified?'.$this->buildQueryString($params));
980 * Update Profile Data via JSON
981 * @data array containing user descriptors
983 public function updateUserData($data = array(), $params = array()) {
984 $json = json_encode($data);
985 return $this->httpReq('put',$this->_baseUrl.'users/me'.'?'.
986 $this->buildQueryString($params), $json, 'application/json');
990 * Update a user image
991 * @which avatar|cover
992 * @image path reference to image
994 protected function updateUserImage($which = 'avatar', $image = null) {
995 $data = array($which=>"@$image");
996 return $this->httpReq('post-raw',$this->_baseUrl.'users/me/'.$which, $data, 'multipart/form-data');
999 public function updateUserAvatar($avatar = null) {
1001 return $this->updateUserImage('avatar', $avatar);
1004 public function updateUserCover($cover = null) {
1006 return $this->updateUserImage('cover', $cover);
1010 * update stream marker
1012 public function updateStreamMarker($data = array()) {
1013 $json = json_encode($data);
1014 return $this->httpReq('post',$this->_baseUrl.'posts/marker', $json, 'application/json');
1018 * get a page of current user subscribed channels
1020 public function getUserSubscriptions($params = array()) {
1021 return $this->httpReq('get',$this->_baseUrl.'channels?'.$this->buildQueryString($params));
1027 public function getMyChannels($params = array()) {
1028 return $this->httpReq('get',$this->_baseUrl.'channels/me?'.$this->buildQueryString($params));
1033 * note: you cannot create a channel with type=net.app.core.pm (see createMessage)
1035 public function createChannel($data = array()) {
1036 $json = json_encode($data);
1037 return $this->httpReq('post',$this->_baseUrl.'channels'.($pm?'/pm/messsages':''), $json, 'application/json');
1041 * get channelid info
1043 public function getChannel($channelid, $params = array()) {
1044 return $this->httpReq('get',$this->_baseUrl.'channels/'.$channelid.'?'.$this->buildQueryString($params));
1048 * get multiple channels' info by an array of channelids
1050 public function getChannels($channels, $params = array()) {
1051 return $this->httpReq('get',$this->_baseUrl.'channels?ids='.join(',',$channels).'&'.$this->buildQueryString($params));
1057 public function updateChannel($channelid, $data = array()) {
1058 $json = json_encode($data);
1059 return $this->httpReq('put',$this->_baseUrl.'channels/'.$channelid, $json, 'application/json');
1063 * subscribe from channelid
1065 public function channelSubscribe($channelid) {
1066 return $this->httpReq('post',$this->_baseUrl.'channels/'.$channelid.'/subscribe');
1070 * unsubscribe from channelid
1072 public function channelUnsubscribe($channelid) {
1073 return $this->httpReq('delete',$this->_baseUrl.'channels/'.$channelid.'/subscribe');
1077 * get all user objects subscribed to channelid
1079 public function getChannelSubscriptions($channelid, $params = array()) {
1080 return $this->httpReq('get',$this->_baseUrl.'channel/'.$channelid.'/subscribers?'.$this->buildQueryString($params));
1084 * get all user IDs subscribed to channelid
1086 public function getChannelSubscriptionsById($channelid) {
1087 return $this->httpReq('get',$this->_baseUrl.'channel/'.$channelid.'/subscribers/ids');
1092 * get a page of messages in channelid
1094 public function getMessages($channelid, $params = array()) {
1095 return $this->httpReq('get',$this->_baseUrl.'channels/'.$channelid.'/messages?'.$this->buildQueryString($params));
1100 * @param $channelid numeric or "pm" for auto-chanenl (type=net.app.core.pm)
1101 * @param $data array('text'=>'YOUR_MESSAGE') If a type=net.app.core.pm, then "destinations" key can be set to address as an array of people to send this PM too
1103 public function createMessage($channelid,$data) {
1104 $json = json_encode($data);
1105 return $this->httpReq('post',$this->_baseUrl.'channels/'.$channelid.'/messages', $json, 'application/json');
1111 public function getMessage($channelid,$messageid) {
1112 return $this->httpReq('get',$this->_baseUrl.'channels/'.$channelid.'/messages/'.$messageid);
1118 public function deleteMessage($channelid,$messageid) {
1119 return $this->httpReq('delete',$this->_baseUrl.'channels/'.$channelid.'/messages/'.$messageid);
1124 * Get Application Information
1126 public function getAppTokenInfo() {
1127 // requires appAccessToken
1128 if (!$this->_appAccessToken) {
1129 $this->getAppAccessToken();
1131 // ensure request is made with our appAccessToken
1132 $params['access_token']=$this->_appAccessToken;
1133 return $this->httpReq('get',$this->_baseUrl.'token',$params);
1137 * Get User Information
1139 public function getUserTokenInfo() {
1140 return $this->httpReq('get',$this->_baseUrl.'token');
1144 * Get Application Authorized User IDs
1146 public function getAppUserIDs() {
1147 // requires appAccessToken
1148 if (!$this->_appAccessToken) {
1149 $this->getAppAccessToken();
1151 // ensure request is made with our appAccessToken
1152 $params['access_token']=$this->_appAccessToken;
1153 return $this->httpReq('get',$this->_baseUrl.'apps/me/tokens/user_ids',$params);
1157 * Get Application Authorized User Tokens
1159 public function getAppUserTokens() {
1160 // requires appAccessToken
1161 if (!$this->_appAccessToken) {
1162 $this->getAppAccessToken();
1164 // ensure request is made with our appAccessToken
1165 $params['access_token']=$this->_appAccessToken;
1166 return $this->httpReq('get',$this->_baseUrl.'apps/me/tokens',$params);
1169 public function getLastRequest() {
1170 return $this->_last_request;
1172 public function getLastResponse() {
1173 return $this->_last_response;
1177 * Registers your function (or an array of object and method) to be called
1178 * whenever an event is received via an open app.net stream. Your function
1179 * will receive a single parameter, which is the object wrapper containing
1180 * the meta and data.
1181 * @param mixed A PHP callback (either a string containing the function name,
1182 * or an array where the first element is the class/object and the second
1185 public function registerStreamFunction($function) {
1186 $this->_streamCallback = $function;
1190 * Opens a stream that's been created for this user/app and starts sending
1191 * events/objects to your defined callback functions. You must define at
1192 * least one callback function before opening a stream.
1193 * @param mixed $stream Either a stream ID or the endpoint of a stream
1194 * you've already created. This stream must exist and must be valid for
1195 * your current access token. If you pass a stream ID, the library will
1196 * make an API call to get the endpoint.
1198 * This function will return immediately, but your callback functions
1199 * will continue to receive events until you call closeStream() or until
1200 * App.net terminates the stream from their end with an error.
1202 * If you're disconnected due to a network error, the library will
1203 * automatically attempt to reconnect you to the same stream, no action
1204 * on your part is necessary for this. However if the app.net API returns
1205 * an error, a reconnection attempt will not be made.
1207 * Note there is no closeStream, because once you open a stream you
1208 * can't stop it (unless you exit() or die() or throw an uncaught
1209 * exception, or something else that terminates the script).
1210 * @return boolean True
1211 * @see createStream()
1213 public function openStream($stream) {
1214 // if there's already a stream running, don't allow another
1215 if ($this->_currentStream) {
1216 throw new AppDotNetException('There is already a stream being consumed, only one stream can be consumed per AppDotNetStream instance');
1218 // must register a callback (or the exercise is pointless)
1219 if (!$this->_streamCallback) {
1220 throw new AppDotNetException('You must define your callback function using registerStreamFunction() before calling openStream');
1222 // if the stream is a numeric value, get the stream info from the api
1223 if (is_numeric($stream)) {
1224 $stream = $this->getStream($stream);
1225 $this->_streamUrl = $stream['endpoint'];
1228 $this->_streamUrl = $stream;
1230 // continue doing this until we get an error back or something...?
1231 $this->httpStream('get',$this->_streamUrl);
1237 * Close the currently open stream.
1240 public function closeStream() {
1241 if (!$this->_lastStreamActivity) {
1245 if (!$this->_multiStream) {
1246 throw new AppDotNetException('You must open a stream before calling closeStream()');
1248 curl_close($this->_currentStream);
1249 curl_multi_remove_handle($this->_multiStream,$this->_currentStream);
1250 curl_multi_close($this->_multiStream);
1251 $this->_currentStream = null;
1252 $this->_multiStream = null;
1256 * Retrieve all streams for the current access token.
1257 * @return array An array of stream definitions.
1259 public function getAllStreams() {
1260 return $this->httpReq('get',$this->_baseUrl.'streams');
1264 * Returns a single stream specified by a stream ID. The stream must have been
1265 * created with the current access token.
1266 * @return array A stream definition
1268 public function getStream($streamId) {
1269 return $this->httpReq('get',$this->_baseUrl.'streams/'.urlencode($streamId));
1273 * Creates a stream for the current app access token.
1275 * @param array $objectTypes The objects you want to retrieve data for from the
1276 * stream. At time of writing these can be 'post', 'star', and/or 'user_follow'.
1277 * If you don't specify, all events will be retrieved.
1279 public function createStream($objectTypes=null) {
1280 // default object types to everything
1281 if (is_null($objectTypes)) {
1282 $objectTypes = array('post','star','user_follow');
1285 'object_types'=>$objectTypes,
1286 'type'=>'long_poll',
1288 $data = json_encode($data);
1289 $response = $this->httpReq('post',$this->_baseUrl.'streams',$data,'application/json');
1294 * Update stream for the current app access token
1296 * @param integer $streamId The stream ID to update. This stream must have been
1297 * created by the current access token.
1298 * @param array $data allows object_types, type, filter_id and key to be updated. filter_id/key can be omitted
1300 public function updateStream($streamId,$data) {
1301 // objectTypes is likely required
1302 if (is_null($data['object_types'])) {
1303 $data['object_types'] = array('post','star','user_follow');
1305 // type can still only be long_poll
1306 if (is_null($data['type'])) {
1307 $data['type']='long_poll';
1309 $data = json_encode($data);
1310 $response = $this->httpReq('put',$this->_baseUrl.'streams/'.urlencode($streamId),$data,'application/json');
1315 * Deletes a stream if you no longer need it.
1317 * @param integer $streamId The stream ID to delete. This stream must have been
1318 * created by the current access token.
1320 public function deleteStream($streamId) {
1321 return $this->httpReq('delete',$this->_baseUrl.'streams/'.urlencode($streamId));
1325 * Deletes all streams created by the current access token.
1327 public function deleteAllStreams() {
1328 return $this->httpReq('delete',$this->_baseUrl.'streams');
1332 * Internal function used to process incoming chunks from the stream. This is only
1333 * public because it needs to be accessed by CURL. Do not call or use this function
1337 public function httpStreamReceive($ch,$data) {
1338 $this->_lastStreamActivity = time();
1339 $this->_streamBuffer .= $data;
1340 if (!$this->_streamHeaders) {
1341 $pos = strpos($this->_streamBuffer,"\r\n\r\n");
1343 $this->_streamHeaders = substr($this->_streamBuffer,0,$pos);
1344 $this->_streamBuffer = substr($this->_streamBuffer,$pos+4);
1348 $pos = strpos($this->_streamBuffer,"\r\n");
1349 while ($pos!==false) {
1350 $command = substr($this->_streamBuffer,0,$pos);
1351 $this->_streamBuffer = substr($this->_streamBuffer,$pos+2);
1352 $command = json_decode($command,true);
1354 call_user_func($this->_streamCallback,$command);
1356 $pos = strpos($this->_streamBuffer,"\r\n");
1359 return strlen($data);
1363 * Opens a long lived HTTP connection to the app.net servers, and sends data
1364 * received to the httpStreamReceive function. As a general rule you should not
1365 * directly call this method, it's used by openStream().
1367 protected function httpStream($act, $req, $params=array(),$contentType='application/x-www-form-urlencoded') {
1368 if ($this->_currentStream) {
1369 throw new AppDotNetException('There is already an open stream, you must close the existing one before opening a new one');
1372 $this->_streamBuffer = '';
1373 if ($this->_accessToken) {
1374 $headers[] = 'Authorization: Bearer '.$this->_accessToken;
1376 $this->_currentStream = curl_init($req);
1377 curl_setopt($this->_currentStream, CURLOPT_HTTPHEADER, $headers);
1378 curl_setopt($this->_currentStream, CURLOPT_RETURNTRANSFER, true);
1379 curl_setopt($this->_currentStream, CURLINFO_HEADER_OUT, true);
1380 curl_setopt($this->_currentStream, CURLOPT_HEADER, true);
1381 if ($this->_sslCA) {
1382 curl_setopt($this->_currentStream, CURLOPT_CAINFO, $this->_sslCA);
1384 // every time we receive a chunk of data, forward it to httpStreamReceive
1385 curl_setopt($this->_currentStream, CURLOPT_WRITEFUNCTION, array($this, "httpStreamReceive"));
1390 $this->_multiStream = curl_multi_init();
1391 $this->_lastStreamActivity = time();
1392 curl_multi_add_handle($this->_multiStream,$this->_currentStream);
1395 public function reconnectStream() {
1396 $this->closeStream();
1397 $this->_connectFailCounter++;
1398 // if we've failed a few times, back off
1399 if ($this->_connectFailCounter>1) {
1400 $sleepTime = pow(2,$this->_connectFailCounter);
1401 // don't sleep more than 60 seconds
1402 if ($sleepTime>60) {
1407 $this->httpStream('get',$this->_streamUrl);
1411 * Process an open stream for x microseconds, then return. This is useful if you want
1412 * to be doing other things while processing the stream. If you just want to
1413 * consume the stream without other actions, you can call processForever() instead.
1414 * @param float @microseconds The number of microseconds to process for before
1415 * returning. There are 1,000,000 microseconds in a second.
1419 public function processStream($microseconds=null) {
1420 if (!$this->_multiStream) {
1421 throw new AppDotNetException('You must open a stream before calling processStream()');
1423 $start = microtime(true);
1428 // if we haven't received anything within 5.5 minutes, reconnect
1429 // keepalives are sent every 5 minutes (measured on 2013-3-12 by @ryantharp)
1430 if (time()-$this->_lastStreamActivity>=330) {
1431 $this->reconnectStream();
1433 curl_multi_exec($this->_multiStream, $active);
1435 $httpCode = curl_getinfo($this->_currentStream,CURLINFO_HTTP_CODE);
1436 // don't reconnect on 400 errors
1437 if ($httpCode>=400 && $httpCode<=499) {
1438 throw new AppDotNetException('Received HTTP error '.$httpCode.' check your URL and credentials before reconnecting');
1440 $this->reconnectStream();
1442 // sleep for a max of 2/10 of a second
1443 $timeSoFar = (microtime(true)-$start)*1000000;
1444 $sleepFor = $this->streamingSleepFor;
1445 if ($timeSoFar+$sleepFor>$microseconds) {
1446 $sleepFor = $microseconds - $timeSoFar;
1452 } while ($timeSoFar+$sleepFor<$microseconds);
1456 * Process an open stream forever. This function will never return, if you
1457 * want to perform other actions while consuming the stream, you should use
1458 * processFor() instead.
1459 * @return void This function will never return
1460 * @see processFor();
1462 public function processStreamForever() {
1464 $this->processStream(600);
1470 * Upload a file to a user's file store
1471 * @param string $file A string containing the path of the file to upload.
1472 * @param array $data Additional data about the file you're uploading. At the
1473 * moment accepted keys are: mime-type, kind, type, name, public and annotations.
1474 * - If you don't specify mime-type, ADNPHP will attempt to guess the mime type
1475 * based on the file, however this isn't always reliable.
1476 * - If you don't specify kind ADNPHP will attempt to determine if the file is
1478 * - If you don't specify name, ADNPHP will use the filename of the first
1480 * - If you don't specify public, your file will be uploaded as a private file.
1481 * - Type is REQUIRED.
1482 * @param array $params An associative array of optional general parameters.
1483 * This will likely change as the API evolves, as of this writing allowed keys
1484 * are: include_annotations|include_file_annotations.
1485 * @return array An associative array representing the file
1487 public function createFile($file, $data, $params=array()) {
1489 throw new AppDotNetException('You must specify a path to a file');
1491 if (!file_exists($file)) {
1492 throw new AppDotNetException('File path specified does not exist');
1494 if (!is_readable($file)) {
1495 throw new AppDotNetException('File path specified is not readable');
1502 if (!array_key_exists('type',$data) || !$data['type']) {
1503 throw new AppDotNetException('Type is required when creating a file');
1506 if (!array_key_exists('name',$data)) {
1507 $data['name'] = basename($file);
1510 if (array_key_exists('mime-type',$data)) {
1511 $mimeType = $data['mime-type'];
1512 unset($data['mime-type']);
1517 if (!array_key_exists('kind',$data)) {
1518 $test = @getimagesize($path);
1519 if ($test && array_key_exists('mime',$test)) {
1520 $data['kind'] = 'image';
1522 $mimeType = $test['mime'];
1526 $data['kind'] = 'other';
1530 $finfo = finfo_open(FILEINFO_MIME_TYPE);
1531 $mimeType = finfo_file($finfo, $file);
1532 finfo_close($finfo);
1535 throw new AppDotNetException('Unable to determine mime type of file, try specifying it explicitly');
1537 if (!array_key_exists('public',$data) || !$data['public']) {
1544 $data['content'] = "@$file;type=$mimeType";
1545 return $this->httpReq('post-raw',$this->_baseUrl.'files', $data, 'multipart/form-data');
1549 public function createFilePlaceholder($file = null, $params=array()) {
1550 $name = basename($file);
1551 $data = array('annotations' => $params['annotations'], 'kind' => $params['kind'],
1552 'name' => $name, 'type' => $params['metadata']);
1553 $json = json_encode($data);
1554 return $this->httpReq('post',$this->_baseUrl.'files', $json, 'application/json');
1557 public function updateFileContent($fileid, $file) {
1559 $data = file_get_contents($file);
1560 $finfo = finfo_open(FILEINFO_MIME_TYPE);
1561 $mime = finfo_file($finfo, $file);
1562 finfo_close($finfo);
1564 return $this->httpReq('put',$this->_baseUrl.'files/' . $fileid
1565 .'/content', $data, $mime);
1569 * Allows for file rename and annotation changes.
1570 * @param integer $file_id The ID of the file to update
1571 * @param array $params An associative array of file parameters.
1572 * @return array An associative array representing the updated file
1574 public function updateFile($file_id=null, $params=array()) {
1575 $data = array('annotations' => $params['annotations'] , 'name' => $params['name']);
1576 $json = json_encode($data);
1577 return $this->httpReq('put',$this->_baseUrl.'files/'.urlencode($file_id), $json, 'application/json');
1581 * Returns a specific File.
1582 * @param integer $file_id The ID of the file to retrieve
1583 * @param array $params An associative array of optional general parameters.
1584 * This will likely change as the API evolves, as of this writing allowed keys
1585 * are: include_annotations|include_file_annotations.
1586 * @return array An associative array representing the file
1588 public function getFile($file_id=null,$params = array()) {
1589 return $this->httpReq('get',$this->_baseUrl.'files/'.urlencode($file_id)
1590 .'?'.$this->buildQueryString($params));
1593 public function getFileContent($file_id=null,$params = array()) {
1594 return $this->httpReq('get',$this->_baseUrl.'files/'.urlencode($file_id)
1595 .'/content?'.$this->buildQueryString($params));
1598 /** $file_key : derived_file_key */
1599 public function getDerivedFileContent($file_id=null,$file_key=null,$params = array()) {
1600 return $this->httpReq('get',$this->_baseUrl.'files/'.urlencode($file_id)
1601 .'/content/'.urlencode($file_key)
1602 .'?'.$this->buildQueryString($params));
1606 * Returns file objects.
1607 * @param array $file_ids The IDs of the files to retrieve
1608 * @param array $params An associative array of optional general parameters.
1609 * This will likely change as the API evolves, as of this writing allowed keys
1610 * are: include_annotations|include_file_annotations.
1611 * @return array An associative array representing the file data.
1613 public function getFiles($file_ids=array(), $params = array()) {
1615 foreach($file_ids as $id) {
1618 $params['ids'] = substr($ids, 0, -1);
1619 return $this->httpReq('get',$this->_baseUrl.'files'
1620 .'?'.$this->buildQueryString($params));
1624 * Returns a user's file objects.
1625 * @param array $params An associative array of optional general parameters.
1626 * This will likely change as the API evolves, as of this writing allowed keys
1627 * are: include_annotations|include_file_annotations|include_user_annotations.
1628 * @return array An associative array representing the file data.
1630 public function getUserFiles($params = array()) {
1631 return $this->httpReq('get',$this->_baseUrl.'users/me/files'
1632 .'?'.$this->buildQueryString($params));
1636 * Delete a File. The current user must be the same user who created the File.
1637 * It returns the deleted File on success.
1638 * @param integer $file_id The ID of the file to delete
1639 * @return array An associative array representing the file that was deleted
1641 public function deleteFile($file_id=null) {
1642 return $this->httpReq('delete',$this->_baseUrl.'files/'.urlencode($file_id));
1647 class AppDotNetException extends Exception {}