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.             // See http://pear.php.net/bugs/bug.php?id=17345
  531.             $previousSetting libxml_use_internal_errors(true);
  532.             $result          simplexml_load_string($body);
  533.             libxml_use_internal_errors($previousSetting);
  534.             $isbool ((string)$result == 'true' || (string)$result == 'false');
  535.         else 
  536.             // default to Services_Twitter::OUTPUT_JSON
  537.             $result json_decode($body);
  538.             $isbool ($result == 'true' || $result == 'false');
  539.         }
  540.         // special case where the API returns true/false strings
  541.         if ($isbool{
  542.             return (string)$result == 'true';
  543.         }
  544.         return $result;
  545.     }
  546.  
  547.     // }}}
  548.     // loadAPI() {{{
  549.  
  550.     /**
  551.      * Loads the XML API definition.
  552.      *
  553.      * @return void 
  554.      */
  555.     protected function loadAPI()
  556.     {
  557.         // initialize xml mapping
  558.         $p is_dir('@data_dir@'
  559.             ? array('@data_dir@''Services_Twitter''data')
  560.             : array(dirname(__FILE__)'..''data');
  561.         $d implode(DIRECTORY_SEPARATOR$p. DIRECTORY_SEPARATOR;
  562.         if ($this->getOption('validate'&& class_exists('DomDocument')) {
  563.             // this should be done only when testing
  564.             $doc = new DomDocument();
  565.             $doc->load($d 'api.xml');
  566.             $doc->relaxNGValidate($d 'api.rng');
  567.         }
  568.         $xmlApi simplexml_load_file($d 'api.xml');
  569.         foreach ($xmlApi->category as $category{
  570.             $catName             = (string)$category['name'];
  571.             $this->api[$catName= array();
  572.             foreach ($category->endpoint as $endpoint{
  573.                 $this->api[$catName][(string)$endpoint['name']] $endpoint;
  574.             }
  575.         }
  576.     }
  577.  
  578.     // }}}
  579.     // prepareRequest() {{{
  580.  
  581.     /**
  582.      * Prepare the request before it is sent.
  583.      *
  584.      * @param SimpleXMLElement $endpoint API endpoint xml node
  585.      * @param array            $args     API endpoint arguments
  586.      * @param string           $cat      The current category
  587.      *
  588.      * @throws Services_Twitter_Exception
  589.      * @return array The array of arguments to pass to in the request
  590.      */
  591.     protected function prepareRequest($endpointarray $args = array()$cat = null)
  592.     {
  593.         $params = array();
  594.         $files  = array();
  595.  
  596.         // check if we have is a search or trends call, in this case the base 
  597.         // uri is different
  598.         if (   $cat == 'search' 
  599.             || $cat == 'trends'
  600.             && !in_array((string)$endpoint['name']array('available''location')))
  601.         {
  602.             $uri = self::$searchUri;
  603.         else {
  604.             $uri = self::$uri;
  605.         }
  606.  
  607.         // ssl requested
  608.         if ($this->getOption('use_ssl')) {
  609.             $uri str_replace('http://''https://'$uri);
  610.         }
  611.  
  612.         // build the uri path
  613.         $path '/';
  614.         if ($cat !== null && $cat !== 'search'{
  615.             $path .= $cat '/';
  616.         }
  617.         $path  .= (string)$endpoint['name'];
  618.         $method = (string)$endpoint['method'];
  619.  
  620.         // check if we have a POST method and a registered source to pass
  621.         $source $this->getOption('source');
  622.         if ($method == 'POST' && $source !== null{
  623.             $params['source'$source;
  624.         }
  625.         
  626.         // check arguments requirements
  627.         $minargs = isset($endpoint['min_args'])
  628.             ? (int)$endpoint['min_args']
  629.             : count($endpoint->xpath('param[@required="true" or @required="1"]'));
  630.         if (!$minargs && (isset($args[0]&& !is_array($args[0]))) {
  631.             throw new Services_Twitter_Exception(
  632.                 $path ' expects an array as unique parameter',
  633.                 self::ERROR_PARAMS,
  634.                 $path
  635.             );
  636.         }
  637.         if (   $minargs && (!isset($args[0]
  638.             || is_array($args[0]
  639.             && $minargs count($args[0]))
  640.         {
  641.             throw new Services_Twitter_Exception(
  642.                 'Not enough arguments for ' $path,
  643.                 self::ERROR_PARAMS,
  644.                 $path
  645.             );
  646.         }
  647.         $needargs $minargs;
  648.  
  649.         $routing = array();
  650.         if (isset($endpoint['routing'])) {
  651.             $routing explode('/'(string)$endpoint['routing']);
  652.         }
  653.         // now process arguments according to their definition in the xml 
  654.         // mapping
  655.         foreach ($endpoint->param as $param{
  656.             $pName      = (string)$param['name'];
  657.             $pType      = (string)$param['type'];
  658.             $pMaxLength = (int)$param['max_length'];
  659.             $pMaxLength $pMaxLength > 0 ? $pMaxLength : null;
  660.             $pReq       = (string)$param['required'== 'true' || $needargs;
  661.             if ($pReq && !is_array($args[0])) {
  662.                 $arg array_shift($args);
  663.                 $needargs--;
  664.             else if (isset($args[0][$pName])) {
  665.                 $arg $args[0][$pName];
  666.                 $needargs--;
  667.             else {
  668.                 continue;
  669.             }
  670.             try {
  671.                 $this->validateArg($pName$arg$pType$pMaxLength);
  672.             catch (Exception $exc{
  673.                 throw new Services_Twitter_Exception(
  674.                     $path ': ' $exc->getMessage(),
  675.                     self::ERROR_PARAMS,
  676.                     $path
  677.                 );
  678.             }
  679.             if (in_array(':' $pName$routing)) {
  680.                 $routing[array_search(':' $pName$routing)rawurlencode($arg);
  681.             else {
  682.                 if ($pName == 'id'{
  683.                      if (count($routing> 1{
  684.                          $params[$pName$arg;
  685.                          if ($method == 'DELETE'{
  686.                              $method "POST";
  687.                              $params['_method''DELETE';
  688.                          }
  689.                      else {
  690.                          $path .= '/' $arg;
  691.                      }
  692.                 else {
  693.                     if ($pType == 'string' && !$this->isUtf8($arg)) {
  694.                         // iso-8859-1 string that we must convert to unicode
  695.                         $arg utf8_encode($arg);
  696.                     }
  697.                     if ($pType == 'image'{
  698.                         // we have a file upload
  699.                         $files[$pName$arg;
  700.                     else {
  701.                         $params[$pName$arg;
  702.                     }
  703.                 }
  704.             }
  705.         }
  706.         $path count($routingimplode('/'$routing$path;
  707.         $uri .= $path '.' $this->getOption('format');
  708.         return array($uri$method$params$files);
  709.     }
  710.  
  711.     // }}}
  712.     // sendRequest() {{{
  713.  
  714.     /**
  715.      * Send a request to the Twitter API.
  716.      *
  717.      * @param string $uri    The full URI to the API endpoint
  718.      * @param array  $method The HTTP request method (GET or POST)
  719.      * @param array  $args   The API endpoint arguments if any
  720.      * @param array  $files  The API endpoint file uploads if any
  721.      *
  722.      * @throws Services_Twitter_Exception
  723.      * @return object Instance of SimpleXMLElement
  724.      */
  725.     protected function sendRequest($uri$method 'GET'array $args = array(),
  726.         array $files = array()
  727.     {
  728.         try {
  729.             $request = clone $this->getRequest();
  730.             $request->setMethod($method);
  731.             if ($method == 'POST'{
  732.                 foreach ($args as $key => $val{
  733.                     $request->addPostParameter($key$val);
  734.                 }
  735.                 foreach ($files as $key => $val{
  736.                     $request->addUpload($key$val);
  737.                 }
  738.             else {
  739.                 $prefix '?';
  740.                 foreach ($args as $key => $val{
  741.                     $uri   .= $prefix $key '=' urlencode($val);
  742.                     $prefix '&';
  743.                 }
  744.             }
  745.             $request->setUrl($uri);
  746.             if ($this->user !== null && $this->pass !== null{
  747.                 $request->setAuth($this->user$this->pass);
  748.             }
  749.             $response $request->send();
  750.         catch (HTTP_Request2_Exception $exc{
  751.             throw new Services_Twitter_Exception(
  752.                 $exc->getMessage(),
  753.                 $exc// the original exception cause
  754.                 $uri
  755.             );
  756.         }
  757.         return $response;
  758.     }
  759.  
  760.     // }}}
  761.     // validateArg() {{{
  762.  
  763.     /**
  764.      * Check the validity of an argument (required, max length, type...).
  765.      *
  766.      * @param array $name      The argument name
  767.      * @param array &$val      The argument value, passed by reference
  768.      * @param array $type      The argument type
  769.      * @param array $maxLength The argument maximum length (optional)
  770.      *
  771.      * @throws Services_Twitter_Exception
  772.      * @return void 
  773.      */
  774.     protected function validateArg($name&$val$type$maxLength = null)
  775.     {
  776.         // check length if necessary
  777.         if ($maxLength !== null && mb_strlen($val'UTF-8'$maxLength{
  778.             throw new Exception(
  779.                 $name ' must not exceed ' $maxLength ' chars',
  780.                 self::ERROR_PARAMS
  781.             );
  782.         }
  783.  
  784.         // type checks
  785.         $msg = null;
  786.         switch ($type{
  787.         case 'boolean':
  788.             if (!is_bool($val)) {
  789.                 $msg $name ' must be a boolean';
  790.             }
  791.             // we modify the value by reference
  792.             $val $val 'true' 'false';
  793.             break;
  794.         case 'integer':
  795.             if (!is_numeric($val)) {
  796.                 $msg $name ' must be an integer';
  797.             }
  798.             break;
  799.         case 'string':
  800.             if (!is_string($val)) {
  801.                 $msg $name ' must be a string';
  802.             }
  803.             break;
  804.         case 'date':
  805.             if (is_numeric($val)) {
  806.                 // we have a timestamp
  807.                 $val date('Y-m-d'$val);
  808.             else {
  809.                 $rx '/^\d{4}\-\d{2}\-\d{2}$/';
  810.                 if (!preg_match($rx$val)) {
  811.                     $msg $name ' should be formatted YYYY-MM-DD';
  812.                 }
  813.             }
  814.             break;
  815.         case 'id_or_screenname':
  816.             if (!preg_match('/^[a-zA-Z0-9_\.]{1,16}$/'$val)) {
  817.                 $msg $name ' must be a valid id or screen name';
  818.             }
  819.             break;
  820.         case 'device':
  821.             $devices = array('none''sms''im');
  822.             if (!in_array($val$devices)) {
  823.                 $msg $name ' must be one of the following: ' 
  824.                      . implode(', '$devices);
  825.             }
  826.             break;
  827.         case 'iso-639-1':
  828.             if (strlen($val!= 2{
  829.                 $msg $name ' must be a valid iso-639-1 language code';
  830.             }
  831.             break;
  832.         case 'geocode':
  833.             if (!preg_match('/^([-\d\.]+,){2}([-\d\.]+(km|mi)$)/'$val)) {
  834.                 $msg $name ' must be "latitide,longitude,radius(km or mi)"';
  835.             }
  836.             break;
  837.         case 'lat':
  838.         case 'long':
  839.             if (!is_numeric($val)) {
  840.                 $msg $name ' must be a float';
  841.             else {
  842.                 $val floatval($val);
  843.                 if ($type == 'lat' && ($val < -90 || $val > 90)) {
  844.                     $msg 'valid range for ' $name ' is -90.0 to +90.0';
  845.                 }
  846.                 if ($type == 'long' && ($val < -180 || $val > 180)) {
  847.                     $msg 'valid range for ' $name ' is -180.0 to +180.0';
  848.                 }
  849.             }
  850.             break;
  851.         case 'color':
  852.             if (!preg_match('/^([0-9a-f]{1,2}){3}$/i'$val)) {
  853.                 $msg $name ' must be an hexadecimal color code (eg. fff)';
  854.             }
  855.             break;
  856.         case 'image':
  857.             if (!file_exists($val|| !is_readable($val)) {
  858.                 $msg $name ' must be a valid image path';
  859.             }
  860.             // XXX we don't check the image type for now...
  861.             break;
  862.         case 'listid_or_slug':
  863.             if (!preg_match('/^[0-9a-z]+(?:-?[0-9a-z]+)*$/'$val)) {
  864.                 $msg $name ' must be a valid list id or slug';
  865.             }
  866.             break;
  867.         case 'mode':
  868.             $modes = array('public''private');
  869.             if (!in_array($val$modes)) {
  870.                 $msg $name ' must be one of the following: '
  871.                      . implode(', '$modes);
  872.             }
  873.             break;
  874.         }
  875.         if ($msg !== null{
  876.             throw new Services_Twitter_Exception($msgself::ERROR_PARAMS);
  877.         }
  878.     }
  879.  
  880.     // }}}
  881.     // isUtf8() {{{
  882.  
  883.     /**
  884.      * Check if the given string is a UTF-8 string or an iso-8859-1 one.
  885.      *
  886.      * @param string $str The string to check
  887.      *
  888.      * @return boolean Wether the string is unicode or not
  889.      */
  890.     protected function isUtf8($str)
  891.     {
  892.         return (bool)preg_match(
  893.             '%^(?:
  894.                   [\x09\x0A\x0D\x20-\x7E]            # ASCII
  895.                 | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
  896.                 |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
  897.                 | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
  898.                 |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
  899.                 |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
  900.                 | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
  901.                 |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
  902.             )*$%xs',
  903.             $str
  904.         );
  905.     }
  906.  
  907.     // }}}
  908. }

Documentation generated on Mon, 11 Mar 2019 15:39:03 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.