Source for file Twitter.php
Documentation is available at Twitter.php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
* An interface for Twitter's REST API and Search API.
* @package Services_Twitter
* @author Joe Stump <joe@joestump.net>
* @author David Jean Louis <izimobil@gmail.com>
* @author Bill Shupp <shupp@php.net>
* @copyright 1997-2008 Joe Stump <joe@joestump.net>
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @version SVN: $Id: Twitter.php 40 2009-01-07 12:40:35Z izimobil $
* @link http://twitter.com
* @link http://apiwiki.twitter.com
* Include HTTP_Request2 class and exception classes.
require_once 'HTTP/Request2.php';
require_once 'Services/Twitter/Exception.php';
* Base class for interacting with Twitter's API.
* Here's a basic auth example:
* require_once 'Services/Twitter.php';
* $username = 'Your_Username';
* $password = 'Your_Password';
* $twitter = new Services_Twitter($username, $password);
* $msg = $twitter->statuses->update("I'm coding with PEAR right now!");
* } catch (Services_Twitter_Exception $e) {
* Here's an OAuth example:
* require_once 'Services/Twitter.php';
* require_once 'HTTP/OAuth/Consumer.php';
* $twitter = new Services_Twitter();
* $oauth = new HTTP_OAuth_Consumer('consumer_key',
* $twitter->setOAuth($oauth);
* $msg = $twitter->statuses->update("I'm coding with PEAR right now!");
* } catch (Services_Twitter_Exception $e) {
* @package Services_Twitter
* @author Joe Stump <joe@joestump.net>
* @author David Jean Louis <izimobil@gmail.com>
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @link http://twitter.com
* @link http://apiwiki.twitter.com
* Exception codes constants defined by this package.
* @global integer ERROR_UNKNOWN An unknown error occurred
* @global integer ERROR_ENDPOINT Bad endpoint
* @global integer ERROR_PARAMS Bad endpoint parameters
const ERROR_ENDPOINT = 2;
* Twitter API output parsing options
* @global string OUTPUT_XML The response is expected to be XML
* @global string OUTPUT_JSON The response is expected to be JSON
const OUTPUT_XML = 'xml';
const OUTPUT_JSON = 'json';
* Public URI of Twitter's API
public static $uri = 'http://api.twitter.com/1';
* Public URI of Twitter's Search API
public static $searchUri = 'http://search.twitter.com';
* Username of Twitter user
* @var string $user Twitter username
* @see Services_Twitter::__construct()
* Password of Twitter user
* @var string $pass User's password for Twitter
* @see Services_Twitter::__construct()
* Optional instance of HTTP_OAuth_Consumer
* @var HTTP_OAuth_Consumer $oauth
* @see HTTP_OAuth_Consumer
* Options for HTTP requests and misc.
* - format: the desired output format: json (default) or xml,
* - raw_format: if set to true, the configured format is returned "as is",
* - source: you can set this if you have registered a twitter source
* {@see http://twitter.com/help/request_source}, your source will be
* passed with each POST request,
* - use_ssl: whether to send all requests over ssl or not. If set to true, the
* script will replace the http:// URL scheme with https://,
* - validate: if set to true the class will validate api xml file against
* the RelaxNG file (you should not need this unless you are hacking
* These options can be set either by passing them directly to the
* constructor as an array (3rd parameter), or by using the setOption() or
* @see Services_Twitter::__construct()
* @see Services_Twitter::setOption()
* @see Services_Twitter::setOptions()
'format' => self ::OUTPUT_JSON ,
'source' => 'pearservicestwitter',
* The HTTP_Request2 instance, you can customize the request if you want to
* (proxy, auth etc...) with the get/setRequest() methods.
* @var HTTP_Request2 $request
* @see Services_Twitter::getRequest()
* @see Services_Twitter::setRequest()
* The Twitter API mapping array, used internally to retrieve infos about
* the API categories, endpoints, parameters, etc...
* The mapping is constructed with the api.xml file present in the
* Services_Twitter data directory.
* @var array $api Twitter api array
* @see Services_Twitter::__construct()
* @see Services_Twitter::prepareRequest()
protected $api = array ();
* Used internally by the __get() and __call() methods to identify the
* @var string $currentCategory
* @see Services_Twitter::__get()
* @see Services_Twitter::__call()
* @param string $user Twitter username
* @param string $pass Twitter password
* @param array $options An array of options
public function __construct($user = null , $pass = null , $options = array ())
// set properties and options
// load the XML API definition
* Get interceptor, if the requested property is "options", it just return
* the options array, otherwise, if the property matches a valid API
* category it return an instance of this class.
* @param string $property The property of the call
public function __get($property)
if (isset ($this->api[$property])) {
'Unsupported endpoint ' . $property,
* Overloaded call for API passthrough.
* @param string $endpoint API endpoint being called
* @param array $args $args[0] is an array of GET/POST arguments
* @return object Instance of SimpleXMLElement
public function __call($endpoint, array $args = array ())
// case of a classic "category->endpoint()" call
} else if (isset ($this->api[$endpoint][$endpoint])) {
// case of a "root" endpoint call, the method is the name of the
// category (ex: $twitter->direct_messages())
$ep = $this->api[$endpoint][$endpoint];
'Unsupported endpoint ' . $endpoint,
// check that endpoint is available in the configured format
$formats = explode(',', (string) $ep->formats );
'Endpoint ' . $endpoint . ' does not support '
// we must reset the current category to null for future calls.
list ($uri, $method, $params, $files)
// we can now send our request
if ($this->oauth instanceof HTTP_OAuth_Consumer ) {
$resp = $this->sendRequest($uri, $method, $params, $files);
$body = $resp->getBody ();
if (substr($resp->getStatus (), 0 , 1 ) != '2') {
if (isset ($error->error )) {
$message = (string) $error->error;
$message = $resp->getReasonPhrase ();
// sendOAuthRequest() {{{
* Sends a request using OAuth instead of basic auth
* @param string $uri The full URI of the endpoint
* @param string $method GET or POST
* @param array $params Array of additional parameter
* @param array $files Array of files to upload
* @throws Services_Twitter_Exception on failure
* @return HTTP_Request2_Response
include_once 'HTTP/OAuth/Consumer/Request.php';
foreach ($files as $key => $val) {
$request->addUpload ($key, $val);
// Use the same instance of HTTP_Request2
$consumerRequest = new HTTP_OAuth_Consumer_Request;
$consumerRequest->accept ($request);
$this->oauth->accept ($consumerRequest);
$response = $this->oauth->sendRequest ($uri, $params, $method);
} catch (HTTP_Request2_Exception $exc) {
$exc, // the original exception cause
* Set an option in {@link Services_Twitter::$options}
* If a function exists named _set$option (e.g. _setUserAgent()) then that
* method will be used instead. Otherwise, the value is set directly into
* @param string $option Name of option
* @param mixed $value Value of option
* @see Services_Twitter::$options
* Sets an instance of HTTP_OAuth_Consumer
* @param HTTP_OAuth_Consumer $oauth Object containing OAuth credentials
public function setOAuth(HTTP_OAuth_Consumer $oauth)
* Get an option from {@link Services_Twitter::$options}
* @param string $option Name of option
* @see Services_Twitter::$options
if (isset ($this->options[$option])) {
* Set a number of options at once in {@link Services_Twitter::$options}
* @param array $options Associative array of options name/value
* @see Services_Twitter::$options
* @see Services_Twitter::setOption()
foreach ($options as $option => $value) {
* Return the Services_Twitter options array.
* @see Services_Twitter::$options
* Returns the HTTP_Request2 instance.
* @return HTTP_Request2 The request
$this->request = new HTTP_Request2 ();
// XXX ssl won't work with ssl_verify_peer set to true, which is
// the default in HTTP_Request2
$this->request->setConfig ('ssl_verify_peer', false );
* Sets the HTTP_Request2 instance.
* @param HTTP_Request2 $request The request to set
public function setRequest(HTTP_Request2 $request)
* Decode the response body according to the configured format.
* @param string $body The response body to decode
// See http://pear.php.net/bugs/bug.php?id=17345
$isbool = ((string) $result == 'true' || (string) $result == 'false');
// default to Services_Twitter::OUTPUT_JSON
$isbool = ($result == 'true' || $result == 'false');
// special case where the API returns true/false strings
return (string) $result == 'true';
* Loads the XML API definition.
// initialize xml mapping
? array ('@data_dir@', 'Services_Twitter', 'data')
: array (dirname(__FILE__ ), '..', 'data');
$d = implode(DIRECTORY_SEPARATOR , $p) . DIRECTORY_SEPARATOR;
// this should be done only when testing
$doc = new DomDocument ();
$doc->load ($d . 'api.xml');
$doc->relaxNGValidate ($d . 'api.rng');
foreach ($xmlApi->category as $category) {
$catName = (string) $category['name'];
$this->api[$catName] = array ();
foreach ($category->endpoint as $endpoint) {
$this->api[$catName][(string) $endpoint['name']] = $endpoint;
* Prepare the request before it is sent.
* @param SimpleXMLElement $endpoint API endpoint xml node
* @param array $args API endpoint arguments
* @param string $cat The current category
* @throws Services_Twitter_Exception
* @return array The array of arguments to pass to in the request
protected function prepareRequest($endpoint, array $args = array (), $cat = null )
// check if we have is a search or trends call, in this case the base
&& !in_array((string) $endpoint['name'], array ('available', 'location')))
if ($this->getOption ('use_ssl')) {
if ($cat !== null && $cat !== 'search') {
$path .= (string) $endpoint['name'];
$method = (string) $endpoint['method'];
// check if we have a POST method and a registered source to pass
if ($method == 'POST' && $source !== null ) {
$params['source'] = $source;
// check arguments requirements
$minargs = isset ($endpoint['min_args'])
? (int) $endpoint['min_args']
: count($endpoint->xpath ('param[@required="true" or @required="1"]'));
if (!$minargs && (isset ($args[0 ]) && !is_array($args[0 ]))) {
$path . ' expects an array as unique parameter',
if ( $minargs && (!isset ($args[0 ])
&& $minargs > count($args[0 ]))
'Not enough arguments for ' . $path,
if (isset ($endpoint['routing'])) {
$routing = explode('/', (string) $endpoint['routing']);
// now process arguments according to their definition in the xml
foreach ($endpoint->param as $param) {
$pName = (string) $param['name'];
$pType = (string) $param['type'];
$pMaxLength = (int) $param['max_length'];
$pMaxLength = $pMaxLength > 0 ? $pMaxLength : null;
$pReq = (string) $param['required'] == 'true' || $needargs;
} else if (isset ($args[0 ][$pName])) {
} catch (Exception $exc) {
$path . ': ' . $exc->getMessage (),
if (count($routing) > 1 ) {
if ($method == 'DELETE') {
$params['_method'] = 'DELETE';
if ($pType == 'string' && !$this->isUtf8($arg)) {
// iso-8859-1 string that we must convert to unicode
$uri .= $path . '.' . $this->getOption('format');
return array ($uri, $method, $params, $files);
* Send a request to the Twitter API.
* @param string $uri The full URI to the API endpoint
* @param array $method The HTTP request method (GET or POST)
* @param array $args The API endpoint arguments if any
* @param array $files The API endpoint file uploads if any
* @throws Services_Twitter_Exception
* @return object Instance of SimpleXMLElement
protected function sendRequest($uri, $method = 'GET', array $args = array (),
$request->setMethod ($method);
foreach ($args as $key => $val) {
$request->addPostParameter ($key, $val);
foreach ($files as $key => $val) {
$request->addUpload ($key, $val);
foreach ($args as $key => $val) {
$uri .= $prefix . $key . '=' . urlencode($val);
if ($this->user !== null && $this->pass !== null ) {
$request->setAuth ($this->user, $this->pass);
$response = $request->send ();
} catch (HTTP_Request2_Exception $exc) {
$exc, // the original exception cause
* Check the validity of an argument (required, max length, type...).
* @param array $name The argument name
* @param array &$val The argument value, passed by reference
* @param array $type The argument type
* @param array $maxLength The argument maximum length (optional)
* @throws Services_Twitter_Exception
protected function validateArg($name, &$val, $type, $maxLength = null )
// check length if necessary
if ($maxLength !== null && mb_strlen($val, 'UTF-8') > $maxLength) {
$name . ' must not exceed ' . $maxLength . ' chars',
$msg = $name . ' must be a boolean';
// we modify the value by reference
$val = $val ? 'true' : 'false';
$msg = $name . ' must be an integer';
$msg = $name . ' must be a string';
$val = date('Y-m-d', $val);
$rx = '/^\d{4}\-\d{2}\-\d{2}$/';
$msg = $name . ' should be formatted YYYY-MM-DD';
if (!preg_match('/^[a-zA-Z0-9_\.]{1,16}$/', $val)) {
$msg = $name . ' must be a valid id or screen name';
$devices = array ('none', 'sms', 'im');
$msg = $name . ' must be one of the following: '
$msg = $name . ' must be a valid iso-639-1 language code';
if (!preg_match('/^([-\d\.]+,){2}([-\d\.]+(km|mi)$)/', $val)) {
$msg = $name . ' must be "latitide,longitude,radius(km or mi)"';
$msg = $name . ' must be a float';
if ($type == 'lat' && ($val < -90 || $val > 90 )) {
$msg = 'valid range for ' . $name . ' is -90.0 to +90.0';
if ($type == 'long' && ($val < -180 || $val > 180 )) {
$msg = 'valid range for ' . $name . ' is -180.0 to +180.0';
if (!preg_match('/^([0-9a-f]{1,2}){3}$/i', $val)) {
$msg = $name . ' must be an hexadecimal color code (eg. fff)';
$msg = $name . ' must be a valid image path';
// XXX we don't check the image type for now...
if (!preg_match('/^[0-9a-z]+(?:-?[0-9a-z]+)*$/', $val)) {
$msg = $name . ' must be a valid list id or slug';
$modes = array ('public', 'private');
$msg = $name . ' must be one of the following: '
* Check if the given string is a UTF-8 string or an iso-8859-1 one.
* @param string $str The string to check
* @return boolean Wether the string is unicode or not
protected function isUtf8($str)
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
Documentation generated on Mon, 11 Mar 2019 15:39:03 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|