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: 4 years ago
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) > 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];
    }
}