<?php
/**
 * json decoder & encoder
 *
 * wrapper for json_encode/decode functions.
 * have a json parser for non-standart js
 *
 * @version:    0.1
 * @author:    alex 'alex_ez' yaroshevich <qfox@ya.ru>
 * @date:    20 oct 2008
 * @link:    http://src.qfox.ru/
 * @dependence:    PHP5.2+
 * @todo:    optimization, change parsing algorithms
 *
 */

class json {
static function 
decode$s )
{
    
$_r json_decode$s);
    if( !
is_null$_r ) ) return $_r;
    
    
$s trim$s );
    if( 
$_r self::parseScalar$s ) ) return $_r;
    
    
$ch mb_substr$s0);
    switch( 
$ch ) {
    case 
'[':
        
$_sub mb_substr$s);
        
$_end self::findEnd$_sub );
        
$_sub mb_substr$_sub1$_end-);
        
$_r self::parseArray$_sub );
        break;
    case 
'{':
        
$_sub mb_substr$s);
        
$_end self::findEnd$_sub );
        
$_sub mb_substr$_sub1$_end-);
        
$_r self::parseObjecttrim$_sub ) );
        break;
    default:
        
$_r "?: ".$s;
    }
// end of parser
    
    
return $_r;
}

static function 
parseScalar$s )
{
    
$s trim$s );
    if( 
$s === '' || $s === 'null' ) return "null";
    if( 
is_numeric$s ) ) return (( strpos$s'.' ) ) !== false floatval$s ) : intval$s ));
    if( 
$r=self::parseString$s ) ) return $r;
    return 
null;
}

static function 
parseString$s )
{
    
$s trim$s );
    if( 
preg_match('@(^"[^"]+"$)|(^\'[^\']+\'$)@',$s) ) return stripslashes(trim($s,"\"\'"));
    if( 
preg_match('@(^[a-z_][a-z0-9_]*$)@',$s) ) return $s;
    return 
null;
}

static function 
parseKey$s )
{
    
$s trim$s );
    if( 
$r=self::parseString$s ) ) return $r;
    if( 
preg_match('@(^[a-z0-9_]*$)@',$s) ) return $s;
    return 
null;
}



static function 
parseObject$s )
{
    
$_r = array();
    
//echo "! ".addcslashes($s,"\r\n\t")."\n";
    
$s trim$s );
    for( 
$l=mb_strlen($s),$i=0$i<$l$i++ )
    {
        
// skip delimiter and spaces
        
$ch mb_substr$s$i);
        if( 
$ch == ',' || trim$ch ) === '' ) continue;
        
        
$_sub mb_substr$s$i );
        
$_end self::findEnd$_sub );
        
$_k self::parseKeymb_substr$s$i$_end ) );
        
//echo ": $i; '".addcslashes(mb_substr($_sub, 0, $_end),"\r\n\t")."|".addcslashes(mb_substr($_sub,$_end),"\r\n\t")."'\n";
        
$i += $_end;
        
        while( 
mb_substr$s$i-1) != ':' && $i<$l $i++;
        
        
$_sub mb_substr$s$i );
        
//echo "> '".addcslashes(mb_substr( $s, $i ),"\r\n\t")."'\n";
        
$_end self::findEnd$_sub );
        
$_v mb_substr$s$i$_end );
        
$i += $_end;
        
        
$_r[$_k] = json::decode$_v );
        
//echo "\ti:$i;\te:$_end;  k: ".addcslashes($_k,"\r\n\t").", \tv: ".addcslashes($_v,"\r\n\t")."; sub: <".addcslashes($_sub,"\r\n\t").">;\n\ts: <".addcslashes(mb_substr( $s, 0, $i )."^".mb_substr( $s, $i ),"\n\r\t").">\n";
    
}
    
//print_r( $_r );
    
return $_r;
}

static function 
parseArray$s )
{
    
$_r = array();
    
//echo "! ".addcslashes($s,"\r\n\t")."\n";
    
for( $l=mb_strlen($s),$i=0$i<$l$i++ )
    {
        
// skip delimiter and spaces
        
$ch mb_substr$s$i);
        if( 
$ch == ',' || trim$ch ) === '' ) continue;
        
        
$_sub mb_substr$s$i );
        
