<?php
/*
** repariert PHP Skripte:
** entfernt BOMs
** entfernt Leerzeilen am Beginn
** entfernt Leerzeichen vor <? oder <?php 
** ersetzt <? durch <?php
*/

# Konfiguration:

$config['DIR'] = '/tmp';   # In welchem Verzeichnis sollen die Dateien repariert werden?
#$config['DIR'] = '.';     # aktuelles Verzeichnis
#$config['DIR'] = __DIR__; # Verzeichnis dieses Skriptes
#$config['DIR'] = $SERVER['DOCUMENT_ROOT'];

$config['IGNOREUPPERLOWER'] = false; # Für Linux-Benutzer...
#$config['IGNOREUPPERLOWER'] = true; # Für Windows-Benutzer...

$config['FIND'] = '*.inc.php'; # * beliebiges Zeichen beliebig oft
                           # ? beliebiges Zeichen genau einmal

$arExkludeList=array(
    # Nach letztem Eintrag kein Komma!
    'diese_nicht/*', # Unterverzeichnis ausgehend von $config['DIR'], kein führender "/"
    '*.ex'    # Dateien mit Endung "ex"
);

$habeBackup = false; # Setzen Sie dieses aus true, nachdem ein Backup gemacht wurde.

## Ab hier ändern nur Programmierer, die wissen, was sie tun:

define ( 'DIR', rtrim($config['DIR']), '/' );
define ( 'FIND', $config['FIND'] );
define ( 'IGNOREUPPERLOWER', $config['IGNOREUPPERLOWER']);

if ( isset($SERVER['DOCUMENT_ROOT']) ) {
    define ('NL', "<br>\n");
} else {
    define ('NL', "\n");
}

$fileList=getFileList(DIR);
$arExkludeListRegex=array();

foreach ($arExkludeList as $s) {
   if ($s) {
        $arExkludeListRegex[] = patternToRegex(DIR . '/' . $s, true, IGNOREUPPERLOWER);
   }
}
unset($arExkludeList);

foreach ($fileList as $file) {
    $isExcluded=false;
    foreach ($arExkludeListRegex as $regex) {
        if ( ! $isExcluded && preg_match($regex, $file)  ) {
           $isExcluded=true;
           break;
        }
    }

    if ($isExcluded) {
        echo "Info: '$file' wurde ausgeschlossen.", NL;
    } elseif (! is_readable($file)) {
        echo "Warnung: Keine Leserechte an '$file'", NL;
    } elseif (! is_writable($file)) {
        echo "Warnung: Keine Schreibrechte an '$file'", NL;
    } elseif ($habeBackup) {
        echo "Info: Verarbeite '$file'";
        repair($file);
    } else {
        echo "Info: würde '$file' verarbeiten, wenn das Anlegen eines Backups bestätigt wäre.", NL;
    }
}

echo "### Fertig! ###", NL;

#### Von diesem Skript verwendete Funktionen ####
# sollten unverändert bleiben.

function patternToRegex($string, $allRow=false, $ignoreUpperLower=false) {
    if ($ignoreUpperLower) {
        $ignoreUpperLower='i';
    } else {
        $ignoreUpperLower='';
    }

    $string=str_replace('/', '\\/', $string);

    $search  = array( '.' , '*' , '?');
    $replacment = array( '\.', '.*', '.');

    if ($allRow) {
        return '/^' . str_replace($search, $replacment,  $string) . '$/' . $ignoreUpperLower;
    } else {
        return '/' . str_replace($search, $replacment,  $string) . '/' . $ignoreUpperLower;
    }
}

function getFileList($dir) {
    $list=array();
    $regex = patternToRegex(FIND, false, IGNOREUPPERLOWER);
    if ( is_dir($dir) && is_readable($dir) ) {
        $d = dir($dir);
        if ($d) {
            while (false !== ($entry = $d->read())) {
                if ( '.' != $entry && '..' != $entry ) {
                    $entry = $dir .'/'. $entry;
                    if ( is_dir($entry) ) {
                        $arT = getFileList($entry);
                        foreach ($arT as $s) {
                            $list[]=$s;
                        }
                    } elseif ( preg_match($regex, $entry) ) {
                        $list[] = $entry;
    }   }   }   }   }
    return $list;
}


#### Arbeitende Funktionen ####
# können für andere Verwendungen ersetzt werden

function repair($file) {
    $newSkript = '';
    $isFirstLine = true;

    $ar=file($file);

    foreach ($ar as $row) {
        #$row = trim($row);
        $row = rmBOM($row);                      #löscht alle bekannten BOM
        if ( $isFirstLine ) {
            $row = rmOnlySpaces($row);           #löscht alle Spaces in Zeilen, die nur solche
                                                 #enthalten 
        }
        if ( $row ) {
            $row = rmSpacesBevorPhpTag($row);    #löscht alle spaces vor <?php
                                                 #ersetzt <? und <?PHP durch <?php
            $newSkript .= $row;
            $isFirstLine = false;
        }
    }
    file_put_contents($file, $newSkript);
    echo " ... erledigt!", NL;
}


function rmBOM($str, $replacment = '') {
 # source: https://en.wikipedia.org/wiki/Byte_order_mark

    $boms=array( # Liste möglicher BOMs
        urldecode('%EF%BB%BF'),       #UTF-8
        urldecode('%FE%FF'),          #UTF-16 (BE)
        urldecode('%FF%FE'),          #UTF-16 (LE)
        urldecode('%00%00%FE%FF'),    #UTF-32 (BE)
        urldecode('%FF%FE%00%00'),    #UTF-32 (LE)
        urldecode('%2B%2F%76%38'),    #UTF-7
        urldecode('%2B%2F%76%39'),    #UTF-7
        urldecode('%2B%2F%76%2B'),    #UTF-7rmOnlySpaces
        urldecode('%2B%2F%76%2F'),    #UTF-7
        urldecode('%2B%2F%76%38%2D'), #UTF-7
        urldecode('%F7%64%4C'),       #UTF-1rmOnlySpaces
        urldecode('%DD%73%66%73'),    #UTF-EeBCDIC
        urldecode('%0E%FE%FF'),       #SCSU
        urldecode('%FB%EE%28'),       #BOCU-1
        urldecode('%84%31%95%33')     #GB-18030
    );
    return str_replace($boms, $replacment, $str);
}

function rmSpacesBevorPhpTag($str, $replacment = '') {
    $replacment .= '<?php';
    return preg_replace('/^\s*<\?([pP][hH][pP]){0,1}/', $replacment, $str);
}

function rmOnlySpaces($str, $replacment = 'repair($file)') {
    return preg_replace('/^\s+$/', $replacment, $str);
}