Skip to content

Commit

Permalink
Add tests and re-add funtionality for non-existent paths
Browse files Browse the repository at this point in the history
Signed-off-by: ramchale <[email protected]>
  • Loading branch information
ramchale committed Nov 14, 2024
1 parent 5a51a0c commit 2337e18
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 26 deletions.
4 changes: 2 additions & 2 deletions docs/book/v3/standard-filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -1140,8 +1140,8 @@ For any given link or pathname, its absolute path will be returned.
References to `/./`, `/../` and extra `/` sequences in the input path will be stripped.
The resulting path will not have any symbolic links, `/./`, or `/../` sequences.

`Laminas\Filter\RealPath` will return `FALSE` on failure, e.g. if the file does not exist.
On BSD systems `Laminas\Filter\RealPath` doesn't fail if only the last path component doesn't exist, while other systems will return `false`.
`Laminas\Filter\RealPath` will return the value passed to the filter on failure, e.g. if the file does not exist.
On BSD systems `Laminas\Filter\RealPath` doesn't fail if only the last path component doesn't exist, while other systems will return the value passed to the filter.

```php
$filter = new Laminas\Filter\RealPath();
Expand Down
1 change: 1 addition & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@
</file>
<file src="test/RealPathTest.php">
<PossiblyUnusedMethod>
<code><![CDATA[returnNonExistentPathDataProvider]]></code>
<code><![CDATA[returnUnfilteredDataProvider]]></code>
</PossiblyUnusedMethod>
</file>
Expand Down
34 changes: 30 additions & 4 deletions src/RealPath.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

namespace Laminas\Filter;

use function array_pop;
use function explode;
use function file_exists;
use function getcwd;
use function implode;
use function is_string;
use function realpath;
use function str_starts_with;

use const DIRECTORY_SEPARATOR;

/**
* @psalm-type Options = array{
Expand Down Expand Up @@ -34,13 +41,32 @@ public function filter(mixed $value): mixed
return $value;
}

$realpath = realpath($value);
$realPath = realpath($value);

if ($realpath === false) {
return $value;
if ($realPath !== false) {
return $realPath;
}

$path = $value;

if (! str_starts_with($path, DIRECTORY_SEPARATOR)) {
$path = getcwd() . DIRECTORY_SEPARATOR . $path;
}

$stack = [];
$parts = explode(DIRECTORY_SEPARATOR, $path);

foreach ($parts as $dir) {
if ($dir !== '' && $dir !== '.') {
if ($dir === '..') {
array_pop($stack);
} else {
$stack[] = $dir;
}
}
}

return $realpath;
return DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $stack);
}

public function __invoke(mixed $value): mixed
Expand Down
53 changes: 33 additions & 20 deletions test/RealPathTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
use PHPUnit\Framework\TestCase;
use stdClass;

use const DIRECTORY_SEPARATOR;
use function dirname;
use function getcwd;
use function str_contains;

use const PHP_OS;

class RealPathTest extends TestCase
{
/**
* Ensures expected behavior for existing file
*/
public function testFileExists(): void
public function testExistingFileReturnsRealPath(): void
{
$filter = new RealPathFilter();

Expand All @@ -26,32 +27,44 @@ public function testFileExists(): void
self::assertStringContainsString($filename, $result);
}

/**
* Ensures expected behavior for nonexistent file
*/
public function testFileNonexistent(): void
public function testNonexistentFileReturnsValuePassedToFilter(): void
{
$filter = new RealPathFilter();

$path = '/path/to/nonexistent';
self::assertSame($path, $filter->filter($path));
}

public function testNonExistentPath(): void
public function testBSDAllowsLastPortionToNotExist(): void
{
$filter = new RealPathFilter(['exists' => false]);
$filter = new RealPathFilter();

$path = './nonexistent';

if (str_contains(PHP_OS, 'BSD')) {
self::assertSame(getcwd() . '/nonexistent', $filter($path));
} else {
self::assertSame($path, $filter($path));
}
}

$path = __DIR__ . DIRECTORY_SEPARATOR . '_files';
self::assertSame($path, $filter($path));
public static function returnNonExistentPathDataProvider(): array
{
return [
['/nonexistent/absolute/path', '/nonexistent/absolute/path'],
['/nonexistent/absolute/extra///slashes', '/nonexistent/absolute/extra/slashes'],
['./nonexistent/relative/path', getcwd() . '/nonexistent/relative/path'],
['./dropped/parts/../../path', getcwd() . '/path'],
['../relative/from/parent', dirname(getcwd()) . '/relative/from/parent'],
];
}

$path2 = __DIR__ . DIRECTORY_SEPARATOR . '_files'
. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '_files';
self::assertSame($path, $filter($path2));
#[DataProvider('returnNonExistentPathDataProvider')]
public function testNonExistentPathAllowed(string $path, string $expectedPath): void
{
$filter = new RealPathFilter(['exists' => false]);

$path3 = __DIR__ . DIRECTORY_SEPARATOR . '_files'
. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '.'
. DIRECTORY_SEPARATOR . '_files';
self::assertSame($path, $filter($path3));
self::assertSame($expectedPath, $filter($path));
}

/** @return list<array{0: mixed}> */
Expand Down

0 comments on commit 2337e18

Please sign in to comment.