1 : <?php
2 : /*
3 : * Class to integrate with Twitter's API.
4 : * Authenticated calls are done using OAuth and require access tokens for a user.
5 : * API calls which do not require authentication do not require tokens (i.e. search/trends)
6 : *
7 : * Full documentation available on github
8 : * http://wiki.github.com/jmathai/twitter-async
9 : *
10 : * @author Jaisen Mathai <jaisen@jmathai.com>
11 : */
12 : class EpiTwitter extends EpiOAuth
13 : {
14 : const EPITWITTER_SIGNATURE_METHOD = 'HMAC-SHA1';
15 : const EPITWITTER_AUTH_OAUTH = 'oauth';
16 : const EPITWITTER_AUTH_BASIC = 'basic';
17 : protected $requestTokenUrl= 'http://twitter.com/oauth/request_token';
18 : protected $accessTokenUrl = 'http://twitter.com/oauth/access_token';
19 : protected $authorizeUrl = 'http://twitter.com/oauth/authorize';
20 : protected $authenticateUrl= 'http://twitter.com/oauth/authenticate';
21 : protected $apiUrl = 'http://twitter.com';
22 : protected $searchUrl = 'http://search.twitter.com';
23 : protected $userAgent = 'EpiTwitter (http://github.com/jmathai/twitter-async/tree/)';
24 :
25 : public function __call($name, $params = null)
26 : {
27 20 : $parts = explode('_', $name);
28 20 : $method = strtoupper(array_shift($parts));
29 20 : $parts = implode('_', $parts);
30 20 : $path = '/' . preg_replace('/[A-Z]|[0-9]+/e', "'/'.strtolower('\\0')", $parts) . '.json';
31 20 : $args = !empty($params) ? array_shift($params) : null;
32 :
33 : // calls which do not have a consumerKey are assumed to not require authentication
34 20 : if(empty($this->consumerKey))
35 20 : {
36 2 : $query = isset($args) ? http_build_query($args, '', '&') : '';
37 2 : $url = "{$this->searchUrl}{$path}?{$query}";
38 2 : $ch = curl_init($url);
39 2 : curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
40 2 : curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
41 :
42 2 : return new EpiTwitterJson(EpiCurl::getInstance()->addCurl($ch), self::EPITWITTER_AUTH_BASIC);
43 : }
44 :
45 : // parse the keys to determine if this should be multipart
46 18 : $isMultipart = false;
47 : if($args)
48 18 : {
49 14 : foreach($args as $k => $v)
50 : {
51 14 : if(strncmp('@',$k,1) === 0)
52 14 : {
53 2 : $isMultipart = true;
54 2 : break;
55 : }
56 12 : }
57 14 : }
58 :
59 18 : $url = $this->getUrl("{$this->apiUrl}{$path}");
60 18 : return new EpiTwitterJson(call_user_func(array($this, 'httpRequest'), $method, $url, $args, $isMultipart));
61 : }
62 :
63 : public function __construct($consumerKey = null, $consumerSecret = null, $oauthToken = null, $oauthTokenSecret = null)
64 : {
65 24 : parent::__construct($consumerKey, $consumerSecret, self::EPITWITTER_SIGNATURE_METHOD);
66 24 : $this->setToken($oauthToken, $oauthTokenSecret);
67 24 : }
68 : }
69 :
70 : class EpiTwitterJson implements ArrayAccess, Countable, IteratorAggregate
71 : {
72 : private $__resp;
73 : private $__auth = EpiTwitter::EPITWITTER_AUTH_OAUTH;
74 : public function __construct($response, $auth = null)
75 : {
76 20 : $this->__resp = $response;
77 20 : if($auth !== null)
78 20 : $this->__auth = $auth;
79 20 : }
80 :
81 : // ensure that calls complete by blocking for results, NOOP if already returned
82 : public function __destruct()
83 : {
84 20 : $this->responseText;
85 20 : }
86 :
87 : // Implementation of the IteratorAggregate::getIterator() to support foreach ($this as $...)
88 : public function getIterator ()
89 : {
90 2 : return new ArrayIterator($this->__obj);
91 : }
92 :
93 : // Implementation of Countable::count() to support count($this)
94 : public function count ()
95 : {
96 2 : return count($this->__obj);
97 : }
98 :
99 : // Next four functions are to support ArrayAccess interface
100 : // 1
101 : public function offsetSet($offset, $value)
102 : {
103 0 : $this->response[$offset] = $value;
104 0 : }
105 :
106 : // 2
107 : public function offsetExists($offset)
108 : {
109 4 : return isset($this->response[$offset]);
110 : }
111 :
112 : // 3
113 : public function offsetUnset($offset)
114 : {
115 0 : unset($this->response[$offset]);
116 0 : }
117 :
118 : // 4
119 : public function offsetGet($offset)
120 : {
121 4 : return isset($this->response[$offset]) ? $this->response[$offset] : null;
122 : }
123 :
124 : public function __get($name)
125 : {
126 20 : if(($this->__resp->code < 200 || $this->__resp->code >= 400) && $name !== 'responseText')
127 20 : {
128 1 : switch($this->__auth)
129 : {
130 1 : case EpiTwitter::EPITWITTER_AUTH_OAUTH:
131 1 : EpiOAuthException::raise($this->__resp->data, $this->__resp->code);
132 0 : case EpiTwitter::EPITWITTER_AUTH_BASIC:
133 0 : throw new EpiTwitterException($this->__resp->data, $this->__resp->code);
134 0 : default:
135 0 : throw new Exception("Unknown EpiTwitter Exception. Response: {$this->__resp->data}", $this->__resp->code);
136 0 : }
137 : }
138 :
139 20 : $this->responseText = $this->__resp->data;
140 20 : $this->code = $this->__resp->code;
141 20 : $this->response = json_decode($this->responseText, 1);
142 20 : $this->__obj = json_decode($this->responseText);
143 :
144 20 : if(gettype($this->__obj) === 'object')
145 20 : {
146 13 : foreach($this->__obj as $k => $v)
147 : {
148 13 : $this->$k = $v;
149 13 : }
150 13 : }
151 :
152 20 : return $this->$name;
153 : }
154 :
155 : public function __isset($name)
156 : {
157 0 : $value = self::__get($name);
158 0 : return empty($name);
159 : }
160 : }
161 :
162 : class EpiTwitterException extends Exception {}
|