<?php     
/** 
 * @author Martin Scotta <[email protected]> 
 */ 
  
/** 
 * PHP5.3 version to 5.2.x translator 
 * 
 * It attemps to the its best to translate classes names like 
 * <code>\php53\Class</code> into <code>php53_Class</code> 
 *  
 * Usage: <code> 
        $vt = VersionTranslater::getTranslator( '/path/to/Class.php' ); 
        $vt->translateTo( '/other/path/Class.php' ); 
 *</code> 
 */ 
abstract class VersionTranslater { 
 
    /** 
     * Return the correct Translator for a given <code>$path</code> 
     *  
     * @param string $path 
     * @return VersionTranslater <code>null</code> if $path is neither a file nor a directory 
     */ 
    static function getTranslator($path) { 
        if( is_dir( $path )) { 
            return new DirectoryTranslator( $path ); 
        } 
         
        if( is_file( $path )) { 
            return new FileTranslator( $path ); 
        } 
    } 
     
    /** 
     * Translates the <code>$path</code> into the OS format 
     * @param string $path 
     * @return string 
     */ 
    static function fixPath($path){ 
        return strtr( $path, array( '\\' => DIRECTORY_SEPARATOR, '/' => DIRECTORY_SEPARATOR)); 
    } 
     
    /** 
     * @var string 
     */ 
    private $path; 
     
    /** 
     * @param string $path a filesystem path to the resource to be translated 
     */ 
    function __construct($path) { 
        $path = self::fixPath($path); 
        $this->path = new SplFileInfo($path); 
    } 
     
    /** 
     * @return string 
     */ 
    function getPath() { 
        return $this->path; 
    } 
     
    /** 
     * Runs the translation process and outputs the result in the <code>$outputPath</code> 
     * 
     * @param string $outputPath 
     * @throws RuntimeException on parser error 
     */ 
    abstract function translateTo($outputPath); 
} 
 
/** 
 * a directory translator 
 */ 
class DirectoryTranslator extends FileTranslator { 
     
    /** 
     * Perform a full directory translation directory and outputs the results on <code>$outputDir</code> 
     * It skips everything that starts with '.' 
     * 
     * @param string $outputDir 
     * @throw RuntimeException 
     */ 
    function translateTo($outputDir) { 
        $outputPath = self::fixPath($outputDir); 
         
        if( !is_dir( $outputDir )) { 
            mkdir( $outputDir ); 
        } 
         
        $directoryIterator = new RecursiveDirectoryIterator( $this->getPath(), RecursiveIteratorIterator::CHILD_FIRST); 
         
        foreach($directoryIterator as $file) { 
            $fileName = $file->getFileName(); 
             
            if( '.' === $fileName[0]) { 
                continue; 
            } 
             
            $translator = VersionTranslater::getTranslator( $file->getPathName() ); 
            $translator->translateTo( $outputDir . DIRECTORY_SEPARATOR . $fileName); 
        } 
         
    } 
} 
 
/** 
 * A file version translator 
 * 
 * This is where all the magic happens 
 */ 
class FileTranslator extends VersionTranslater { 
     
