Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Unknown

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /simplepie/idn/idna_convert.class.php

Documentation is available at idna_convert.class.php

  1. <?php
  2. /* ------------------------------------------------------------------------- */
  3. /* idna_convert.class.php - Encode / Decode Internationalized Domain Names   */
  4. /* (c) 2004-2005 phlyLabs, Berlin (http://phlylabs.de)                       */
  5. /* All rights reserved                                                       */
  6. /* v0.4.2                                                                    */
  7. /* ------------------------------------------------------------------------- */
  8.  
  9. // {{{ license
  10.  
  11. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  12. //
  13. // +----------------------------------------------------------------------+
  14. // | This library is free software; you can redistribute it and/or modify |
  15. // | it under the terms of the GNU Lesser General Public License as       |
  16. // | published by the Free Software Foundation; either version 2.1 of the |
  17. // | License, or (at your option) any later version.                      |
  18. // |                                                                      |
  19. // | This library is distributed in the hope that it will be useful, but  |
  20. // | WITHOUT ANY WARRANTY; without even the implied warranty of           |
  21. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
  22. // | Lesser General Public License for more details.                      |
  23. // |                                                                      |
  24. // | You should have received a copy of the GNU Lesser General Public     |
  25. // | License along with this library; if not, write to the Free Software  |
  26. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 |
  27. // | USA.                                                                 |
  28. // +----------------------------------------------------------------------+
  29. //
  30.  
  31. // }}}
  32.  
  33. /**
  34.  * Encode/decode Internationalized Domain Names.
  35.  *
  36.  * The class allows to convert internationalized domain names
  37.  * (see RFC 3490 for details) as they can be used with various registries worldwide
  38.  * to be translated between their original (localized) form and their encoded form
  39.  * as it will be used in the DNS (Domain Name System).
  40.  *
  41.  * The class provides two public methods, encode() and decode(), which do exactly
  42.  * what you would expect them to do. You are allowed to use complete domain names,
  43.  * simple strings and complete email addresses as well. That means, that you might
  44.  * use any of the following notations:
  45.  *
  46.  * - www.nörgler.com
  47.  * - xn--nrgler-wxa
  48.  * - xn--brse-5qa.xn--knrz-1ra.info
  49.  *
  50.  * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4
  51.  * array. Unicode output is available in the same formats.
  52.  * You can select your preferred format via {@link set_paramter()}.
  53.  *
  54.  * ACE input and output is always expected to be ASCII.
  55.  *
  56.  * @author  Matthias Sommerfeld <[email protected]>
  57.  * @version 0.4.2
  58.  *
  59.  */
  60.  
  61. {
  62.     // {{{ npdata
  63.         /**
  64.      * Holds all relevant mapping tables, loaded from a seperate file on construct
  65.      * See RFC3454 for details
  66.      *
  67.      * @var array 
  68.      * @access private
  69.      */
  70.     var $_np_ array();
  71.     // }}}
  72.  
  73.  
  74.     // Internal settings, do not mess with them
  75.         var $_punycode_prefix = 'xn--';
  76.     var $_invalid_ucs =     0x80000000;
  77.     var $_max_ucs =         0x10FFFF;
  78.     var $_base =            36;
  79.     var $_tmin =            1;
  80.     var $_tmax =            26;
  81.     var $_skew =            38;
  82.     var $_damp =            700;
  83.     var $_initial_bias =    72;
  84.     var $_initial_n =       0x80;
  85.     var $_sbase =           0xAC00;
  86.     var $_lbase =           0x1100;
  87.     var $_vbase =           0x1161;
  88.     var $_tbase =           0x11a7;
  89.     var $_lcount =          19;
  90.     var $_vcount =          21;
  91.     var $_tcount =          28;
  92.     var $_ncount =          588;   // _vcount * _tcount
  93.         var $_scount =          11172// _lcount * _tcount * _vcount
  94.         var $_error =           false;
  95.  
  96.     // See set_parameter() for details of how to change the following settings
  97.     // from within your script / application
  98.         var $_api_encoding   =  'utf8'// Default input charset is UTF-8
  99.         var $_allow_overlong =  false;  // Overlong UTF-8 encodings are forbidden
  100.         var $_strict_mode    =  false;  // Behave strict or not
  101.  
  102.     // The constructor
  103.         function idna_convert($options false)
  104.     {
  105.         $this->slast $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
  106.         if (function_exists('file_get_contents')) {
  107.             $this->_np_ unserialize(file_get_contents(dirname(__FILE__).'/npdata.ser'));
  108.         else {
  109.             $this->_np_ unserialize(join(''file(dirname(__FILE__).'/npdata.ser')));
  110.         }
  111.         // If parameters are given, pass these to the respective method
  112.         if (is_array($options)) {
  113.             return $this->set_parameter($options);
  114.         }
  115.         return true;
  116.     }
  117.  
  118.     /**
  119.     * Sets a new option value. Available options and values:
  120.     * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
  121.     *         'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
  122.     * [overlong - Unicode does not allow unnecessarily long encodings of chars,
  123.     *             to allow this, set this parameter to true, else to false;
  124.     *             default is false.]
  125.     * [strict - true: strict mode, good for registration purposes - Causes errors
  126.     *           on failures; false: loose mode, ideal for "wildlife" applications
  127.     *           by silently ignoring errors and returning the original input instead
  128.     *
  129.     * @param    mixed     Parameter to set (string: single parameter; array of Parameter => Value pairs)
  130.     * @param    string    Value to use (if parameter 1 is a string)
  131.     * @return   boolean   true on success, false otherwise
  132.     * @access   public
  133.     */
  134.     function set_parameter($option$value false)
  135.     {
  136.         if (!is_array($option)) {
  137.             $option array($option => $value);
  138.         }
  139.         foreach ($option as $k => $v{
  140.             switch ($k{
  141.             case 'encoding':
  142.                 switch ($v{
  143.                 case 'utf8':
  144.                 case 'ucs4_string':
  145.                 case 'ucs4_array':
  146.                     $this->_api_encoding = $v;
  147.                     break;
  148.                 default:
  149.                     $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k);
  150.                     return false;
  151.                 }
  152.                 break;
  153.             case 'overlong':
  154.                 $this->_allow_overlong = ($vtrue false;
  155.                 break;
  156.             case 'strict':
  157.                 $this->_strict_mode = ($vtrue false;
  158.                 break;
  159.             default:
  160.                 $this->_error('Set Parameter: Unknown option '.$k);
  161.                 return false;
  162.             }
  163.         }
  164.         return true;
  165.     }
  166.  
  167.     /**
  168.     * Decode a given ACE domain name
  169.     * @param    string   Domain name (ACE string)
  170.     *  [@param    string   Desired output encoding, see {@link set_parameter}]
  171.     * @return   string   Decoded Domain name (UTF-8 or UCS-4)
  172.     * @access   public
  173.     */
  174.     function decode($input$one_time_encoding false)
  175.     {
  176.         // Optionally set
  177.         if ($one_time_encoding{
  178.             switch ($one_time_encoding{
  179.             case 'utf8':
  180.             case 'ucs4_string':
  181.             case 'ucs4_array':
  182.                 break;
  183.             default:
  184.                 $this->_error('Unknown encoding '.$one_time_encoding);
  185.                 return false;
  186.             }
  187.         }
  188.         // Make sure to drop any newline characters around
  189.         $input trim($input);
  190.  
  191.         // Negotiate input and try to determine, wether it is a plain string,
  192.         // an email address or something like a complete URL
  193.         if (strpos($input'@')) // Maybe it is an email address
  194.             // No no in strict mode
  195.             if ($this->_strict_mode{
  196.                 $this->_error('Only simple domain name parts can be handled in strict mode');
  197.                 return false;
  198.             }
  199.             list($email_pref$inputexplode('@'$input2);
  200.             $arr explode('.'$input);
  201.             foreach ($arr as $k => $v{
  202.                 $conv $this->_decode($v);
  203.                 if ($conv$arr[$k$conv;
  204.             }
  205.             $return $email_pref '@' join('.'$arr);
  206.         elseif (preg_match('![:\./]!'$input)) // Or a complete domain name (with or without paths / parameters)
  207.             // No no in strict mode
  208.             if ($this->_strict_mode{
  209.                 $this->_error('Only simple domain name parts can be handled in strict mode');
  210.                 return false;
  211.             }
  212.             $parsed parse_url($input);
  213.             if (isset($parsed['host'])) {
  214.                 $arr explode('.'$parsed['host']);
  215.                 foreach ($arr as $k => $v{
  216.                     $conv $this->_decode($v);
  217.                     if ($conv$arr[$k$conv;
  218.                 }
  219.                 $parsed['host'join('.'$arr);
  220.                 $return =
  221.                         (empty($parsed['scheme']'' $parsed['scheme'].(strtolower($parsed['scheme']== 'mailto' ':' '://'))
  222.                         .(empty($parsed['user']'' $parsed['user'].(empty($parsed['pass']'' ':'.$parsed['pass']).'@')
  223.                         .$parsed['host']
  224.                         .(empty($parsed['port']'' ':'.$parsed['port'])
  225.                         .$parsed['path']
  226.                         .(empty($parsed['query']'' '?'.$parsed['query'])
  227.                         .(empty($parsed['fragment']'' '#'.$parsed['fragment']);
  228.             else // parse_url seems to have failed, try without it
  229.                 $arr explode('.'$input);
  230.                 foreach ($arr as $k => $v{
  231.                     $conv $this->_decode($v);
  232.                     if ($conv$arr[$k$conv;
  233.                 }
  234.                 $return join('.'$arr);
  235.             }
  236.         else // Otherwise we consider it being a pure domain name string
  237.             $return $this->_decode($input);
  238.         }
  239.         // The output is UTF-8 by default, other output formats need conversion here
  240.         // If one time encoding is given, use this, else the objects property
  241.         switch (($one_time_encoding$one_time_encoding $this->_api_encoding{
  242.         case 'utf8':
  243.             return $return;
  244.             break;
  245.         case 'ucs4_string':
  246.            return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return));
  247.            break;
  248.         case 'ucs4_array':
  249.             return $this->_utf8_to_ucs4($return);
  250.             break;
  251.         default:
  252.             $this->_error('Unsupported output format');
  253.             return false;
  254.         }
  255.     }
  256.  
  257.     /**
  258.     * Encode a given UTF-8 domain name
  259.     * @param    string   Domain name (UTF-8 or UCS-4)
  260.     *  [@param    string   Desired input encoding, see {@link set_parameter}]
  261.     * @return   string   Encoded Domain name (ACE string)
  262.     * @access   public
  263.     */
  264.     function encode($decoded$one_time_encoding false)
  265.     {
  266.         // Forcing conversion of input to UCS4 array
  267.         // If one time encoding is given, use this, else the objects property
  268.         switch (($one_time_encoding$one_time_encoding $this->_api_encoding{
  269.         case 'utf8':
  270.             $decoded $this->_utf8_to_ucs4($decoded);
  271.             break;
  272.         case 'ucs4_string':
  273.            $decoded $this->_ucs4_string_to_ucs4($decoded);
  274.         case 'ucs4_array':
  275.            break;
  276.         default:
  277.             // $this->_error('Unsupported input format: '.$this->_api_encoding);
  278.             $this->_error('Unsupported input format');
  279.             return false;
  280.         }
  281.  
  282.         // No input, no output, what else did you expect?
  283.         if (empty($decoded)) return '';
  284.  
  285.         // Anchors for iteration
  286.         $last_begin 0;
  287.         // Output string
  288.         $output '';
  289.         foreach ($decoded as $k => $v{
  290.             // Make sure to use just the plain dot
  291.             switch($v{
  292.             case 0x3002:
  293.             case 0xFF0E:
  294.             case 0xFF61:
  295.                 $decoded[$k0x2E;
  296.                 // It's right, no break here
  297.                 // The codepoints above have to be converted to dots anyway
  298.  
  299.             // Stumbling across an anchoring character
  300.             case 0x2E:
  301.             case 0x2F:
  302.             case 0x3A:
  303.             case 0x3F:
  304.             case 0x40:
  305.                 // Neither email addresses nor URLs allowed in strict mode
  306.                 if ($this->_strict_mode{
  307.                    $this->_error('Neither email addresses nor URLs are allowed in strict mode.');
  308.                    return false;
  309.                 else {
  310.                     // Skip first char
  311.                     if ($k{
  312.                         $encoded '';
  313.                         $encoded $this->_encode(array_slice($decoded$last_begin(($k)-$last_begin)));
  314.                         if ($encoded{
  315.                             $output .= $encoded;
  316.                         else {
  317.                             $output .= $this->_ucs4_to_utf8(array_slice($decoded$last_begin(($k)-$last_begin)));
  318.                         }
  319.                         $output .= chr($decoded[$k]);
  320.                     }
  321.                     $last_begin $k 1;
  322.                 }
  323.             }
  324.         }
  325.         // Catch the rest of the string
  326.         if ($last_begin{
  327.             $inp_len sizeof($decoded);
  328.             $encoded '';
  329.             $encoded $this->_encode(array_slice($decoded$last_begin(($inp_len)-$last_begin)));
  330.             if ($encoded{
  331.                 $output .= $encoded;
  332.             else {
  333.                 $output .= $this->_ucs4_to_utf8(array_slice($decoded$last_begin(($inp_len)-$last_begin)));
  334.             }
  335.             return $output;
  336.         else {
  337.             if ($output $this->_encode($decoded)) {
  338.                 return $output;
  339.             else {
  340.                 return $this->_ucs4_to_utf8($decoded);
  341.             }
  342.         }
  343.     }
  344.  
  345.     /**
  346.     * Use this method to get the last error ocurred
  347.     * @param    void 
  348.     * @return   string   The last error, that occured
  349.     * @access   public
  350.     */
  351.     function get_last_error()
  352.     {
  353.         return $this->_error;
  354.     }
  355.  
  356.     /**
  357.     * The actual decoding algorithm
  358.     * @access   private
  359.     */
  360.     function _decode($encoded)
  361.     {
  362.         // We do need to find the Punycode prefix
  363.         if (!preg_match('!^'.preg_quote($this->_punycode_prefix'!').'!'$encoded)) {
  364.             $this->_error('This is not a punycode string');
  365.             return false;
  366.         }
  367.         $encode_test preg_replace('!^'.preg_quote($this->_punycode_prefix'!').'!'''$encoded);
  368.         // If nothing left after removing the prefix, it is hopeless
  369.         if (!$encode_test{
  370.             $this->_error('The given encoded string was empty');
  371.             return false;
  372.         }
  373.         // Find last occurence of the delimiter
  374.         $delim_pos strrpos($encoded'-');
  375.         if ($delim_pos strlen($this->_punycode_prefix)) {
  376.             for ($k strlen($this->_punycode_prefix)$k $delim_pos++$k{
  377.                 $decoded[ord($encoded{$k});
  378.             }
  379.         else {
  380.             $decoded array();
  381.         }
  382.         $deco_len count($decoded);
  383.         $enco_len strlen($encoded);
  384.  
  385.         // Wandering through the strings; init
  386.         $is_first true;
  387.         $bias     $this->_initial_bias;
  388.         $idx      0;
  389.         $char     $this->_initial_n;
  390.  
  391.         for ($enco_idx ($delim_pos($delim_pos 10$enco_idx $enco_len++$deco_len{
  392.             for ($old_idx $idx$w 1$k $this->_base$k += $this->_base{
  393.                 $digit $this->_decode_digit($encoded{$enco_idx++});
  394.                 $idx += $digit $w;
  395.                 $t ($k <= $bias$this->_tmin :
  396.                         (($k >= $bias $this->_tmax$this->_tmax : ($k $bias));
  397.                 if ($digit $tbreak;
  398.                 $w = (int) ($w ($this->_base - $t));
  399.             }
  400.             $bias $this->_adapt($idx $old_idx$deco_len 1$is_first);
  401.             $is_first false;
  402.             $char += (int) ($idx ($deco_len 1));
  403.             $idx %= ($deco_len 1);
  404.             if ($deco_len 0{
  405.                 // Make room for the decoded char
  406.                 for ($i $deco_len$i $idx$i--{
  407.                     $decoded[$i$decoded[($i 1)];
  408.                 }
  409.             }
  410.             $decoded[$idx++$char;
  411.         }
  412.         return $this->_ucs4_to_utf8($decoded);
  413.     }
  414.  
  415.     /**
  416.     * The actual encoding algorithm
  417.     * @access   private
  418.     */
  419.     function _encode($decoded)
  420.     {
  421.         // We cannot encode a domain name containing the Punycode prefix
  422.         $extract strlen($this->_punycode_prefix);
  423.         $check_pref $this->_utf8_to_ucs4($this->_punycode_prefix);
  424.         $check_deco array_slice($decoded0$extract);
  425.  
  426.         if ($check_pref == $check_deco{
  427.             $this->_error('This is already a punycode string');
  428.             return false;
  429.         }
  430.         // We will not try to encode strings consisting of basic code points only
  431.         $encodable false;
  432.         foreach ($decoded as $k => $v{
  433.             if ($v 0x7a{
  434.                 $encodable true;
  435.                 break;
  436.             }
  437.         }
  438.         if (!$encodable{
  439.             $this->_error('The given string does not contain encodable chars');
  440.             return false;
  441.         }
  442.  
  443.         // Do NAMEPREP
  444.         $decoded $this->_nameprep($decoded);
  445.         if (!$decoded || !is_array($decoded)) return false// NAMEPREP failed
  446.  
  447.         $deco_len  count($decoded);
  448.         if (!$deco_lenreturn false// Empty array
  449.  
  450.         $codecount 0// How many chars have been consumed
  451.  
  452.         $encoded '';
  453.         // Copy all basic code points to output
  454.         for ($i 0$i $deco_len++$i{
  455.             $test $decoded[$i];
  456.             // Will match [0-9a-zA-Z-]
  457.             if ((0x2F $test && $test 0x40)
  458.                     || (0x40 $test && $test 0x5B)
  459.                     || (0x60 $test && $test <= 0x7B)
  460.                     || (0x2D == $test)) {
  461.                 $encoded .= chr($decoded[$i]);
  462.                 $codecount++;
  463.             }
  464.         }
  465.         if ($codecount == $deco_lenreturn $encoded// All codepoints were basic ones
  466.  
  467.         // Start with the prefix; copy it to output
  468.         $encoded $this->_punycode_prefix.$encoded;
  469.  
  470.         // If we have basic code points in output, add an hyphen to the end
  471.         if ($codecount$encoded .= '-';
  472.  
  473.         // Now find and encode all non-basic code points
  474.         $is_first  true;
  475.         $cur_code  $this->_initial_n;
  476.         $bias      $this->_initial_bias;
  477.         $delta     0;
  478.         while ($codecount $deco_len{
  479.             // Find the smallest code point >= the current code point and
  480.             // remember the last ouccrence of it in the input
  481.             for ($i 0$next_code $this->_max_ucs$i $deco_len$i++{
  482.                 if ($decoded[$i>= $cur_code && $decoded[$i<= $next_code{
  483.                     $next_code $decoded[$i];
  484.                 }
  485.             }
  486.  
  487.             $delta += ($next_code $cur_code($codecount 1);
  488.             $cur_code $next_code;
  489.  
  490.             // Scan input again and encode all characters whose code point is $cur_code
  491.             for ($i 0$i $deco_len$i++{
  492.                 if ($decoded[$i$cur_code{
  493.                     $delta++;
  494.                 elseif ($decoded[$i== $cur_code{
  495.                     for ($q $delta$k $this->_base1$k += $this->_base{
  496.                         $t ($k <= $bias$this->_tmin :
  497.                                 (($k >= $bias $this->_tmax$this->_tmax : $k $bias);
  498.                         if ($q $tbreak;
  499.                         $encoded .= $this->_encode_digit(ceil($t (($q $t($this->_base - $t))));
  500.                         $q ($q $t($this->_base - $t);
  501.                     }
  502.                     $encoded .= $this->_encode_digit($q);
  503.                     $bias $this->_adapt($delta$codecount+1$is_first);
  504.                     $codecount++;
  505.                     $delta 0;
  506.                     $is_first false;
  507.                 }
  508.             }
  509.             $delta++;
  510.             $cur_code++;
  511.         }
  512.         return $encoded;
  513.     }
  514.  
  515.     /**
  516.     * Adapt the bias according to the current code point and position
  517.     * @access   private
  518.     */
  519.     function _adapt($delta$npoints$is_first)
  520.     {
  521.         $delta = (int) ($is_first ($delta $this->_damp($delta 2));
  522.         $delta += (int) ($delta $npoints);
  523.         for ($k 0$delta (($this->_base - $this->_tmin$this->_tmax2$k += $this->_base{
  524.             $delta = (int) ($delta ($this->_base - $this->_tmin));
  525.         }
  526.         return (int) ($k ($this->_base - $this->_tmin + 1$delta ($delta $this->_skew));
  527.     }
  528.  
  529.     /**
  530.     * Encoding a certain digit
  531.     * @access   private
  532.     */
  533.     function _encode_digit($d)
  534.     {
  535.         return chr($d 22 75 ($d 26));
  536.     }
  537.  
  538.     /**
  539.     * Decode a certain digit
  540.     * @access   private
  541.     */
  542.     function _decode_digit($cp)
  543.     {
  544.         $cp ord($cp);
  545.         return ($cp 48 10$cp 22 (($cp 65 26$cp 65 (($cp 97 26$cp 97 $this->_base));
  546.     }
  547.  
  548.     /**
  549.     * Internal error handling method
  550.     * @access   private
  551.     */
  552.     function _error($error '')
  553.     {
  554.         $this->_error = $error;
  555.     }
  556.  
  557.     /**
  558.     * Do Nameprep according to RFC3491 and RFC3454
  559.     * @param    array    Unicode Characters
  560.     * @return   string   Unicode Characters, Nameprep'd
  561.     * @access   private
  562.     */
  563.     function _nameprep($input)
  564.     {
  565.         $output array();
  566.         $error false;
  567.         //
  568.         // Mapping
  569.         // Walking through the input array, performing the required steps on each of
  570.         // the input chars and putting the result into the output array
  571.         // While mapping required chars we apply the cannonical ordering
  572.  
  573.         // $this->_show_hex($input);
  574.         foreach ($input as $v{
  575.             // Map to nothing == skip that code point
  576.             if (in_array($v$this->_np_['map_nothing'])) continue;
  577.  
  578.             // Try to find prohibited input
  579.             if (in_array($v$this->_np_['prohibit']|| in_array($v$this->_np_['general_prohibited'])) {
  580.                 $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X'$v));
  581.                 return false;
  582.             }
  583.             foreach ($this->_np_['prohibit_ranges'as $range{
  584.                 if ($range[0<= $v && $v <= $range[1]{
  585.                     $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X'$v));
  586.                     return false;
  587.                 }
  588.             }
  589.             //
  590.             // Hangul syllable decomposition
  591.             if (0xAC00 <= $v && $v <= 0xD7AF{
  592.                 foreach ($this->_hangul_decompose($vas $out{
  593.                     $output[$out;
  594.                 }
  595.             // There's a decomposition mapping for that code point
  596.             elseif (isset($this->_np_['replacemaps'][$v])) {
  597.                 foreach ($this->_apply_cannonical_ordering($this->_np_['replacemaps'][$v]as $out{
  598.                     $output[$out;
  599.                 }
  600.             else {
  601.                 $output[$v;
  602.             }
  603.         }
  604.         //
  605.         // Combine code points
  606.         //
  607.         $last_class   0;
  608.         $last_starter 0;
  609.         $out_len      count($output);
  610.         for ($i 0$i $out_len++$i{
  611.             $class $this->_get_combining_class($output[$i]);
  612.             if ((!$last_class || $last_class != $class&& $class{
  613.                 // Try to match
  614.                 $seq_len $i $last_starter;
  615.                 $out $this->_combine(array_slice($output$last_starter$seq_len));
  616.                 // On match: Replace the last starter with the composed character and remove
  617.                 // the now redundant non-starter(s)
  618.                 if ($out{
  619.                     $output[$last_starter$out;
  620.                     if (count($out!= $seq_len{
  621.                         for ($j $i+1$j $out_len++$j{
  622.                             $output[$j-1$output[$j];
  623.                         }
  624.                         unset($output[$out_len]);
  625.                     }
  626.                     // Rewind the for loop by one, since there can be more possible compositions
  627.                     $i--;
  628.                     $out_len--;
  629.                     $last_class ($i == $last_starter$this->_get_combining_class($output[$i-1]);
  630.                     continue;
  631.                 }
  632.             }
  633.             if (!$class// The current class is 0
  634.                 $last_starter $i;
  635.             }
  636.             $last_class $class;
  637.         }
  638.         return $output;
  639.     }
  640.  
  641.     /**
  642.     * Decomposes a Hangul syllable
  643.     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
  644.     * @param    integer  32bit UCS4 code point
  645.     * @return   array    Either Hangul Syllable decomposed or original 32bit value as one value array
  646.     * @access   private
  647.     */
  648.     function _hangul_decompose($char)
  649.     {
  650.         $sindex $char $this->_sbase;
  651.         if ($sindex || $sindex >= $this->_scount{
  652.             return array($char);
  653.         }
  654.         $result array();
  655.         $T $this->_tbase + $sindex $this->_tcount;
  656.         $result[= (int) ($this->_lbase + $sindex $this->_ncount);
  657.         $result[= (int) ($this->_vbase + ($sindex $this->_ncount$this->_tcount);
  658.         if ($T != $this->_tbase$result[$T;
  659.         return $result;
  660.     }
  661.  
  662.     /**
  663.     * Ccomposes a Hangul syllable
  664.     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
  665.     * @param    array    Decomposed UCS4 sequence
  666.     * @return   array    UCS4 sequence with syllables composed
  667.     * @access   private
  668.     */
  669.     function _hangul_compose($input)
  670.     {
  671.         $inp_len count($input);
  672.         if (!$inp_lenreturn array();
  673.         $result array();
  674.         $last $input[0];
  675.         $result[$last// copy first char from input to output
  676.  
  677.         for ($i 1$i $inp_len++$i{
  678.             $char $input[$i];
  679.  
  680.             // Find out, wether two current characters from L and V
  681.             $lindex $last $this->_lbase;
  682.             if (<= $lindex && $lindex $this->_lcount{
  683.                 $vindex $char $this->_vbase;
  684.                 if (<= $vindex && $vindex $this->_vcount{
  685.                     // create syllable of form LV
  686.                     $last ($this->_sbase + ($lindex $this->_vcount + $vindex$this->_tcount);
  687.                     $out_off count($result1;
  688.                     $result[$out_off$last// reset last
  689.                     continue// discard char
  690.                 }
  691.             }
  692.  
  693.             // Find out, wether two current characters are LV and T
  694.             $sindex $last $this->_sbase;
  695.             if (<= $sindex && $sindex $this->_scount && ($sindex $this->_tcount== 0{
  696.                 $tindex $char $this->_tbase;
  697.                 if (<= $tindex && $tindex <= $this->_tcount{
  698.                     // create syllable of form LVT
  699.                     $last += $tindex;
  700.                     $out_off count($result1;
  701.                     $result[$out_off$last// reset last
  702.                     continue// discard char
  703.                 }
  704.             }
  705.             // if neither case was true, just add the character
  706.             $last $char;
  707.             $result[$char;
  708.         }
  709.         return $result;
  710.     }
  711.  
  712.     /**
  713.     * Returns the combining class of a certain wide char
  714.     * @param    integer    Wide char to check (32bit integer)
  715.     * @return   integer    Combining class if found, else 0
  716.     * @access   private
  717.     */
  718.     function _get_combining_class($char)
  719.     {
  720.         return isset($this->_np_['norm_combcls'][$char]$this->_np_['norm_combcls'][$char0;
  721.     }
  722.  
  723.     /**
  724.     * Apllies the cannonical ordering of a decomposed UCS4 sequence
  725.     * @param    array      Decomposed UCS4 sequence
  726.     * @return   array      Ordered USC4 sequence
  727.     * @access   private
  728.     */
  729.     function _apply_cannonical_ordering($input)
  730.     {
  731.         $swap true;
  732.         $size count($input);
  733.         while ($swap{
  734.             $swap false;
  735.             $last $this->_get_combining_class($input[0]);
  736.             for ($i 0$i $size 1++$i{
  737.                 $next $this->_get_combining_class($input[$i+1]);
  738.                 if ($next != && $last $next{
  739.                     // Move item leftward until it fits
  740.                     for ($j $i 1$j 0--$j{
  741.                         if ($this->_get_combining_class($input[$j 1]<= $nextbreak;
  742.                         $t $input[$j];
  743.                         $input[$j$input[$j 1];
  744.                         $input[$j 1$t;
  745.                         $swap 1;
  746.                     }
  747.                     // Reentering the loop looking at the old character again
  748.                     $next $last;
  749.                 }
  750.                 $last $next;
  751.             }
  752.         }
  753.         return $input;
  754.     }
  755.  
  756.     /**
  757.     * Do composition of a sequence of starter and non-starter
  758.     * @param    array      UCS4 Decomposed sequence
  759.     * @return   array      Ordered USC4 sequence
  760.     * @access   private
  761.     */
  762.     function _combine($input)
  763.     {
  764.         $inp_len count($input);
  765.         // Is it a Hangul syllable?
  766.         if (!= $inp_len{
  767.             $hangul $this->_hangul_compose($input);
  768.             if (count($hangul!= $inp_lenreturn $hangul// This place is probably wrong
  769.         }
  770.         foreach ($this->_np_['replacemaps'as $np_src => $np_target{
  771.             if ($np_target[0!= $input[0]continue;
  772.             if (count($np_target!= $inp_lencontinue;
  773.             $hit false;
  774.             foreach ($input as $k2 => $v2{
  775.                 if ($v2 == $np_target[$k2]{
  776.                     $hit true;
  777.                 else {
  778.                     $hit false;
  779.                     break;
  780.                 }
  781.             }
  782.             if ($hitreturn $np_src;
  783.         }
  784.         return false;
  785.     }
  786.  
  787.     /**
  788.     * This converts an UTF-8 encoded string to its UCS-4 representation
  789.     * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing
  790.     * each of the "chars". This is due to PHP not being able to handle strings with
  791.     * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too.
  792.     * The following UTF-8 encodings are supported:
  793.     * bytes bits  representation
  794.     * 1        7  0xxxxxxx
  795.     * 2       11  110xxxxx 10xxxxxx
  796.     * 3       16  1110xxxx 10xxxxxx 10xxxxxx
  797.     * 4       21  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  798.     * 5       26  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  799.     * 6       31  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  800.     * Each x represents a bit that can be used to store character data.
  801.     * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000
  802.     * @access   private
  803.     */
  804.     function _utf8_to_ucs4($input)
  805.     {
  806.         $output array();
  807.         $out_len 0;
  808.         $inp_len strlen($input);
  809.         $mode 'next';
  810.         $test 'none';
  811.         for ($k 0$k $inp_len++$k{
  812.             $v ord($input{$k})// Extract byte from input string
  813.  
  814.             if ($v 128// We found an ASCII char - put into stirng as is
  815.                 $output[$out_len$v;
  816.                 ++$out_len;
  817.                 if ('add' == $mode{
  818.                     $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
  819.                     return false;
  820.                 }
  821.                 continue;
  822.             }
  823.             if ('next' == $mode// Try to find the next start byte; determine the width of the Unicode char
  824.                 $start_byte $v;
  825.                 $mode 'add';
  826.                 $test 'range';
  827.                 if ($v >> == 6// &110xxxxx 10xxxxx
  828.                     $next_byte 0// Tells, how many times subsequent bitmasks must rotate 6bits to the left
  829.                     $v ($v 192<< 6;
  830.                 elseif ($v >> == 14// &1110xxxx 10xxxxxx 10xxxxxx
  831.                     $next_byte 1;
  832.                     $v ($v 224<< 12;
  833.                 elseif ($v >> == 30// &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  834.                     $next_byte 2;
  835.                     $v ($v 240<< 18;
  836.                 elseif ($v >> == 62// &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  837.                     $next_byte 3;
  838.                     $v ($v 248<< 24;
  839.                 elseif ($v >> == 126// &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  840.                     $next_byte 4;
  841.                     $v ($v 252<< 30;
  842.                 else {
  843.                     $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k);
  844.                     return false;
  845.                 }
  846.                 if ('add' == $mode{
  847.                     $output[$out_len= (int) $v;
  848.                     ++$out_len;
  849.                     continue;
  850.                 }
  851.             }
  852.             if ('add' == $mode{
  853.                 if (!$this->_allow_overlong && $test == 'range'{
  854.                     $test 'none';
  855.                     if (($v 0xA0 && $start_byte == 0xE0|| ($v 0x90 && $start_byte == 0xF0|| ($v 0x8F && $start_byte == 0xF4)) {
  856.                         $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k);
  857.                         return false;
  858.                     }
  859.                 }
  860.                 if ($v >> == 2// Bit mask must be 10xxxxxx
  861.                     $v ($v 128<< ($next_byte 6);
  862.                     $output[($out_len 1)+= $v;
  863.                     --$next_byte;
  864.                 else {
  865.                     $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
  866.                     return false;
  867.                 }
  868.                 if ($next_byte 0{
  869.                     $mode 'next';
  870.                 }
  871.             }
  872.         // for
  873.         return $output;
  874.     }
  875.  
  876.     /**
  877.     * Convert UCS-4 string into UTF-8 string
  878.     * See _utf8_to_ucs4() for details
  879.     * @access   private
  880.     */
  881.     function _ucs4_to_utf8($input)
  882.     {
  883.         $output '';
  884.         foreach ($input as $v{
  885.             // $v = ord($v);
  886.             if ($v 128// 7bit are transferred literally
  887.                 $output .= chr($v);
  888.             elseif ($v (<< 11)) // 2 bytes
  889.                 $output .= chr(192 ($v >> 6)) chr(128 ($v 63));
  890.             elseif ($v (<< 16)) // 3 bytes
  891.                 $output .= chr(224 ($v >> 12)) chr(128 (($v >> 663)) chr(128 ($v 63));
  892.             elseif ($v (<< 21)) // 4 bytes
  893.                 $output .= chr(240 ($v >> 18)) chr(128 (($v >> 1263))
  894.                          . chr(128 (($v >> 663)) chr(128 ($v 63));
  895.             elseif ($v (<< 26)) // 5 bytes
  896.                 $output .= chr(248 ($v >> 24)) chr(128 (($v >> 1863))
  897.                          . chr(128 (($v >> 1263)) chr(128 (($v >> 663))
  898.                          . chr(128 ($v 63));
  899.             elseif ($v (<< 31)) // 6 bytes
  900.                 $output .= chr(252 ($v >> 30)) chr(128 (($v >> 2463))
  901.                          . chr(128 (($v >> 1863)) chr(128 (($v >> 1263))
  902.                          . chr(128 (($v >> 663)) chr(128 ($v 63));
  903.             else {
  904.                 $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k);
  905.                 return false;
  906.             }
  907.         }
  908.         return $output;
  909.     }
  910.  
  911.     /**
  912.      * Convert UCS-4 array into UCS-4 string
  913.      *
  914.      * @access   private
  915.      */
  916.     function _ucs4_to_ucs4_string($input)
  917.     {
  918.         $output '';
  919.         // Take array values and split output to 4 bytes per value
  920.         // The bit mask is 255, which reads &11111111
  921.         foreach ($input as $v{
  922.             $output .= chr(($v >> 24255)
  923.                      . chr(($v >> 16255)
  924.                      . chr(($v >> 8255)
  925.                      . chr($v 255);
  926.         }
  927.         return $output;
  928.     }
  929.  
  930.     /**
  931.      * Convert UCS-4 strin into UCS-4 garray
  932.      *
  933.      * @access   private
  934.      */
  935.     function _ucs4_string_to_ucs4($input)
  936.     {
  937.         $output array();
  938.  
  939.         $inp_len strlen($input);
  940.         // Input length must be dividable by 4
  941.         if ($inp_len 4{
  942.             $this->_error('Input UCS4 string is broken');
  943.             return false;
  944.         }
  945.  
  946.         // Empty input - return empty output
  947.         if (!$inp_lenreturn $output;
  948.  
  949.         for ($i 0$out_len = -1$i $inp_len++$i{
  950.             // Increment output position every 4 input bytes
  951.             if (!($i 4)) {
  952.                 $out_len++;
  953.                 $output[$out_len0;
  954.             }
  955.             $output[$out_len+= ord($input{$i}<< ((($i 4) ) );
  956.         }
  957.         return $output;
  958.     }
  959. }
  960.  
  961. /**
  962. * Adapter class for aligning the API of idna_convert with that of
  963. * Net_IDNA
  964. @author  Matthias Sommerfeld <[email protected]>
  965. */
  966. class Net_IDNA_php4 extends idna_convert
  967. {
  968.     /**
  969.     * Sets a new option value. Available options and values:
  970.     * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
  971.     *         'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
  972.     * [overlong - Unicode does not allow unnecessarily long encodings of chars,
  973.     *             to allow this, set this parameter to true, else to false;
  974.     *             default is false.]
  975.     * [strict - true: strict mode, good for registration purposes - Causes errors
  976.     *           on failures; false: loose mode, ideal for "wildlife" applications
  977.     *           by silently ignoring errors and returning the original input instead
  978.     *
  979.     * @param    mixed     Parameter to set (string: single parameter; array of Parameter => Value pairs)
  980.     * @param    string    Value to use (if parameter 1 is a string)
  981.     * @return   boolean   true on success, false otherwise
  982.     * @access   public
  983.     */
  984.     function setParams($option$param false)
  985.     {
  986.         return $this->IC->set_parameters($option$param);
  987.     }
  988. }
  989.  
  990. ?>

Documentation generated on Mon, 05 Mar 2007 21:07:30 +0000 by phpDocumentor 1.3.1