Was zum Mitgucken: Pfadputzer
bearbeitet von RaketenwilliKlasse und derzeit enthaltene Methode `clearPath()` (Enthält noch ein wenig Überflüssiges...)
Diese Klasse soll etwas wie /foo/bar/../baz/.//tok zu /foo/baz/tok machen. Auch für Windows.
Fragen dazu:
* Sieh jemand echte FEHLER, die zu unrichtigen Ergebnissen führen?
* Sind die Ergebnisse überhaupt richtig?
* Ist es korrekt bei einem übergeben Errorlevel (3. Parameter)
mit
1. `E_USER_NOTICE`: weiterzumachen und das Ergebnis zu liefern, wenn der resultierende Pfad wegen zu vieler '/../' außerhalb des gegebenen liegt aber mit
2. `E_USER_WARNING` auszusteigen und false zu liefern, wenn der resultierende Pfad wegen zu vieler '/../' außerhalb des gegebenen liegt, jedoch mit
3. `E_USER_ERROR` stets mit einem fatalen Fehler auszusteigen, wenn diese Sachverhalte auftreten.
4. Bei Windows-Pfaden (mit Laufwerksbuchstaben) stets false zu liefern wenn der ermittelte Pfad auf ein Verzeichnis unterhalb des Windows-Laufwerksbuchstaben fallen würde (und nicht `E_USER_ERROR` gefordert wird, was wie oben endet.
~~~PHP
<?php
namespace tools;
class PathManipulations {
static public function clearPath( $string, $delim='/', $errorLevel=E_USER_NOTICE ) {
if ( ! in_array( $errorLevel, [ E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING ] ) ) {
error_log ( "Parameter No. 3 must be E_USER_ERROR, E_USER_NOTICE or E_USER_WARNING", E_USER_ERROR );
}
$returnAsArray = true;
$newArray = [];
$arr = explode( $delim, $string );
if ( 0 == count( $arr) ) {
return '.' . $delim;
}
if ( preg_match( '/^[a-z]:/i', substr( $string, 0, 2 ) ) ) {
$pathStartWithWindowsDrive = true;
} else {
$pathStartWithWindowsDrive = false;
}
if ( $pathStartWithWindowsDrive or $delim == substr( $string, 0, 1 ) ) {
$pathIsAbsolute = true;
$pathIsRelative = false;
$pathDoubleDot = false;
} else {
$pathIsRelative = true;
$pathIsAbsolute = false;
if ( '..' == $arr[0] ) {
$pathDoubleDot = true;
$pathSingleDot = false;
} elseif ( '.' == $arr[0] ) {
$pathDoubleDot = false;
$pathSingleDot = true;
} else {
$string = '.' . $delim . $string;
$arr = explode( $delim, $string );
$pathDoubleDot = false;
$pathSingleDot = true;
}
}
if ( $pathIsAbsolute ) {
$newArray[0] = array_shift( $arr );
foreach ( $arr as $s ) {
if ( '' == $s or '.' == $s ) {
continue;
}
if ( '..' == $s ) {
$l = array_pop( $newArray );
if ( 0 == count( $newArray ) ) {
if ( $pathStartWithWindowsDrive ) {
$eMsg = 'The expected target is not on the given Windows-drive ' . substr( $string, 0, 2 );
if ( E_USER_ERROR != $errorLevel ) {
$eMsg .= ' FALSE returned';
}
error_log( $eMsg, $errorLevel );
return false;
} else {
$eMsg = 'The expected target is outside of the root of the given absolute path: ' . $string;
if ( E_USER_NOTICE == $errorLevel ) {
$eMsg .= ' A new relative path was created.';
error_log( $eMsg, $errorLevel );
$string = '..' . $delim . implode( $delim, $arr );
return self::clearPath( $string, $delim, $errorLevel);
} elseif ( E_USER_WARNING == $errorLevel ) {
$eMsg .= ' FALSE returned.';
error_log( $eMsg, $errorLevel );
return false;
} else {
error_log( $eMsg, E_USER_ERROR );
}
}
}
} else {
$newArray[] = $s;
}
}
return implode( $delim, $newArray );
}
if ( $pathDoubleDot ) {
$newArray[0] = array_shift( $arr );
foreach ( $arr as $s ) {
if ( '' == $s or '.' == $s ) {
continue;
}
if ( '..' == $s ) {
if ( 1 < count( $newArray ) ) {
$l = array_pop( $newArray );
} else {
array_unshift( $newArray, '..' );
}
} else {
$newArray[] = $s;
}
}
return implode( $delim, $newArray );
}
if ( $pathSingleDot ) {
array_shift( $arr );
$newArray[0] = array_shift( $arr );
foreach ( $arr as $s ) {
if ( '' == $s or '.' == $s ) {
continue;
}
if ( '..' == $s ) {
if ( 0 < count( $newArray ) ) {
$l = array_pop( $newArray );
} else {
$eMsg = 'The expected target is outside of the given relative path: ' . $string;
if ( E_USER_NOTICE == $errorLevel ) {
$eMsg .= ' A new relative path was created.';
error_log( $eMsg, $errorLevel );
$string = '..' . $delim . implode( $delim, $arr );
return self::clearPath( $string, $delim, $errorLevel);
} elseif ( E_USER_WARNING == $errorLevel ) {
$eMsg .= ' FALSE returned.';
error_log( $eMsg, $errorLevel );
return false;
} else {
error_log( $eMsg, E_USER_ERROR );
}
/* War
$string = '..' . $delim . implode( $delim, $arr );
return self::clearPath( $string, $delim, $errorLevel );
*/
}
} else {
$newArray[] = $s;
}
}
if ( $pathStartWithWindowsDrive ) {
return implode( $delim, $newArray );
} else {
return '.' . $delim . implode( $delim, $newArray );
}
}
}
}
~~~
Ein paar Tests:
~~~PHP
<?php
require_once 'PathManipulations.php';
echo "Tests mit E_USER_NOTICE\n";
$p[] = './foo/.././bar/../baz//tok';
$p[] = './foo/.././bar/../..//baz//tok';
$p[] = './foo/.././bar/../../..//baz//tok';
$p[] = '../foo/.././bar/../baz//tok';
$p[] = '../foo/.././bar/../..//baz//tok';
$p[] = '../foo/.././bar/../../..//baz//tok';
$p[] = 'foo/.././bar/../baz//tok';
$p[] = 'foo/.././bar/../..//baz//tok';
$p[] = 'foo/.././bar/../../..//baz//tok';
$p[] = '/foo/.././bar/../baz//tok';
$p[] = '/foo/.././bar/../..//baz//tok';
$p[] = '/foo/.././bar/../../..//baz//tok';
foreach ( $p as $s ) {
echo 'Teste: "' . $s . '"' . PHP_EOL;
echo 'Ergebnis: "' . \tools\PathManipulations::clearPath( $s, '/', E_USER_NOTICE ) . PHP_EOL . PHP_EOL;
}
unset( $p );
echo "Tests mit E_USER_WARNING\n";
$p[] = '/foo/.././bar/../baz//tok';
$p[] = '/foo/.././bar/../..//baz//tok';
$p[] = '/foo/.././bar/../../..//baz//tok';
foreach ( $p as $s ) {
echo 'Teste: "' . $s. '"' . PHP_EOL;
echo 'Ergebnis: "' . \tools\PathManipulations::clearPath( $s, '/', E_USER_WARNING ) . '"' . PHP_EOL . PHP_EOL;
}
unset( $p );
echo "Tests für Windows mit E_USER_WARNING\n";
$p[] = '\\foo\\..\\.\\bar\\..\\baz\\\\tok';
$p[] = '\\foo\\..\\.\\bar\\..\\..\\\\baz\\\\tok';
$p[] = '\\foo\\..\\.\\bar\\..\\..\\..\\\\baz\\\\tok';
$p[] = '.\\foo\\..\\.\\bar\\..\\baz\\\\tok';
$p[] = '.\\foo\\..\\.\\bar\\..\\..\\\\baz\\\\tok';
$p[] = '.\\foo\\..\\.\\bar\\..\\..\\..\\\\baz\\\\tok';
$p[] = '..\\foo\\..\\.\\bar\\..\\baz\\\\tok';
$p[] = '..\\foo\\..\\.\\bar\\..\\..\\\\baz\\\\tok';
$p[] = '..\\foo\\..\\.\\bar\\..\\..\\..\\\\baz\\\\tok';
$p[] = 'foo\\..\\.\\bar\\..\\baz\\\\tok';
$p[] = 'foo\\..\\.\\bar\\..\\..\\\\baz\\\\tok';
$p[] = 'foo\\..\\.\\bar\\..\\..\\..\\\\baz\\\\tok';
$p[] = 'C:\\foo\\..\\.\\bar\\..\\baz\\\\tok';
$p[] = 'C:\\foo\\..\\.\\bar\\..\\..\\\\baz\\\\tok';
$p[] = 'C:\\foo\\..\\.\\bar\\..\\..\\..\\\\baz\\\\tok';
foreach ( $p as $s ) {
echo 'Teste: "' . $s . '"' . PHP_EOL;
echo 'Ergebnis: "' . \tools\PathManipulations::clearPath( $s, '\\', E_USER_WARNING ) . '"' . PHP_EOL . PHP_EOL;
}
~~~
Ausgaben:
~~~
Tests mit E_USER_NOTICE
Teste: "./foo/.././bar/../baz//tok"
Ergebnis: "./baz/tok
Teste: "./foo/.././bar/../..//baz//tok"
The expected target is outside of the given relative path: ./foo/.././bar/../..//baz//tok A new relative path was created.
Ergebnis: "../baz/tok
Teste: "./foo/.././bar/../../..//baz//tok"
The expected target is outside of the given relative path: ./foo/.././bar/../../..//baz//tok A new relative path was created.
Ergebnis: "../../baz/tok
Teste: "../foo/.././bar/../baz//tok"
Ergebnis: "../baz/tok
Teste: "../foo/.././bar/../..//baz//tok"
Ergebnis: "../../baz/tok
Teste: "../foo/.././bar/../../..//baz//tok"
Ergebnis: "../baz/tok
Teste: "foo/.././bar/../baz//tok"
Ergebnis: "./baz/tok
Teste: "foo/.././bar/../..//baz//tok"
The expected target is outside of the given relative path: ./foo/.././bar/../..//baz//tok A new relative path was created.
Ergebnis: "../baz/tok
Teste: "foo/.././bar/../../..//baz//tok"
The expected target is outside of the given relative path: ./foo/.././bar/../../..//baz//tok A new relative path was created.
Ergebnis: "../../baz/tok
Teste: "/foo/.././bar/../baz//tok"
Ergebnis: "/baz/tok
Teste: "/foo/.././bar/../..//baz//tok"
The expected target is outside of the root of the given absolute path: /foo/.././bar/../..//baz//tok A new relative path was created.
Ergebnis: "../../baz/tok
Teste: "/foo/.././bar/../../..//baz//tok"
The expected target is outside of the root of the given absolute path: /foo/.././bar/../../..//baz//tok A new relative path was created.
Ergebnis: "../baz/tok
Tests mit E_USER_WARNING
Teste: "/foo/.././bar/../baz//tok"
Ergebnis: "/baz/tok"
Teste: "/foo/.././bar/../..//baz//tok"
The expected target is outside of the root of the given absolute path: /foo/.././bar/../..//baz//tok FALSE returned.
Ergebnis: ""
Teste: "/foo/.././bar/../../..//baz//tok"
The expected target is outside of the root of the given absolute path: /foo/.././bar/../../..//baz//tok FALSE returned.
Ergebnis: ""
Tests für Windows mit E_USER_WARNING
Teste: "\foo\..\.\bar\..\baz\\tok"
Ergebnis: "\baz\tok"
Teste: "\foo\..\.\bar\..\..\\baz\\tok"
The expected target is outside of the root of the given absolute path: \foo\..\.\bar\..\..\\baz\\tok FALSE returned.
Ergebnis: ""
Teste: "\foo\..\.\bar\..\..\..\\baz\\tok"
The expected target is outside of the root of the given absolute path: \foo\..\.\bar\..\..\..\\baz\\tok FALSE returned.
Ergebnis: ""
Teste: ".\foo\..\.\bar\..\baz\\tok"
Ergebnis: ".\baz\tok"
Teste: ".\foo\..\.\bar\..\..\\baz\\tok"
The expected target is outside of the given relative path: .\foo\..\.\bar\..\..\\baz\\tok FALSE returned.
Ergebnis: ""
Teste: ".\foo\..\.\bar\..\..\..\\baz\\tok"
The expected target is outside of the given relative path: .\foo\..\.\bar\..\..\..\\baz\\tok FALSE returned.
Ergebnis: ""
Teste: "..\foo\..\.\bar\..\baz\\tok"
Ergebnis: "..\baz\tok"
Teste: "..\foo\..\.\bar\..\..\\baz\\tok"
Ergebnis: "..\..\baz\tok"
Teste: "..\foo\..\.\bar\..\..\..\\baz\\tok"
Ergebnis: "..\baz\tok"
Teste: "foo\..\.\bar\..\baz\\tok"
Ergebnis: ".\baz\tok"
Teste: "foo\..\.\bar\..\..\\baz\\tok"
The expected target is outside of the given relative path: .\foo\..\.\bar\..\..\\baz\\tok FALSE returned.
Ergebnis: ""
Teste: "foo\..\.\bar\..\..\..\\baz\\tok"
The expected target is outside of the given relative path: .\foo\..\.\bar\..\..\..\\baz\\tok FALSE returned.
Ergebnis: ""
Teste: "C:\foo\..\.\bar\..\baz\\tok"
Ergebnis: "C:\baz\tok"
Teste: "C:\foo\..\.\bar\..\..\\baz\\tok"
The expected target is not on the given Windows-drive C: FALSE returned
Ergebnis: ""
Teste: "C:\foo\..\.\bar\..\..\..\\baz\\tok"
The expected target is not on the given Windows-drive C: FALSE returned
Ergebnis: ""
~~~