    /** 
     * Tranlates a php file 
     * 
     * @param string $outputFile 
     * @throw RuntimeException 
     */ 
    function translateTo($outputFile) { 
         
        if( strtolower(strpos($this->getPath(), -3)) !== 'php') { 
            file_put_contents( $outputFile, $this->getCode() ); 
        } 
         
        $tokens = PHPToken::getTokens( $this ); 
         
        if(!$output = fopen($outputFile, 'w')){ 
            throw new RuntimeException("Unable to open $outputFile for writting"); 
        } 
         
        $namespace = array(); 
        $uses = array(); 
 
translate_start: 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        switch( $token->getCode() ) { 
            case T_NS_SEPARATOR:goto translate_start; 
            case T_NAMESPACE:    goto translate_namespace; 
            case T_USE:            goto translate_use; 
             
            case T_STRING:        array_unshift($tokens, $token); 
                                goto translate_string; 
             
            case T_CLASS: 
            case T_INTERFACE: 
            case T_EXTENDS: 
            case T_IMPLEMENTS: 
            case T_INSTANCEOF: 
            case T_NEW:             
                                fprintf($output, '%s', $token ); 
                                goto translate_class_name; 
 
            default:            fprintf($output, '%s', $token ); 
                                if( count($tokens) > 0 ) 
                                    goto translate_start; 
        } 
        goto translate_end; 
 
translate_namespace: 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        switch( $token->getCode() ) { 
            case T_STRING:             $namespace[] = (string) $token; 
            case T_NS_SEPARATOR:     
            case T_WHITESPACE:        goto translate_namespace; 
            case '{':                 
            case ';':                $namespace = implode( '_', $namespace); 
                                    goto translate_start; 
            default:                goto translate_error; 
        } 
         
translate_use: 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        $parseUse = array(); 
        $parseUseAs = false; 
        $parseUseIsGlobal = true; 
         
        switch( $token->getCode() ) { 
            case T_WHITESPACE:        goto translate_use; 
            case T_NS_SEPARATOR:    goto translate_use_in; 
            case T_STRING:            $parseUse[] = (string) $token; 
                                    $parseUseIsGlobal = false;  
                                    goto translate_use_in; 
 
            default:                goto translate_error; 
        } 
         
translate_use_in: 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        switch( $token->getCode() ) { 
            case T_STRING:            $parseUse[] = (string) $token; 
            case T_WHITESPACE: 
            case T_NS_SEPARATOR:    goto translate_use_in; 
            case T_AS:                goto translate_use_as; 
            case ';':                $parseUseClass = $parseUseAs ? $parseUseAs : end( $parseUse ); 
                                    $parseUse = implode('_', $parseUse); 
                                    if( $namespace && !$parseUseIsGlobal ) { 
                                        $parseUse = $namespace . '_' . $parseUse; 
                                    } 
                                    $uses[ $parseUseClass ] = $parseUse; 
                                     
                                    unset($parseUse, $parseUseClass, $parseUseIsGlobal, $parseUseAs); 
                                    goto translate_start; 
            default:                goto translate_error; 
        } 
         
translate_use_as: 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        switch($token->getCode()) { 
            case T_WHITESPACE:    goto translate_use_as; 
            case T_STRING:        $parseUseAs = (string) $token; 
                                goto translate_use_in; 
            default:            goto translate_error; 
        } 
         
translate_class_name: 
        $parseClass = array(); 
        $parseClassIsGlobal = false; 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        if( $token->getCode() === T_WHITESPACE ) { 
            fprintf($output, $token); 
        } else { 
            array_unshift($tokens, $token); 
        } 
 
translate_class_name_in: 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        switch( $token->getCode() ) { 
            case T_NAMESPACE:        goto translate_class_name_in; 
             
            case T_NS_SEPARATOR:    if( count($parseClass) === 0 ) 
                                        $parseClassIsGlobal = true; 
                                    goto translate_class_name_in; 
 
            case T_STRING:            $parseClass[] = (string) $token; 
                                    goto translate_class_name_in; 
             
             
            default:                if( count($parseClass) > 0 )  { 
                                        $parseClass = implode('_', $parseClass); 
                                        if( array_key_exists($parseClass, $uses)) { 
                                            $parseClass = $uses[$parseClass]; 
                                        } elseif( $namespace && !$parseClassIsGlobal && !class_exists($parseClass, false)){ 
                                            $parseClass = $namespace . '_' . $parseClass; 
                                        } 
                                        fprintf($output, '%s', $parseClass ); 
                                    } 
                                    fprintf($output, '%s', $token); 
                                    unset($parseClass); 
                                    goto translate_start; 
        } 
 
translate_string: 
        if(!$token = array_shift($tokens)) goto translate_end; 
        if( $token->getCode() !== T_STRING ) goto translate_end; 
         
        switch( (string) $token ) { 
            case 'self': 
            case 'parent': 
            case 'static': 
                fprintf($output, $token); 
                goto translate_start; 
        } 
         
        $parseString = $token; 
        if(!$token = array_shift($tokens)) goto translate_end; 
         
        switch( $token->getCode() ) { 
            case T_NS_SEPARATOR: 
            case T_DOUBLE_COLON:    array_unshift($tokens, $token); 
                                    array_unshift($tokens, $parseString); 
                                    unset( $parseString ); 
                                    goto translate_class_name; 
             
            default:                fprintf($output, '%s', $parseString); 
                                    fprintf($output, '%s', $token); 
                                    goto translate_start; 
        } 
 
translate_error: 
        $tokenName = $token->isLiteral() ? "'{$token->getName()}'" : $token->getName(); 
        throw new RuntimeException( 
            "Parse error: unexpected {$tokenName} in $outputFile on line {$token->getLine()}" 
        ); 
 
translate_end: 
        fclose( $output ); 
    } 
     
    /** 
     * Reads all the file and 
     */ 
    function getCode() { 
        return file_get_contents( $this->getPath() ); 
    } 
} 
 
/** 
 * A helper class used for hold php token information 
 */ 
class PHPToken { 
     
    /** 
     * @param FileTranslator $translator 
     * @return array 
     */ 
    static function getTokens(FileTranslator $translator) { 
        $tokens = array(); 
        foreach(token_get_all( $translator->getCode() ) as $token) { 
            $tokens[] = new self($token); 
        } 
        return $tokens; 
    } 
     
    /** 
     * @var array|string 
     */ 
    private $token; 
     
    /** 
     * @param array|string the parsed php token structure 
     */ 
    function __construct($token) { 
        $this->token = $token; 
    } 
     
    /** 
     * Returns the token as a string, it's the code 
     * 
     * @return string  
     */ 
    function __toString() { 
        return $this->isLiteral() ? $this->token : $this->token[1]; 
    } 
     
    /** 
     * Returns the php token structure 
     */ 
    function getToken() { 
        return $this->token; 
    } 
     
    /** 
     * Returns the token code 
     * @return int 
     */ 
    function getCode() { 
        return $this->isLiteral() ? $this->token : $this->token[0]; 
    } 
     
    /** 
     * Returns true, only and only if, the token is a string literal 
     * 
     * <code> 
        $t1 = new PHPToken( array(T_OPEN_TAG, '<?php')); 
        $t2 = new PHPToken( ';' ); 
         
        var_dump( $t1->iaLiteral() ); // false  
        var_dump( $t2->iaLiteral() ); // true  
     </code> 
     *  
     * @return boolean 
     */ 
    function isLiteral() { 
        return !is_array($this->token); 
    } 
     
    /** 
     * Return php constant name for the token or the token if it's literal 
     *  
     * @return string 
     */ 
    function getName(){ 
        return $this->isLiteral() ? $this->token : token_name( $this->token[0] ); 
    } 
     
    /** 
     * Return the line number of the token, or <code>false</code> if it's literal 
     *  
     * @return int 
     */ 
    function getLine(){ 
        return $this->isLiteral() ? false : $this->token[2]; 
    } 
}
 
 |