読者です 読者をやめる 読者になる 読者になる

php-compressor をちゃんと動くようにする

PHP ソースコードの難読化ツール php-compressor がたまにうまく動かないので修正した。
php-compressor の紹介と、ほかのツールとの比較はこちら。

問題点

クラス内で関数定義以降に変数定義があると、その変数名まで短縮されてしまう。

<?php

class Hoge {

	private function oops() {}

	public $pub = 'public';
	protected $pro = 'protected';
	private $pri = 'private';
	private static $sta = 'static';

	public function piyo() {
		static $lst = 'static in function';
		$local = 'local';
		$this->pub;
		$this->pro;
		$this->pri;
		self::$static;
		$local;
		$lst;
	}
}

->

class Hoge{private function oops(){}var$F='public';protected$E='protected';private$D='private';private static$C='static';function piyo(){static$A='static in function';$B='local';$this->pub;$this->pro;$this->pri;self::$static;$B;$A;}}

問題となる書きかたはあまりいい書きかたではないが、コードが動かなくなるよりいい。

解決策

is_class_scope() の判定に問題があるので、このロジックを変更する。compressor.php の shrink_var_names() を次のように書き換えればよい。

private function shrink_var_names() {
    $stat = array();
    $indices = array();

    $is_class_scope = false;
    $class_scope_level = 0;
               
    for($i = 0; $i < count($this->tokens); $i++) {                
        list($type, $text) = $this->tokens[$i];

        if ($type === T_CLASS) {
            $is_class_scope = true;
            continue;
        }

        if ($is_class_scope) {
            if ($text === '{' || $text === '(') {
                $class_scope_level++;
            }
            else if ($text === '}' || $text === ')') {
                $class_scope_level--;
                if ($class_scope_level === 0) {
                    $is_class_scope = false;
                }
            }
        }
                       
        if($type != T_VARIABLE)
            continue;

        if(isset(self::$RESERVED_VARS[$text]))
            continue;                    
                              
        if($i > 0) {
            $prev_type = $this->tokens[$i - 1][0];
            if($prev_type == T_DOUBLE_COLON)
                continue;                    
            if($class_scope_level === 1)
                continue;
        }
        
        $indices[] = $i;
        if(!isset($stat[$text]))
            $stat[$text] = 0;
        $stat[$text]++;
    }
    
    arsort($stat);
    
    $aliases = array();
    $i = 0;
    foreach(array_keys($stat) as $name) {
        $aliases[$name] = $this->encode_id($i);
        $i++;
    }
    unset($stat);
    
    foreach($indices as $index) {
        $name = $this->tokens[$index][1];
        $this->tokens[$index][1] = '$' . $aliases[$name];
    }
}