Skip to content

Commit

Permalink
Add more checks when decoding JSON Files
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromegamez committed Dec 1, 2022
1 parent da12a20 commit a40692f
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 18 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

* Added more checks when decoding JSON Files

## [1.1.0] - 2022-11-04

Dropped support for PHP <8.1
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
]
},
"scripts-descriptions": {
"clean": "Recreates ",
"clean": "Recreates the build/cache directory",
"phpstan": "Runs static analysis with PHPStan",
"phpunit": "Runs tests with PHPUnit",
"psalm": "Runs static analysis with Psalm",
Expand Down
50 changes: 35 additions & 15 deletions src/Json.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
namespace Beste;

use JsonException;
use SplFileInfo;
use SplFileObject;
use Throwable;
use UnexpectedValueException;

final class Json
Expand All @@ -14,11 +17,11 @@ final class Json
private const DECODE_DEFAULT = JSON_BIGINT_AS_STRING;

/**
* @throws UnexpectedValueException
* param non-empty-string $json
*
* @return mixed
* @throws UnexpectedValueException
*/
public static function decode(string $json, ?bool $forceArray = null)
public static function decode(string $json, ?bool $forceArray = null): mixed
{
$forceArray = $forceArray ?? false;
$flags = $forceArray ? JSON_OBJECT_AS_ARRAY : 0;
Expand All @@ -31,25 +34,44 @@ public static function decode(string $json, ?bool $forceArray = null)
}

/**
* @throws UnexpectedValueException
* @param non-empty-string $path
*
* @return mixed
* @throws UnexpectedValueException
*/
public static function decodeFile(string $path, bool $forceArray = null)
public static function decodeFile(string $path, bool $forceArray = null): mixed
{
if (!is_readable($path)) {
throw new UnexpectedValueException("The file at '$path' does not exist");
$fileInfo = new SplFileInfo($path);

if ($fileInfo->isLink() && $linkTarget = $fileInfo->getLinkTarget()) {
$fileInfo = new SplFileInfo($linkTarget);
}

if (!$fileInfo->isFile()) {
throw new UnexpectedValueException("`$path` does not point to a file.");
}

if (!$fileInfo->isReadable()) {
throw new UnexpectedValueException("`$path` is not readable.");
}

$file = $fileInfo->openFile();
$contents = $file->fread($file->getSize());

if ($contents === false) {
throw new UnexpectedValueException("Unable to read contents of `$path`");
}

return self::decode((string) file_get_contents($path), $forceArray);
if ($contents === '') {
throw new UnexpectedValueException("The file at `$path` is empty");
}

return self::decode($contents, $forceArray);
}

/**
* @param mixed $data
*
* @throws UnexpectedValueException
*/
public static function encode($data, ?int $options = null): string
public static function encode(mixed $data, ?int $options = null): string
{
$options = $options ?? 0;

Expand All @@ -61,11 +83,9 @@ public static function encode($data, ?int $options = null): string
}

/**
* @param mixed $value
*
* @throws UnexpectedValueException
*/
public static function pretty($value, ?int $options = null): string
public static function pretty(mixed $value, ?int $options = null): string
{
$options = $options ?? 0;

Expand Down
28 changes: 26 additions & 2 deletions tests/DecodeJsonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Beste\Json\Tests;

use Beste\Json;
use SplFileObject;
use UnexpectedValueException;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -68,10 +69,33 @@ public function it_rejects_an_unreadable_file(): void
public function it_rejects_a_file_with_invalid_json(): void
{
$path = __DIR__.'/invalid.json';
assert(file_exists($path));

$this->expectException(UnexpectedValueException::class);
assert(file_exists($path));
Json::decodeFile($path);
}

/** @test */
public function it_rejects_a_directory(): void
{
$this->expectException(UnexpectedValueException::class);
Json::decodeFile(__DIR__);
}

/** @test */
public function it_resolves_links(): void
{
$path = __DIR__.'/valid.json';
$symlinkPath = __DIR__.'/'.__FUNCTION__.'.json';

try {
$this->assertNotFalse(symlink($path, $symlinkPath));
$this->assertTrue(is_link($symlinkPath));

$this->assertIsObject(Json::decodeFile($symlinkPath));
} finally {
unlink($symlinkPath);
}

$this->assertIsObject(Json::decodeFile($path));
}
}

0 comments on commit a40692f

Please sign in to comment.