Ignore:
Timestamp:
2013/08/26 15:52:37 (11 years ago)
Author:
m_uehara
Message:

#2348 r23116 - r23125 をマージ

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/version-2_13_0/data/module/Services/JSON.php

    r20116 r23126  
    11<?php 
    22/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 
    3  
    43/** 
    54 * Converts to and from JSON format. 
     
    9392 
    9493/** 
     94 * Behavior switch for Services_JSON::decode() 
     95 */ 
     96define('SERVICES_JSON_USE_TO_JSON', 64); 
     97 
     98/** 
    9599 * Converts to and from JSON format. 
    96100 * 
     
    130134    *                                   bubble up with an error, so all return values 
    131135    *                                   from encode() should be checked with isError() 
     136    *                           - SERVICES_JSON_USE_TO_JSON:  call toJSON when serializing objects 
     137    *                                   It serializes the return value from the toJSON call rather  
     138    *                                   than the object it'self,  toJSON can return associative arrays,  
     139    *                                   strings or numbers, if you return an object, make sure it does 
     140    *                                   not have a toJSON method, otherwise an error will occur. 
    132141    */ 
    133142    function Services_JSON($use = 0) 
    134143    { 
    135144        $this->use = $use; 
    136     } 
    137  
     145        $this->_mb_strlen            = function_exists('mb_strlen'); 
     146        $this->_mb_convert_encoding  = function_exists('mb_convert_encoding'); 
     147        $this->_mb_substr            = function_exists('mb_substr'); 
     148    } 
     149    // private - cache the mbstring lookup results.. 
     150    var $_mb_strlen = false; 
     151    var $_mb_substr = false; 
     152    var $_mb_convert_encoding = false; 
     153     
    138154   /** 
    139155    * convert a string from one UTF-16 char to one UTF-8 char 
     
    150166    { 
    151167        // oh please oh please oh please oh please oh please 
    152         if(function_exists('mb_convert_encoding')) { 
     168        if($this->_mb_convert_encoding) { 
    153169            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); 
    154170        } 
     
    194210    { 
    195211        // oh please oh please oh please oh please oh please 
    196         if(function_exists('mb_convert_encoding')) { 
     212        if($this->_mb_convert_encoding) { 
    197213            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 
    198214        } 
    199215 
    200         switch(strlen($utf8)) { 
     216        switch($this->strlen8($utf8)) { 
    201217            case 1: 
    202218                // this case should never be reached, because we are in ASCII range 
     
    225241 
    226242   /** 
    227     * encodes an arbitrary variable into JSON format 
     243    * encodes an arbitrary variable into JSON format (and sends JSON Header) 
    228244    * 
    229245    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded. 
     
    237253    function encode($var) 
    238254    { 
     255        header('Content-type: application/json'); 
     256        return $this->encodeUnsafe($var); 
     257    } 
     258    /** 
     259    * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!) 
     260    * 
     261    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded. 
     262    *                           see argument 1 to Services_JSON() above for array-parsing behavior. 
     263    *                           if var is a strng, note that encode() always expects it 
     264    *                           to be in ASCII or UTF-8 format! 
     265    * 
     266    * @return   mixed   JSON string representation of input var or an error if a problem occurs 
     267    * @access   public 
     268    */ 
     269    function encodeUnsafe($var) 
     270    { 
     271        // see bug #16908 - regarding numeric locale printing 
     272        $lc = setlocale(LC_NUMERIC, 0); 
     273        setlocale(LC_NUMERIC, 'C'); 
     274        $ret = $this->_encode($var); 
     275        setlocale(LC_NUMERIC, $lc); 
     276        return $ret; 
     277         
     278    } 
     279    /** 
     280    * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format  
     281    * 
     282    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded. 
     283    *                           see argument 1 to Services_JSON() above for array-parsing behavior. 
     284    *                           if var is a strng, note that encode() always expects it 
     285    *                           to be in ASCII or UTF-8 format! 
     286    * 
     287    * @return   mixed   JSON string representation of input var or an error if a problem occurs 
     288    * @access   public 
     289    */ 
     290    function _encode($var)  
     291    { 
     292          
    239293        switch (gettype($var)) { 
    240294            case 'boolean': 
     
    249303            case 'double': 
    250304            case 'float': 
    251                 return (float) $var; 
     305                return  (float) $var; 
    252306 
    253307            case 'string': 
    254308                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 
    255309                $ascii = ''; 
    256                 $strlen_var = strlen($var); 
     310                $strlen_var = $this->strlen8($var); 
    257311 
    258312               /* 
     
    296350                            // characters U-00000080 - U-000007FF, mask 110XXXXX 
    297351                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
     352                            if ($c+1 >= $strlen_var) { 
     353                                $c += 1; 
     354                                $ascii .= '?'; 
     355                                break; 
     356                            } 
     357                             
    298358                            $char = pack('C*', $ord_var_c, ord($var{$c + 1})); 
    299359                            $c += 1; 
     
    303363 
    304364                        case (($ord_var_c & 0xF0) == 0xE0): 
     365                            if ($c+2 >= $strlen_var) { 
     366                                $c += 2; 
     367                                $ascii .= '?'; 
     368                                break; 
     369                            } 
    305370                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX 
    306371                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
    307372                            $char = pack('C*', $ord_var_c, 
    308                                          ord($var{$c + 1}), 
    309                                          ord($var{$c + 2})); 
     373                                         @ord($var{$c + 1}), 
     374                                         @ord($var{$c + 2})); 
    310375                            $c += 2; 
    311376                            $utf16 = $this->utf82utf16($char); 
     
    314379 
    315380                        case (($ord_var_c & 0xF8) == 0xF0): 
     381                            if ($c+3 >= $strlen_var) { 
     382                                $c += 3; 
     383                                $ascii .= '?'; 
     384                                break; 
     385                            } 
    316386                            // characters U-00010000 - U-001FFFFF, mask 11110XXX 
    317387                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
     
    328398                            // characters U-00200000 - U-03FFFFFF, mask 111110XX 
    329399                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
     400                            if ($c+4 >= $strlen_var) { 
     401                                $c += 4; 
     402                                $ascii .= '?'; 
     403                                break; 
     404                            } 
    330405                            $char = pack('C*', $ord_var_c, 
    331406                                         ord($var{$c + 1}), 
     
    339414 
    340415                        case (($ord_var_c & 0xFE) == 0xFC): 
     416                        if ($c+5 >= $strlen_var) { 
     417                                $c += 5; 
     418                                $ascii .= '?'; 
     419                                break; 
     420                            } 
    341421                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X 
    342422                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
     
    353433                    } 
    354434                } 
    355  
    356                 return '"'.$ascii.'"'; 
     435                return  '"'.$ascii.'"'; 
    357436 
    358437            case 'array': 
     
    391470 
    392471                // treat it like a regular array 
    393                 $elements = array_map(array($this, 'encode'), $var); 
     472                $elements = array_map(array($this, '_encode'), $var); 
    394473 
    395474                foreach($elements as $element) { 
     
    402481 
    403482            case 'object': 
     483             
     484                // support toJSON methods. 
     485                if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) { 
     486                    // this may end up allowing unlimited recursion 
     487                    // so we check the return value to make sure it's not got the same method. 
     488                    $recode = $var->toJSON(); 
     489                     
     490                    if (method_exists($recode, 'toJSON')) { 
     491                         
     492                        return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) 
     493                        ? 'null' 
     494                        : new Services_JSON_Error(class_name($var). 
     495                            " toJSON returned an object with a toJSON method."); 
     496                             
     497                    } 
     498                     
     499                    return $this->_encode( $recode ); 
     500                }  
     501                 
    404502                $vars = get_object_vars($var); 
    405  
     503                 
    406504                $properties = array_map(array($this, 'name_value'), 
    407505                                        array_keys($vars), 
     
    434532    function name_value($name, $value) 
    435533    { 
    436         $encoded_value = $this->encode($value); 
     534        $encoded_value = $this->_encode($value); 
    437535 
    438536        if(Services_JSON::isError($encoded_value)) { 
     
    440538        } 
    441539 
    442         return $this->encode(strval($name)) . ':' . $encoded_value; 
     540        return $this->_encode(strval($name)) . ':' . $encoded_value; 
    443541    } 
    444542 
     
    513611                } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { 
    514612                    // STRINGS RETURNED IN UTF-8 FORMAT 
    515                     $delim = substr($str, 0, 1); 
    516                     $chrs = substr($str, 1, -1); 
     613                    $delim = $this->substr8($str, 0, 1); 
     614                    $chrs = $this->substr8($str, 1, -1); 
    517615                    $utf8 = ''; 
    518                     $strlen_chrs = strlen($chrs); 
     616                    $strlen_chrs = $this->strlen8($chrs); 
    519617 
    520618                    for ($c = 0; $c < $strlen_chrs; ++$c) { 
    521619 
    522                         $substr_chrs_c_2 = substr($chrs, $c, 2); 
     620                        $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); 
    523621                        $ord_chrs_c = ord($chrs{$c}); 
    524622 
     
    555653                                break; 
    556654 
    557                             case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): 
     655                            case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)): 
    558656                                // single, escaped unicode character 
    559                                 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) 
    560                                        . chr(hexdec(substr($chrs, ($c + 4), 2))); 
     657                                $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2))) 
     658                                       . chr(hexdec($this->substr8($chrs, ($c + 4), 2))); 
    561659                                $utf8 .= $this->utf162utf8($utf16); 
    562660                                $c += 5; 
     
    570668                                // characters U-00000080 - U-000007FF, mask 110XXXXX 
    571669                                //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
    572                                 $utf8 .= substr($chrs, $c, 2); 
     670                                $utf8 .= $this->substr8($chrs, $c, 2); 
    573671                                ++$c; 
    574672                                break; 
     
    577675                                // characters U-00000800 - U-0000FFFF, mask 1110XXXX 
    578676                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
    579                                 $utf8 .= substr($chrs, $c, 3); 
     677                                $utf8 .= $this->substr8($chrs, $c, 3); 
    580678                                $c += 2; 
    581679                                break; 
     
    584682                                // characters U-00010000 - U-001FFFFF, mask 11110XXX 
    585683                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
    586                                 $utf8 .= substr($chrs, $c, 4); 
     684                                $utf8 .= $this->substr8($chrs, $c, 4); 
    587685                                $c += 3; 
    588686                                break; 
     
    591689                                // characters U-00200000 - U-03FFFFFF, mask 111110XX 
    592690                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
    593                                 $utf8 .= substr($chrs, $c, 5); 
     691                                $utf8 .= $this->substr8($chrs, $c, 5); 
    594692                                $c += 4; 
    595693                                break; 
     
    598696                                // characters U-04000000 - U-7FFFFFFF, mask 1111110X 
    599697                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
    600                                 $utf8 .= substr($chrs, $c, 6); 
     698                                $utf8 .= $this->substr8($chrs, $c, 6); 
    601699                                $c += 5; 
    602700                                break; 
     
    628726                                           'delim' => false)); 
    629727 
    630                     $chrs = substr($str, 1, -1); 
     728                    $chrs = $this->substr8($str, 1, -1); 
    631729                    $chrs = $this->reduce_string($chrs); 
    632730 
     
    643741                    //print("\nparsing {$chrs}\n"); 
    644742 
    645                     $strlen_chrs = strlen($chrs); 
     743                    $strlen_chrs = $this->strlen8($chrs); 
    646744 
    647745                    for ($c = 0; $c <= $strlen_chrs; ++$c) { 
    648746 
    649747                        $top = end($stk); 
    650                         $substr_chrs_c_2 = substr($chrs, $c, 2); 
     748                        $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); 
    651749 
    652750                        if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { 
    653751                            // found a comma that is not inside a string, array, etc., 
    654752                            // OR we've reached the end of the character list 
    655                             $slice = substr($chrs, $top['where'], ($c - $top['where'])); 
     753                            $slice = $this->substr8($chrs, $top['where'], ($c - $top['where'])); 
    656754                            array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); 
    657                             //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
     755                            //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
    658756 
    659757                            if (reset($stk) == SERVICES_JSON_IN_ARR) { 
     
    668766                                $parts = array(); 
    669767                                 
    670                                 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 
    671                                     // "name":value pair 
     768                               if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) { 
     769                                  // "name":value pair 
    672770                                    $key = $this->decode($parts[1]); 
    673                                     $val = $this->decode($parts[2]); 
    674  
     771                                    $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); 
    675772                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 
    676773                                        $obj[$key] = $val; 
     
    678775                                        $obj->$key = $val; 
    679776                                    } 
    680                                 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 
     777                                } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) { 
    681778                                    // name:value pair, where name is unquoted 
    682779                                    $key = $parts[1]; 
    683                                     $val = $this->decode($parts[2]); 
     780                                    $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); 
    684781 
    685782                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 
     
    699796                        } elseif (($chrs{$c} == $top['delim']) && 
    700797                                 ($top['what'] == SERVICES_JSON_IN_STR) && 
    701                                  ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { 
     798                                 (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) { 
    702799                            // found a quote, we're in a string, and it's not escaped 
    703800                            // we know that it's not escaped becase there is _not_ an 
    704801                            // odd number of backslashes at the end of the string so far 
    705802                            array_pop($stk); 
    706                             //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 
     803                            //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 
    707804 
    708805                        } elseif (($chrs{$c} == '[') && 
     
    715812                            // found a right-bracket, and we're in an array 
    716813                            array_pop($stk); 
    717                             //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
     814                            //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
    718815 
    719816                        } elseif (($chrs{$c} == '{') && 
     
    726823                            // found a right-brace, and we're in an object 
    727824                            array_pop($stk); 
    728                             //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
     825                            //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
    729826 
    730827                        } elseif (($substr_chrs_c_2 == '/*') && 
     
    743840                                $chrs = substr_replace($chrs, ' ', $i, 1); 
    744841 
    745                             //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
     842                            //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 
    746843 
    747844                        } 
     
    775872        return false; 
    776873    } 
     874     
     875    /** 
     876    * Calculates length of string in bytes 
     877    * @param string  
     878    * @return integer length 
     879    */ 
     880    function strlen8( $str )  
     881    { 
     882        if ( $this->_mb_strlen ) { 
     883            return mb_strlen( $str, "8bit" ); 
     884        } 
     885        return strlen( $str ); 
     886    } 
     887     
     888    /** 
     889    * Returns part of a string, interpreting $start and $length as number of bytes. 
     890    * @param string  
     891    * @param integer start  
     892    * @param integer length  
     893    * @return integer length 
     894    */ 
     895    function substr8( $string, $start, $length=false )  
     896    { 
     897        if ( $length === false ) { 
     898            $length = $this->strlen8( $string ) - $start; 
     899        } 
     900        if ( $this->_mb_substr ) { 
     901            return mb_substr( $string, $start, $length, "8bit" ); 
     902        } 
     903        return substr( $string, $start, $length ); 
     904    } 
     905 
    777906} 
    778907 
     
    801930        } 
    802931    } 
    803  
     932     
    804933} 
    805      
    806 ?> 
Note: See TracChangeset for help on using the changeset viewer.