Skip to content

Commit b331797

Browse files
authored
Merge pull request #657 from flightphp/json-util
added JSON util to use elsewhere
2 parents f2b4aea + eecbdc3 commit b331797

File tree

9 files changed

+356
-21
lines changed

9 files changed

+356
-21
lines changed

flight/Engine.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use flight\net\Response;
1515
use flight\net\Router;
1616
use flight\template\View;
17+
use flight\util\Json;
1718
use Throwable;
1819
use flight\net\Route;
1920
use Psr\Container\ContainerInterface;
@@ -522,11 +523,11 @@ public function _start(): void
522523
// not doing much here, just setting the requestHandled flag to true
523524
$this->requestHandled = true;
524525

525-
// Allow filters to run
526-
// This prevents multiple after events from being registered
527-
$this->after('start', function () use ($self) {
528-
$self->stop();
529-
});
526+
// Allow filters to run
527+
// This prevents multiple after events from being registered
528+
$this->after('start', function () use ($self) {
529+
$self->stop();
530+
});
530531
} else {
531532
// deregister the request and response objects and re-register them with new instances
532533
$this->unregister('request');
@@ -665,7 +666,7 @@ public function _error(Throwable $e): void
665666
<h1>500 Internal Server Error</h1>
666667
<h3>%s (%s)</h3>
667668
<pre>%s</pre>
668-
HTML,
669+
HTML, // phpcs:ignore
669670
$e->getMessage(),
670671
$e->getCode(),
671672
$e->getTraceAsString()
@@ -906,9 +907,7 @@ public function _json(
906907
?string $charset = 'utf-8',
907908
int $option = 0
908909
): void {
909-
// add some default flags
910-
$option |= JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR;
911-
$json = $encode ? json_encode($data, $option) : $data;
910+
$json = $encode ? Json::encode($data, $option) : $data;
912911

913912
$this->response()
914913
->status($code)
@@ -966,7 +965,7 @@ public function _jsonp(
966965
string $charset = 'utf-8',
967966
int $option = 0
968967
): void {
969-
$json = $encode ? json_encode($data, $option) : $data;
968+
$json = $encode ? Json::encode($data, $option) : $data;
970969
$callback = $this->request()->query[$param];
971970

972971
$this->response()

flight/commands/AiGenerateInstructionsCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public function execute()
8585
$detailsText
8686
Current instructions:
8787
$context
88-
EOT;
88+
EOT; // phpcs:ignore
8989

9090
// Read LLM creds
9191
$creds = json_decode(file_get_contents($runwayCredsFile), true);

flight/util/Json.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace flight\util;
6+
7+
use Exception;
8+
use JsonException;
9+
10+
/**
11+
* Json utility class for encoding and decoding JSON data.
12+
*
13+
* This class provides centralized JSON handling for the FlightPHP framework,
14+
* with consistent error handling and default options.
15+
*/
16+
class Json
17+
{
18+
/**
19+
* Default JSON encoding options
20+
*/
21+
public const DEFAULT_ENCODE_OPTIONS = JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR;
22+
23+
/**
24+
* Default JSON decoding options
25+
*/
26+
public const DEFAULT_DECODE_OPTIONS = JSON_THROW_ON_ERROR;
27+
28+
/**
29+
* Encodes data to JSON string.
30+
*
31+
* @param mixed $data Data to encode
32+
* @param int $options JSON encoding options (bitmask)
33+
* @param int $depth Maximum depth
34+
*
35+
* @return string JSON encoded string
36+
* @throws Exception If encoding fails
37+
*/
38+
public static function encode($data, int $options = 0, int $depth = 512): string
39+
{
40+
$options = $options | self::DEFAULT_ENCODE_OPTIONS; // Ensure default options are applied
41+
try {
42+
return json_encode($data, $options, $depth);
43+
} catch (JsonException $e) {
44+
throw new Exception('JSON encoding failed: ' . $e->getMessage(), $e->getCode(), $e);
45+
}
46+
}
47+
48+
/**
49+
* Decodes JSON string to PHP data.
50+
*
51+
* @param string $json JSON string to decode
52+
* @param bool $associative Whether to return associative arrays instead of objects
53+
* @param int $depth Maximum decoding depth
54+
* @param int $options JSON decoding options (bitmask)
55+
*
56+
* @return mixed Decoded data
57+
* @throws Exception If decoding fails
58+
*/
59+
public static function decode(string $json, bool $associative = false, int $depth = 512, int $options = 0)
60+
{
61+
$options = $options | self::DEFAULT_DECODE_OPTIONS; // Ensure default options are applied
62+
try {
63+
return json_decode($json, $associative, $depth, $options);
64+
} catch (JsonException $e) {
65+
throw new Exception('JSON decoding failed: ' . $e->getMessage(), $e->getCode(), $e);
66+
}
67+
}
68+
69+
/**
70+
* Checks if a string is valid JSON.
71+
*
72+
* @param string $json String to validate
73+
*
74+
* @return bool True if valid JSON, false otherwise
75+
*/
76+
public static function isValid(string $json): bool
77+
{
78+
try {
79+
json_decode($json, false, 512, JSON_THROW_ON_ERROR);
80+
return true;
81+
} catch (JsonException $e) {
82+
return false;
83+
}
84+
}
85+
86+
/**
87+
* Gets the last JSON error message.
88+
*
89+
* @return string Error message or empty string if no error
90+
*/
91+
public static function getLastError(): string
92+
{
93+
$error = json_last_error();
94+
if ($error === JSON_ERROR_NONE) {
95+
return '';
96+
}
97+
return json_last_error_msg();
98+
}
99+
100+
/**
101+
* Pretty prints JSON data.
102+
*
103+
* @param mixed $data Data to encode
104+
* @param int $additionalOptions Additional options to merge with pretty print
105+
*
106+
* @return string Pretty formatted JSON string
107+
* @throws Exception If encoding fails
108+
*/
109+
public static function prettyPrint($data, int $additionalOptions = 0): string
110+
{
111+
$options = self::DEFAULT_ENCODE_OPTIONS | JSON_PRETTY_PRINT | $additionalOptions;
112+
return self::encode($data, $options);
113+
}
114+
}

tests/EngineTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,9 @@ public function testJson(): void
387387
public function testJsonWithDuplicateDefaultFlags()
388388
{
389389
$engine = new Engine();
390+
$flags = JSON_HEX_TAG | JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
390391
// utf8 emoji
391-
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => '😀'], 201, true, '', JSON_HEX_TAG | JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
392+
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => '😀'], 201, true, '', $flags);
392393
$this->assertEquals('application/json', $engine->response()->headers()['Content-Type']);
393394
$this->assertEquals(201, $engine->response()->status());
394395
$this->assertEquals('{"key1":"value1","key2":"value2","utf8_emoji":"😀"}', $engine->response()->getBody());
@@ -397,7 +398,7 @@ public function testJsonWithDuplicateDefaultFlags()
397398
public function testJsonThrowOnErrorByDefault()
398399
{
399400
$engine = new Engine();
400-
$this->expectException(JsonException::class);
401+
$this->expectException(Exception::class);
401402
$this->expectExceptionMessage('Malformed UTF-8 characters, possibly incorrectly encoded');
402403
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => "\xB1\x31"]);
403404
}

tests/FlightTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ public function testKeepThePreviousStateOfOneViewComponentByDefault(): void
399399
<div>Hi</div>
400400
<input type="number" />
401401
<input type="number" />
402-
html;
402+
html; // phpcs:ignore
403403

404404
$html = str_replace(["\n", "\r"], '', $html);
405405

0 commit comments

Comments
 (0)