Login   Register  
PHP Classes
elePHPant
Icontem

File: VersionTranslater.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of eridal  >  Version Translator  >  VersionTranslater.php  >  Download  
File: VersionTranslater.php
Role: Class source
Content type: text/plain
Description: contains VersionTranslater, DirectoryTranslator, FileTranslator and PHPToken classes
Class: Version Translator
Convert PHP code between namespaces and PEAR names
Author: By
Last change:
Date: 2010-10-27 20:08
Size: 9,515 bytes
 

Contents

Class file image Download
<?php    
/**
 * @author Martin Scotta <martinscotta@gmail.com>
 */
 
/**
 * 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) > )
                                    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) === )
                                        
$parseClassIsGlobal true;
                                    goto 
translate_class_name_in;

            case 
T_STRING:            $parseClass[] = (string) $token;
                                    goto 
translate_class_name_in;
            
            
            default:                if( 
count($parseClass) > )  {
                                        
$parseClass implode('_'$parseClass);
                                        if( 
array_key_exists($parseClass$uses)) {
                                            
$parseClass $uses[$parseClass];
                                        } elseif( 
$namespace && !$parseClassIsGlobal && !class_exists($parseClassfalse)){
                                            
$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];
    }
}