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

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