Services_Twitter
[ class tree: Services_Twitter ] [ index: Services_Twitter ] [ all elements ]

Source for file Twitter.php

Documentation is available at Twitter.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * An interface for Twitter's REST API and Search API.
  7.  *
  8.  * PHP version 5.1.0+
  9.  *
  10.  * @category  Services
  11.  * @package   Services_Twitter
  12.  * @author    Joe Stump <joe@joestump.net>
  13.  * @author    David Jean Louis <izimobil@gmail.com>
  14.  * @author    Bill Shupp <shupp@php.net>
  15.  * @copyright 1997-2008 Joe Stump <joe@joestump.net>
  16.  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
  17.  * @version   SVN: $Id: Twitter.php 40 2009-01-07 12:40:35Z izimobil $
  18.  * @link      http://twitter.com
  19.  * @link      http://apiwiki.twitter.com
  20.  * @filesource
  21.  */
  22.  
  23. /**
  24.  * Include HTTP_Request2 class and exception classes.
  25.  */
  26. require_once 'HTTP/Request2.php';
  27. require_once 'Services/Twitter/Exception.php';
  28.  
  29. /**
  30.  * Base class for interacting with Twitter's API.
  31.  *
  32.  * Here's a basic auth example:
  33.  *
  34.  * <code>
  35.  * require_once 'Services/Twitter.php';
  36.  *
  37.  * $username = 'Your_Username';
  38.  * $password = 'Your_Password';
  39.  *
  40.  * try {
  41.  *     $twitter = new Services_Twitter($username, $password);
  42.  *     $msg = $twitter->statuses->update("I'm coding with PEAR right now!");
  43.  *     print_r($msg);
  44.  * } catch (Services_Twitter_Exception $e) {
  45.  *     echo $e->getMessage();
  46.  * }
  47.  *
  48.  * </code>
  49.  *
  50.  * Here's an OAuth example:
  51.  *
  52.  * <code>
  53.  * require_once 'Services/Twitter.php';
  54.  * require_once 'HTTP/OAuth/Consumer.php';
  55.  *
  56.  *
  57.  * try {
  58.  *     $twitter = new Services_Twitter();
  59.  *     $oauth   = new HTTP_OAuth_Consumer('consumer_key',
  60.  *                                        'consumer_secret',
  61.  *                                        'auth_token',
  62.  *                                        'token_secret');
  63.  *     $twitter->setOAuth($oauth);
  64.  *
  65.  *     $msg = $twitter->statuses->update("I'm coding with PEAR right now!");
  66.  *     print_r($msg);
  67.  * } catch (Services_Twitter_Exception $e) {
  68.  *     echo $e->getMessage();
  69.  * }
  70.  *
  71.  * </code>
  72.  *
  73.  * @category Services
  74.  * @package  Services_Twitter
  75.  * @author   Joe Stump <joe@joestump.net>
  76.  * @author   David Jean Louis <izimobil@gmail.com>
  77.  * @license  http://www.opensource.org/licenses/bsd-license.php New BSD License
  78.  * @link     http://twitter.com
  79.  * @link     http://apiwiki.twitter.com
  80.  */
  81. {
  82.     // constants {{{
  83.  
  84.     /**#@+
  85.      * Exception codes constants defined by this package.
  86.      *
  87.      * @global integer ERROR_UNKNOWN  An unknown error occurred
  88.      * @global integer ERROR_ENDPOINT Bad endpoint
  89.      * @global integer ERROR_PARAMS   Bad endpoint parameters
  90.      */
  91.     const ERROR_UNKNOWN  = 1;
  92.     const ERROR_ENDPOINT = 2;
  93.     const ERROR_PARAMS   = 3;
  94.     /**#@-*/
  95.  
  96.     /**#@+
  97.      * Twitter API output parsing options
  98.      *
  99.      * @global string OUTPUT_XML  The response is expected to be XML
  100.      * @global string OUTPUT_JSON The response is expected to be JSON
  101.      */
  102.     const OUTPUT_XML  = 'xml';
  103.     const OUTPUT_JSON = 'json';
  104.     /**#@-*/
  105.  
  106.     // }}}
  107.     // properties {{{
  108.  
  109.     /**
  110.      * Public URI of Twitter's API
  111.      *
  112.      * @var string $uri 
  113.      */
  114.     public static $uri 'http://api.twitter.com/1';
  115.  
  116.     /**
  117.      * Public URI of Twitter's Search API
  118.      *
  119.      * @var string $uri 
  120.      */
  121.     public static $searchUri 'http://search.twitter.com';
  122.  
  123.     /**
  124.      * Username of Twitter user
  125.      *
  126.      * @var string $user Twitter username
  127.      * @see Services_Twitter::__construct()
  128.      */
  129.     protected $user = '';
  130.  
  131.     /**
  132.      * Password of Twitter user
  133.      *
  134.      * @var string $pass User's password for Twitter
  135.      * @see Services_Twitter::__construct()
  136.      */
  137.     protected $pass = '';
  138.  
  139.     /**
  140.      * Optional instance of HTTP_OAuth_Consumer
  141.      * 
  142.      * @var HTTP_OAuth_Consumer $oauth 
  143.      * @see HTTP_OAuth_Consumer
  144.      */
  145.     protected $oauth = null;
  146.  
  147.     /**
  148.      * Options for HTTP requests and misc.
  149.      *
  150.      * Available options are:
  151.      * - format: the desired output format: json (default) or xml,
  152.      * - raw_format: if set to true, the configured format is returned "as is",
  153.      * - source: you can set this if you have registered a twitter source
  154.      *   {@see http://twitter.com/help/request_source}, your source will be
  155.      *   passed with each POST request,
  156.      * - use_ssl: whether to send all requests over ssl or not. If set to true, the
  157.      *   script will replace the http:// URL scheme with https://,
  158.      * - validate: if set to true the class will validate api xml file against
  159.      *   the RelaxNG file (you should not need this unless you are hacking
  160.      *   Services_Twitter...).
  161.      *
  162.      * These options can be set either by passing them directly to the
  163.      * constructor as an array (3rd parameter), or by using the setOption() or
  164.      * setOptions() methods.
  165.      *
  166.      * @var array $options 
  167.      * @see Services_Twitter::__construct()
  168.      * @see Services_Twitter::setOption()
  169.      * @see Services_Twitter::setOptions()
  170.      */
  171.     protected $options = array(
  172.         'format'     => self::OUTPUT_JSON,
  173.         'raw_format' => false,
  174.         'source'     => 'pearservicestwitter',
  175.         'use_ssl'    => false,
  176.         'validate'   => false,
  177.     );
  178.  
  179.     /**
  180.      * The HTTP_Request2 instance, you can customize the request if you want to
  181.      * (proxy, auth etc...) with the get/setRequest() methods.
  182.      *
  183.      * @var HTTP_Request2 $request 
  184.      * @see Services_Twitter::getRequest()
  185.      * @see Services_Twitter::setRequest()
  186.      */
  187.     protected $request = null;
  188.  
  189.     /**
  190.      * The Twitter API mapping array, used internally to retrieve infos about
  191.      * the API categories, endpoints, parameters, etc...
  192.      *
  193.      * The mapping is constructed with the api.xml file present in the
  194.      * Services_Twitter data directory.
  195.      *
  196.      * @var array $api Twitter api array
  197.      * @see Services_Twitter::__construct()
  198.      * @see Services_Twitter::prepareRequest()
  199.      */
  200.     protected $api = array();
  201.  
  202.     /**
  203.      * Used internally by the __get() and __call() methods to identify the
  204.      * current call.
  205.      *
  206.      * @var string $currentCategory 
  207.      * @see Services_Twitter::__get()
  208.      * @see Services_Twitter::__call()
  209.      */
  210.     protected $currentCategory = null;
  211.  
  212.     // }}}
  213.     // __construct() {{{
  214.  
  215.     /**
  216.      * Constructor.
  217.      *
  218.      * @param string $user    Twitter username
  219.      * @param string $pass    Twitter password
  220.      * @param array  $options An array of options
  221.      *
  222.      * @return void 
  223.      */
  224.     public function __construct($user = null$pass = null$options = array())
  225.     {
  226.         // set properties and options
  227.         $this->user = $user;
  228.         $this->pass = $pass;
  229.         $this->setOptions($options);
  230.  
  231.         // load the XML API definition
  232.         $this->loadAPI();
  233.     }
  234.  
  235.     // }}}
  236.     // __get() {{{
  237.  
  238.     /**
  239.      * Get interceptor, if the requested property is "options", it just return
  240.      * the options array, otherwise, if the property matches a valid API
  241.      * category it return an instance of this class.
  242.      *
  243.      * @param string $property The property of the call
  244.      *
  245.      * @return mixed 
  246.      */
  247.     public function __get($property)
  248.     {
  249.         if ($this->currentCategory === null{
  250.             if (isset($this->api[$property])) {
  251.                 $this->currentCategory = $property;
  252.                 return $this;
  253.             }
  254.         else {
  255.             $this->currentCategory = null;
  256.         }
  257.         throw new Services_Twitter_Exception(
  258.             'Unsupported endpoint ' $property,
  259.             self::ERROR_ENDPOINT
  260.         );
  261.     }
  262.  
  263.     // }}}
  264.     // __call() {{{
  265.  
  266.     /**
  267.      * Overloaded call for API passthrough.
  268.      * 
  269.      * @param string $endpoint API endpoint being called
  270.      * @param array  $args     $args[0] is an array of GET/POST arguments
  271.      * 
  272.      * @return object Instance of SimpleXMLElement
  273.      */
  274.     public function __call($endpointarray $args = array())
  275.     {
  276.         if ($this->currentCategory !== null{
  277.             if (!isset($this->api[$this->currentCategory][$endpoint])) {
  278.                 throw new Services_Twitter_Exception(
  279.                     'Unsupported endpoint ' 
  280.                     . $this->currentCategory . '/' $endpoint,
  281.                     self::ERROR_ENDPOINT
  282.                 );
  283.             }
  284.             // case of a classic "category->endpoint()" call
  285.             $ep $this->api[$this->currentCategory][$endpoint]
  286.         else if (isset($this->api[$endpoint][$endpoint])) {
  287.             // case of a "root" endpoint call, the method is the name of the 
  288.             // category (ex: $twitter->direct_messages())
  289.             $ep $this->api[$endpoint][$endpoint];
  290.         else {
  291.             throw new Services_Twitter_Exception(
  292.                 'Unsupported endpoint ' $endpoint,
  293.                 self::ERROR_ENDPOINT
  294.             );
  295.         }
  296.  
  297.         // check that endpoint is available in the configured format
  298.         $formats explode(','(string)$ep->formats);
  299.         if (!in_array($this->getOption('format')$formats)) {
  300.             throw new Services_Twitter_Exception(
  301.                 'Endpoint ' $endpoint ' does not support '
  302.                 . $this->getOption('format'' format',
  303.                 self::ERROR_ENDPOINT
  304.             );
  305.         }
  306.  
  307.         // we must reset the current category to null for future calls.
  308.         $cat                   $this->currentCategory;
  309.         $this->currentCategory = null;
  310.  
  311.         // prepare the request
  312.         list($uri$method$params$files)
  313.             = $this->prepareRequest($ep$args$cat);
  314.  
  315.         // we can now send our request
  316.         if ($this->oauth instanceof HTTP_OAuth_Consumer{
  317.             $resp $this->sendOAuthRequest($uri$method$params$files);
  318.         else {
  319.             $resp $this->sendRequest($uri$method$params$files);
  320.         }
  321.         $body $resp->getBody();
  322.  
  323.         // check for errors
  324.         if (substr($resp->getStatus()01!= '2'{
  325.             $error $this->decodeBody($body);
  326.             if (isset($error->error)) {
  327.                 $message = (string) $error->error;
  328.             else {
  329.                 $message $resp->getReasonPhrase();
  330.             }
  331.             throw new Services_Twitter_Exception(
  332.                 $message,
  333.                 $resp->getStatus(),
  334.                 $uri,
  335.                 $resp
  336.             );
  337.         }
  338.  
  339.         if ($this->getOption('raw_format')) {
  340.             return $body;
  341.         }
  342.         return $this->decodeBody($body);
  343.     }
  344.  
  345.     // }}}
  346.     // sendOAuthRequest() {{{
  347.  
  348.     /**
  349.      * Sends a request using OAuth instead of basic auth
  350.      * 
  351.      * @param string $uri    The full URI of the endpoint
  352.      * @param string $method GET or POST
  353.      * @param array  $params Array of additional parameter
  354.      * @param array  $files  Array of files to upload
  355.      * 
  356.      * @throws Services_Twitter_Exception on failure
  357.      * @return HTTP_Request2_Response 
  358.      * @see prepareRequest()
  359.      */
  360.     protected function sendOAuthRequest($uri$method$params$files)
  361.     {
  362.         include_once 'HTTP/OAuth/Consumer/Request.php';
  363.         try {
  364.             $request = clone $this->getRequest();
  365.  
  366.             if ($method == 'POST'{
  367.                 foreach ($files as $key => $val{
  368.                     $request->addUpload($key$val);
  369.                 }
  370.             }
  371.  
  372.             // Use the same instance of HTTP_Request2
  373.             $consumerRequest = new HTTP_OAuth_Consumer_Request;
  374.             $consumerRequest->accept($request);
  375.             $this->oauth->accept($consumerRequest);
  376.  
  377.             $response $this->oauth->sendRequest($uri$params$method);
  378.         catch (HTTP_Request2_Exception $exc{
  379.             throw new Services_Twitter_Exception(
  380.                 $exc->getMessage(),
  381.                 $exc// the original exception cause
  382.                 $uri
  383.             );
  384.         }
  385.         return $response;
  386.     }
  387.  
  388.     // }}}
  389.     // setOption() {{{
  390.  
  391.     /**
  392.      * Set an option in {@link Services_Twitter::$options}
  393.      *
  394.      * If a function exists named _set$option (e.g. _setUserAgent()) then that
  395.      * method will be used instead. Otherwise, the value is set directly into
  396.      * the options array.
  397.      *
  398.      * @param string $option Name of option
  399.      * @param mixed  $value  Value of option
  400.      *
  401.      * @return void 
  402.      * @see Services_Twitter::$options
  403.      */
  404.     public function setOption($option$value)
  405.     {
  406.         $func '_set' ucfirst($option);
  407.         if (method_exists($this$func)) {
  408.             $this->$func($value);
  409.         else {
  410.             $this->options[$option$value;
  411.         }
  412.     }
  413.  
  414.     /**
  415.      * Sets an instance of HTTP_OAuth_Consumer
  416.      * 
  417.      * @param HTTP_OAuth_Consumer $oauth Object containing OAuth credentials
  418.      * 
  419.      * @see $oauth
  420.      * @see $sendOAuthRequest
  421.      * @return void 
  422.      */
  423.     public function setOAuth(HTTP_OAuth_Consumer $oauth)
  424.     {
  425.         $this->oauth = $oauth;
  426.     }
  427.  
  428.     // }}}
  429.     // getOption() {{{
  430.  
  431.     /**
  432.      * Get an option from {@link Services_Twitter::$options}
  433.      *
  434.      * @param string $option Name of option
  435.      *
  436.      * @return mixed 
  437.      * @see Services_Twitter::$options
  438.      */
  439.     public function getOption($option)
  440.     {
  441.         if (isset($this->options[$option])) {
  442.             return $this->options[$option];
  443.         }
  444.         return null;
  445.     }
  446.  
  447.     // }}}
  448.     // setOptions() {{{
  449.  
  450.     /**
  451.      * Set a number of options at once in {@link Services_Twitter::$options}
  452.      *
  453.      * @param array $options Associative array of options name/value
  454.      *
  455.      * @return void 
  456.      * @see Services_Twitter::$options
  457.      * @see Services_Twitter::setOption()
  458.      */
  459.     public function setOptions(array $options)
  460.     {
  461.         foreach ($options as $option => $value{
  462.             $this->setOption($option$value);
  463.         }
  464.     }
  465.  
  466.     // }}}
  467.     // getOptions() {{{
  468.  
  469.     /**
  470.      * Return the Services_Twitter options array.
  471.      *
  472.      * @return array 
  473.      * @see Services_Twitter::$options
  474.      */
  475.     public function getOptions()
  476.     {
  477.         return $this->options;
  478.     }
  479.  
  480.     // }}}
  481.     // getRequest() {{{
  482.     
  483.     /**
  484.      * Returns the HTTP_Request2 instance.
  485.      * 
  486.      * @return HTTP_Request2 The request
  487.      */
  488.     public function getRequest()
  489.     {
  490.         if ($this->request === null{
  491.             $this->request = new HTTP_Request2();
  492.         }
  493.         if ($this->getOption('use_ssl')) {
  494.             // XXX ssl won't work with ssl_verify_peer set to true, which is 
  495.             // the default in HTTP_Request2
  496.             $this->request->setConfig('ssl_verify_peer'false);
  497.         }
  498.         return $this->request;
  499.     }
  500.     
  501.     // }}}
  502.     // setRequest() {{{
  503.     
  504.     /**
  505.      * Sets the HTTP_Request2 instance.
  506.      * 
  507.      * @param HTTP_Request2 $request The request to set
  508.      *
  509.      * @return void 
  510.      */
  511.     public function setRequest(HTTP_Request2 $request)
  512.     {
  513.         $this->request = $request;
  514.     }
  515.     
  516.     // }}}
  517.     // decodeBody() {{{
  518.  
  519.     /**
  520.      * Decode the response body according to the configured format.
  521.      *
  522.      * @param string $body The response body to decode
  523.      *
  524.      * @return mixed 
  525.      */
  526.     protected function decodeBody($body)
  527.     {
  528.  
  529.         if ($this->getOption('format'== Services_Twitter::OUTPUT_XML{
  530.             $result simplexml_load_string($body);
  531.             $isbool ((string)$result == 'true' || (string)$result == 'false');
  532.         else 
  533.             // default to Services_Twitter::OUTPUT_JSON
  534.             $result json_decode($body);
  535.             $isbool ($result == 'true' || $result == 'false');
  536.         }
  537.         // special case where the API returns true/false strings
  538.         if ($isbool{
  539.             return (string)$result == 'true';
  540.         }
  541.         return $result;
  542.     }
  543.  
  544.     // }}}
  545.     // loadAPI() {{{
  546.  
  547.     /**
  548.      * Loads the XML API definition.
  549.      *
  550.      * @return void 
  551.      */
  552.     protected function loadAPI()
  553.     {
  554.         // initialize xml mapping
  555.         $p is_dir('@data_dir@'
  556.             ? array('@data_dir@''Services_Twitter''data')
  557.             : array(dirname(__FILE__)'..''data');
  558.         $d implode(DIRECTORY_SEPARATOR$p. DIRECTORY_SEPARATOR;
  559.         if ($this->getOption('validate'&& class_exists('DomDocument')) {
  560.             // this should be done only when testing
  561.             $doc = new DomDocument();
  562.             $doc->load($d 'api.xml');
  563.             $doc->relaxNGValidate($d 'api.rng');
  564.         }
  565.         $xmlApi simplexml_load_file($d 'api.xml');
  566.         foreach ($xmlApi->category as $category{
  567.             $catName             = (string)$category['name'];
  568.             $this->api[$catName= array();
  569.             foreach ($category->endpoint as $endpoint{
  570.                 $this->api[$catName][(string)$endpoint['name']] $endpoint;
  571.             }
  572.         }
  573.     }
  574.  
  575.     // }}}
  576.     // prepareRequest() {{{
  577.  
  578.     /**
  579.      * Prepare the request before it is sent.
  580.      *
  581.      * @param SimpleXMLElement $endpoint API endpoint xml node
  582.      * @param array            $args     API endpoint arguments
  583.      * @param string           $cat      The current category
  584.      *
  585.      * @throws Services_Twitter_Exception
  586.      * @return array The array of arguments to pass to in the request
  587.      */
  588.     protected function prepareRequest($endpointarray $args = array()$cat = null)
  589.     {
  590.         $params = array();
  591.         $files  = array();
  592.  
  593.         // check if we have is a search or trends call, in this case the base 
  594.         // uri is different
  595.         if (   $cat == 'search' 
  596.             || (string)$endpoint['name'== 'search'
  597.             || $cat == 'trends' 
  598.             || (string)$endpoint['name'== 'trends'
  599.         {
  600.             $uri = self::$searchUri;
  601.         else {
  602.             $uri = self::$uri;
  603.         }
  604.  
  605.         // ssl requested
  606.         if ($this->getOption('use_ssl')) {
  607.             $uri str_replace('http://''https://'$uri);
  608.         }
  609.  
  610.         // build the uri path
  611.         $path '/';
  612.         if ($cat !== null{
  613.             $path .= $cat '/';
  614.         }
  615.         $method = (string)$endpoint['method'];
  616.         $path  .= (string)$endpoint['name'];
  617.  
  618.         // check if we have a POST method and a registered source to pass
  619.         $source $this->getOption('source');
  620.         if ($method == 'POST' && $source !== null{
  621.             $params['source'$source;
  622.         }
  623.         
  624.         // check arguments requirements
  625.         $minargs = isset($endpoint['min_args'])
  626.             ? (int)$endpoint['min_args']
  627.             : count($endpoint->xpath('param[@required="true" or @required="1"]'));
  628.         if (!$minargs && (isset($args[0]&& !is_array($args[0]))) {
  629.             throw new Services_Twitter_Exception(
  630.                 $path ' expects an array as unique parameter',
  631.                 self::ERROR_PARAMS,
  632.                 $path
  633.             );
  634.         }
  635.         if (   $minargs && (!isset($args[0]
  636.             || is_array($args[0]
  637.             && $minargs count($args[0]))
  638.         {
  639.             throw new Services_Twitter_Exception(
  640.                 'Not enough arguments for ' $path,
  641.                 self::ERROR_PARAMS,
  642.                 $path
  643.             );
  644.         }
  645.         $needargs $minargs;
  646.  
  647.         // now process arguments according to their definition in the xml 
  648.         // mapping
  649.         foreach ($endpoint->param as $param{
  650.             $pName      = (string)$param['name'];
  651.             $pType      = (string)$param['type'];
  652.             $pMaxLength = (int)$param['max_length'];
  653.             $pMaxLength $pMaxLength > 0 ? $pMaxLength : null;
  654.             $pReq       = (string)$param['required'== 'true' || $needargs;
  655.             if ($pReq && !is_array($args[0])) {
  656.                 $arg array_shift($args);
  657.                 $needargs--;
  658.             else if (isset($args[0][$pName])) {
  659.                 $arg $args[0][$pName];
  660.                 $needargs--;
  661.             else {
  662.                 continue;
  663.             }
  664.             try {
  665.                 $this->validateArg($pName$arg$pType$pMaxLength);
  666.             catch (Exception $exc{
  667.                 throw new Services_Twitter_Exception(
  668.                     $path ': ' $exc->getMessage(),
  669.                     self::ERROR_PARAMS,
  670.                     $path
  671.                 );
  672.             }
  673.             if ($pName == 'id'{
  674.                 $path .= '/' $arg;
  675.             else {
  676.                 if ($pType == 'string' && !$this->isUtf8($arg)) {
  677.                     // iso-8859-1 string that we must convert to unicode
  678.                     $arg utf8_encode($arg);
  679.                 }
  680.                 if ($pType == 'image'{
  681.                     // we have a file upload
  682.                     $files[$pName$arg;
  683.                 else {
  684.                     $params[$pName$arg;
  685.                 }
  686.             }
  687.         }
  688.         $uri .= $path '.' $this->getOption('format');
  689.         return array($uri$method$params$files);
  690.     }
  691.  
  692.     // }}}
  693.     // sendRequest() {{{
  694.  
  695.     /**
  696.      * Send a request to the Twitter API.
  697.      *
  698.      * @param string $uri    The full URI to the API endpoint
  699.      * @param array  $method The HTTP request method (GET or POST)
  700.      * @param array  $args   The API endpoint arguments if any
  701.      * @param array  $files  The API endpoint file uploads if any
  702.      *
  703.      * @throws Services_Twitter_Exception
  704.      * @return object Instance of SimpleXMLElement
  705.      */
  706.     protected function sendRequest($uri$method 'GET'array $args = array(),
  707.         array $files = array()
  708.     {
  709.         try {
  710.             $request = clone $this->getRequest();
  711.             $request->setMethod($method);
  712.             if ($method == 'POST'{
  713.                 foreach ($args as $key => $val{
  714.                     $request->addPostParameter($key$val);
  715.                 }
  716.                 foreach ($files as $key => $val{
  717.                     $request->addUpload($key$val);
  718.                 }
  719.             else {
  720.                 $prefix '?';
  721.                 foreach ($args as $key => $val{
  722.                     $uri   .= $prefix $key '=' urlencode($val);
  723.                     $prefix '&';
  724.                 }
  725.             }
  726.             $request->setUrl($uri);
  727.             if ($this->user !== null && $this->pass !== null{
  728.                 $request->setAuth($this->user$this->pass);
  729.             }
  730.             $response $request->send();
  731.         catch (HTTP_Request2_Exception $exc{
  732.             throw new Services_Twitter_Exception(
  733.                 $exc->getMessage(),
  734.                 $exc// the original exception cause
  735.                 $uri
  736.             );
  737.         }
  738.         return $response;
  739.     }
  740.  
  741.     // }}}
  742.     // validateArg() {{{
  743.  
  744.     /**
  745.      * Check the validity of an argument (required, max length, type...).
  746.      *
  747.      * @param array $name      The argument name
  748.      * @param array &$val      The argument value, passed by reference
  749.      * @param array $type      The argument type
  750.      * @param array $maxLength The argument maximum length (optional)
  751.      *
  752.      * @throws Services_Twitter_Exception
  753.      * @return void 
  754.      */
  755.     protected function validateArg($name&$val$type$maxLength = null)
  756.     {
  757.         // check length if necessary
  758.         if ($maxLength !== null && strlen($val$maxLength{
  759.             throw new Exception(
  760.                 $name ' must not exceed ' $maxLength ' chars',
  761.                 self::ERROR_PARAMS
  762.             );
  763.         }
  764.  
  765.         // type checks
  766.         $msg = null;
  767.         switch ($type{
  768.         case 'boolean':
  769.             if (!is_bool($val)) {
  770.                 $msg $name ' must be a boolean';
  771.             }
  772.             // we modify the value by reference
  773.             $val $val 'true' 'false';
  774.             break;
  775.         case 'integer':
  776.             if (!is_numeric($val)) {
  777.                 $msg $name ' must be an integer';
  778.             }
  779.             break;
  780.         case 'string':
  781.             if (!is_string($val)) {
  782.                 $msg $name ' must be a string';
  783.             }
  784.             break;
  785.         case 'date':
  786.             if (is_numeric($val)) {
  787.                 // we have a timestamp
  788.                 $val date('Y-m-d'$val);
  789.             else {
  790.                 $rx '/^\d{4}\-\d{2}\-\d{2}$/';
  791.                 if (!preg_match($rx$val)) {
  792.                     $msg $name ' should be formatted YYYY-MM-DD';
  793.                 }
  794.             }
  795.             break;
  796.         case 'id_or_screenname':
  797.             if (!preg_match('/^[a-zA-Z0-9_\.]{1,16}$/'$val)) {
  798.                 $msg $name ' must be a valid id or screen name';
  799.             }
  800.             break;
  801.         case 'device':
  802.             $devices = array('none''sms''im');
  803.             if (!in_array($val$devices)) {
  804.                 $msg $name ' must be one of the following: ' 
  805.                      . implode(', '$devices);
  806.             }
  807.             break;
  808.         case 'iso-639-1':
  809.             if (strlen($val!= 2{
  810.                 $msg $name ' must be a valid iso-639-1 language code';
  811.             }
  812.             break;
  813.         case 'geocode':
  814.             if (!preg_match('/^([-\d\.]+,){2}([-\d\.]+(km|mi)$)/'$val)) {
  815.                 $msg $name ' must be "latitide,longitude,radius(km or mi)"';
  816.             }
  817.             break;
  818.         case 'color':
  819.             if (!preg_match('/^([0-9a-f]{1,2}){3}$/i'$val)) {
  820.                 $msg $name ' must be an hexadecimal color code (eg. fff)';
  821.             }
  822.             break;
  823.         case 'image':
  824.             if (!file_exists($val|| !is_readable($val)) {
  825.                 $msg $name ' must be a valid image path';
  826.             }
  827.             // XXX we don't check the image type for now...
  828.             break;
  829.         }
  830.         if ($msg !== null{
  831.             throw new Services_Twitter_Exception($msgself::ERROR_PARAMS);
  832.         }
  833.     }
  834.  
  835.     // }}}
  836.     // isUtf8() {{{
  837.  
  838.     /**
  839.      * Check if the given string is a UTF-8 string or an iso-8859-1 one.
  840.      *
  841.      * @param string $str The string to check
  842.      *
  843.      * @return boolean Wether the string is unicode or not
  844.      */
  845.     protected function isUtf8($str)
  846.     {
  847.         return (bool)preg_match(
  848.             '%^(?:
  849.                   [\x09\x0A\x0D\x20-\x7E]            # ASCII
  850.                 | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
  851.                 |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
  852.                 | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
  853.                 |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
  854.                 |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
  855.                 | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
  856.                 |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
  857.             )*$%xs',
  858.             $str
  859.         );
  860.     }
  861.  
  862.     // }}}
  863. }

Documentation generated on Wed, 06 Jan 2010 22:00:03 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.