Пишем простенький обфускатор кода PHP

Категория: / DEV Блог / PHP (LAMP)
Постановка задачи:
Необходимо модифицировать код для неудобного его дальнейшего изменения. В часности - убрать комментарии, почитстить пробелы, переносы строк.
Зачем? Бывают разные ситуации, когда нужно передать код в третьи руки для ознакомления/тестирования. Чтобы гарантировать оплату (если есть опасения), можно передать
в измененном, неудобном для чтеня человеком виде.

В php есть волшебная функция token_get_all, которая разбирает код на лексемы и выдает результат в виде массива.
Отбросив ненужные части кода, мы получим нужный нам рафинированныый код, который можно смело отдавать на съедение неизвестным тетям и дядям.

Итак,

<?php
 
// CONFIG
 
$dir_from   = dirname(__FILE__);
$dir_to     = 'C:/php/code_obfuscated';
$copyright  = 'copyright (C) 2008';
 
// BEGIN
 
$data = fs::build_tree($dir_from);
 
obfuscator::head($copyright);
 
foreach ($data['files'] as $file) {
 
    $to = str_replace($dir_from, $dir_to, $file);
   
    if (substr($file, -4) == '.php') {
        // obfuscate
        printf("Convert %s\n", $file);        
        obfuscator::run(
            $file,
            $to        
        );
    }
    else {
        // just copy
        printf("Copy %s\n", $file);        
        @mkdir(dirname($to), 0, true);    
        @copy ($file, $to);      
    }
}
 
 
class obfuscator {
       
    static private $_IGNORED_TOKENS = array(
          T_COMMENT
        , T_DOC_COMMENT
        , T_WHITESPACE
        , T_ML_COMMENT
    );
   
    static private $_head;
   
    static function head($string) {
        self::$_head = sprintf("<?php\n/*\n%s\n*/\n?>", $string);
    }
   
    static function run($from, $to = null) {
       
        $file = file_get_contents($from);
        $tokens = token_get_all($file);
        $file = '';
   
        foreach ($tokens as $token) {
 
            if (is_string($token)) {
                // ;{}[]
                $file .= $token;
            }    
            else
            if (isset($token[1]) && !in_array($token[0], self::$_IGNORED_TOKENS)) {
               
                 $_token = $token[0];
                 
                 $pre_spacer  = (
                       $_token == T_AS
                    || $_token == T_EXTENDS
                    || $_token == T_IMPLEMENTS
                    || $_token == T_INSTANCEOF
                 )
                     ? ' '
                     : '';
                 
                 $post_spacer = (
                       $_token == T_VARIABLE
                    || $_token == T_DOUBLE_COLON
                    || $_token == T_STRING
                    || $_token == T_OBJECT_OPERATOR
                    || $_token == T_DOUBLE_ARROW
                    || $_token == T_CONSTANT_ENCAPSED_STRING
                    || $_token == T_LNUMBER
                    || $_token == T_NUM_STRING  
                    || $_token == T_DOLLAR_OPEN_CURLY_BRACES    
                    || $_token == T_CURLY_OPEN            
                    || $_token == T_ENCAPSED_AND_WHITESPACE
                 )
                    ? ''
                    : ' ';
                   
                 if ($_token == T_STRING && lcfirst($token[1]) == 'exception') $post_spacer = ' ';
               
                 $file .= ($pre_spacer . $token[1] . $post_spacer);
                 
                 
            }
        }      
 
       
        if (self::$_head) {
            $file = self::$_head . $file;
        }        
       
        if ($to) {
         @mkdir(dirname($to), 0, true);
         file_put_contents($to, $file);
        }
 
        return $file;
    }
}


По-умолчанию берем текущий каталог, с помощью fs::build_tree получаем рекурсивный список всех файлов в этой папке (код класса fs не привожу, напишите сами),
затем организуем цикл по этому списку файлов и запускаем obfuscator::run для файлов *.php и просто копируем файлы другого типа. Преобразованный код кладется
такой же структурой в папку обозначенную в переменной $dir_to.

Запускается из командной строки (консоли), куда и выводит все проделанные действия.