$_end self::findEnd$_sub );
        
//$_kend = mb_strpos( $s, ',', $i+$_end );
        //echo "\t$_end -\t$_send\n";
        //$_end = self::findEnd( $_sub, false, ',' );
        //self::decode( );
        
$_v mb_substr$s$i$_end );
        
        
$_r[] = json::decodetrim($_v) );
        
//echo "i:$i;\te:$_end; \tv: ".addcslashes($_v,"\r\n\t")."; sub: <".addcslashes($_sub,"\r\n\t").">"."\n\t<".addcslashes(mb_substr( $s, 0, $i )."|".mb_substr( $s, $i ),"\n\r\t").">\n";
        
        
$i += $_end;
        
        
//if( $i > 15 ) die;
    
}
    
//print_r( $_r );
    
return $_r;
}

/**
 * find end of block
 * 
 * string $s    our string
 * bool   $sqm    skip quote blocks mode
 * string $sc    stop character
 * 
 */
static function findEnd$s$sqm false$sc null )
{
    
$_s $s$s=ltrim$s );
    
$left mb_strlen$_s ) - mb_strlen$s );
    
$fc mb_substr$s0);
    
//echo $s;
    
    
if( !mb_strlen$s ) ) return $left;
    
    
// fetch closing character, if need
    
$_fc2sc = array('"'=>'"',"'"=>"'",'{'=>'}','['=>']','('=>')','<'=>'>');
    if( 
is_null$sc ) )
    {
        
$sc $_fc2sc$fc ]; // stop character
        
if( isQuote($sc) ) { $sqm true; }
        if( !
$sc $sc ",:";
    }
    
    
$parenthesis = ( isset( $_fc2sc$sc ] ) || in_array$sc$_fc2sc ) );
    
//echo "\nfc: $fc, sc: $sc, sqm: $sqm, parenthesis: $parenthesis\n\n";
    
    // parenthesis validation.
    
$sq isQuote$fc ) ? $fc false;
    
$cc $fc;
    
    for( 
$l=mb_strlen($s),$j=1,$p=1$p&&$j<$l$j++ )
    {
        
$pc=$cc$cc=mb_substr($s,$j,1);
        
//$pp=$p;
        
        
if( !$sqm && isQuote($cc) )
        {
            
$_nos false// no open backslash
            
if( $pc !== "\\" $_nos true;
            if( !
$_nos ) { for( $_j=$j$_j>1$_j-- ) if( mb_substr($s,$_j-1,1) != "\\" ) break; $_nos=($j-$_j+1)%2; }
            if( 
$_nos && $sq===false ) { $sq=$cc; } elseif( $_nos && $sq==$cc ) { $sq=false; }
        }
        elseif( 
$sqm )
        {
            
$_nos false// no open backslash
            
if( $pc !== "\\" $_nos true;
            if( !
$_nos ) { for( $_j=$j$_j>1$_j-- ) if( mb_substr($s,$_j-1,1) != "\\" ) break; $_nos=($j-$_j+1)%2; }
            if( !
$_nos ) continue;
        }
        
        if( !
$parenthesis && $sq === false && strpos$sc$cc ) !== false )
            
$p 0;
        elseif( !
$sqm && $sq === false )
            
$p += ($cc===$sc) ? (-1) : ( ($cc===$fc) ? );
        elseif( 
$sqm )
            
$p -= ($cc===$sc);
        
        
//echo "\ti: ".($sq?'-':'')."$j\tpp: $pp, p: $p, pc: $pc, cc: $cc,\tsq: $sq, cc=': ".isQuote($cc).", pc!=\\: ".($pc != "\\")."\n";
    
}
    
    if( !
$parenthesis && strpos$sc$cc ) !== false $j--;
    
    
// parenthesis parse error. errors with unclosed parenthesis
    
if( $parenthesis && $p ) die( "unclosed parenthesis ".$fc."; s: ".$s."; pos: ".$j."\n" );
    
    return 
$j+$left;
}

static function 
encode$a )
{
    return 
json_encode$a );
}
}

    function 
isDQuote$c ) { return $c=="'"; }
    function 
isSQuote$c ) { return $c=='"'; }
    function 
isQuote$c ) { return isDQuote($c) || isSQuote($c); }

?>