Skip to content

Commit 08f322f

Browse files
Refactor JSON state serialization (#777)
* Refactor JSON state serialization * fix: satisfy json codec lint --------- Co-authored-by: homeboy-ci[bot] <266378653+homeboy-ci[bot]@users.noreply.github.com>
1 parent a99b7a3 commit 08f322f

5 files changed

Lines changed: 103 additions & 9 deletions

File tree

inc/Storage/CleanupRunRepository.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77

88
namespace DataMachineCode\Storage;
99

10+
use DataMachineCode\Support\JsonCodec;
11+
1012
defined('ABSPATH') || exit;
1113

14+
if ( ! class_exists(JsonCodec::class) ) {
15+
require_once dirname(__DIR__) . '/Support/JsonCodec.php';
16+
}
17+
1218
class CleanupRunRepository {
1319

1420

@@ -188,13 +194,11 @@ private function decode_item( array $row ): array {
188194
}
189195

190196
private function encode( mixed $value ): string {
191-
$encoded = wp_json_encode($value, JSON_UNESCAPED_SLASHES);
192-
return false === $encoded ? '{}' : $encoded;
197+
return JsonCodec::encode_or_default($value, '{}', JSON_UNESCAPED_SLASHES);
193198
}
194199

195200
private function decode( mixed $value ): array {
196-
$decoded = json_decode( (string) $value, true);
197-
return is_array($decoded) ? $decoded : array();
201+
return JsonCodec::decode_array($value, array());
198202
}
199203

200204
private function new_run_id(): string {

inc/Storage/WorktreeInventoryRepository.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77

88
namespace DataMachineCode\Storage;
99

10+
use DataMachineCode\Support\JsonCodec;
11+
1012
defined('ABSPATH') || exit;
1113

14+
if ( ! class_exists(JsonCodec::class) ) {
15+
require_once dirname(__DIR__) . '/Support/JsonCodec.php';
16+
}
17+
1218
class WorktreeInventoryRepository {
1319

1420

@@ -276,7 +282,7 @@ private function normalize_row( array $row ): array {
276282
'size_bytes' => isset($row['size_bytes']) ? ( null === $row['size_bytes'] ? null : (int) $row['size_bytes'] ) : null,
277283
'cleanup_signal' => $this->cleanup_signal($row, $metadata),
278284
'missing_path' => ! empty($row['missing_path']) ? 1 : 0,
279-
'metadata' => wp_json_encode($metadata),
285+
'metadata' => JsonCodec::encode_or_default($metadata),
280286
'updated_at' => current_time('mysql', true),
281287
);
282288
}
@@ -288,8 +294,7 @@ private function normalize_row( array $row ): array {
288294
* @return array<string,mixed>
289295
*/
290296
private function decode_row( array $row ): array {
291-
$decoded = isset($row['metadata']) && is_string($row['metadata']) ? json_decode($row['metadata'], true) : null;
292-
$row['metadata'] = is_array($decoded) ? $decoded : null;
297+
$row['metadata'] = isset($row['metadata']) && is_string($row['metadata']) ? JsonCodec::decode_array($row['metadata'], null) : null;
293298
foreach ( array( 'id', 'is_primary', 'dirty_count', 'unpushed_count', 'artifact_count', 'artifact_size_bytes', 'size_bytes', 'missing_path' ) as $key ) {
294299
if ( isset($row[ $key ]) ) {
295300
$row[ $key ] = (int) $row[ $key ];

inc/Support/JsonCodec.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
/**
3+
* Shared JSON encoding and decoding helpers.
4+
*
5+
* @package DataMachineCode\Support
6+
*/
7+
8+
namespace DataMachineCode\Support;
9+
10+
defined('ABSPATH') || exit;
11+
12+
class JsonCodec {
13+
14+
/**
15+
* Encode a value as JSON.
16+
*/
17+
public static function encode( mixed $value, int $flags = 0, int $depth = 512 ): ?string {
18+
$encoded = function_exists('wp_json_encode')
19+
? wp_json_encode($value, $flags, $depth)
20+
: json_encode($value, $flags, $depth); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode -- Portable fallback.
21+
22+
return is_string($encoded) ? $encoded : null;
23+
}
24+
25+
/**
26+
* Encode a value as JSON, falling back to a caller-provided default on failure.
27+
*/
28+
public static function encode_or_default( mixed $value, string $fallback = '{}', int $flags = 0, int $depth = 512 ): string {
29+
$encoded = self::encode($value, $flags, $depth);
30+
return null === $encoded ? $fallback : $encoded;
31+
}
32+
33+
/**
34+
* Decode a JSON object/array string, falling back to the supplied default.
35+
*
36+
* @param mixed $value JSON string.
37+
* @param array<mixed>|null $fallback Default returned when decoding fails or the result is not an array.
38+
* @return array<mixed>|null
39+
*/
40+
public static function decode_array( mixed $value, ?array $fallback = array() ): ?array {
41+
$decoded = json_decode( (string) $value, true );
42+
return is_array($decoded) ? $decoded : $fallback;
43+
}
44+
}

inc/Workspace/WorkspaceCleanupPlan.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77

88
namespace DataMachineCode\Workspace;
99

10+
use DataMachineCode\Support\JsonCodec;
11+
1012
defined('ABSPATH') || exit;
1113

14+
if ( ! class_exists(JsonCodec::class) ) {
15+
require_once dirname(__DIR__) . '/Support/JsonCodec.php';
16+
}
17+
1218
if ( ! class_exists(WorktreeCleanupClassifier::class) ) {
1319
require_once __DIR__ . '/WorktreeCleanupClassifier.php';
1420
}
@@ -780,8 +786,8 @@ private function stable_cleanup_row_id( string $type, array $row ): string {
780786
*/
781787
private function stable_cleanup_hash( mixed $value, string $prefix ): string {
782788
$this->ksort_recursive($value);
783-
$encoded = wp_json_encode($value, JSON_UNESCAPED_SLASHES);
784-
if ( false === $encoded || '' === $encoded ) {
789+
$encoded = JsonCodec::encode($value, JSON_UNESCAPED_SLASHES);
790+
if ( null === $encoded || '' === $encoded ) {
785791
$encoded = $prefix . '-json-error-' . json_last_error_msg();
786792
}
787793
return $prefix . '-' . substr(hash('sha256', $encoded), 0, 24);

tests/json-codec.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
if ( ! defined('ABSPATH') ) {
6+
define('ABSPATH', __DIR__ . '/fixtures/');
7+
}
8+
9+
require_once dirname(__DIR__) . '/inc/Support/JsonCodec.php';
10+
11+
use DataMachineCode\Support\JsonCodec;
12+
13+
function assert_same( mixed $expected, mixed $actual, string $message ): void {
14+
if ( $expected !== $actual ) {
15+
throw new RuntimeException(
16+
sprintf(
17+
"%s\nExpected: %s\nActual: %s",
18+
$message,
19+
var_export($expected, true),
20+
var_export($actual, true)
21+
)
22+
);
23+
}
24+
}
25+
26+
assert_same(
27+
'{"path":"foo/bar"}',
28+
JsonCodec::encode_or_default(array( 'path' => 'foo/bar' ), '{}', JSON_UNESCAPED_SLASHES),
29+
'Encodes with caller flags.'
30+
);
31+
assert_same(array( 'ok' => true ), JsonCodec::decode_array('{"ok":true}', array()), 'Decodes JSON arrays.');
32+
assert_same(array(), JsonCodec::decode_array('not-json', array()), 'Defaults invalid JSON to an empty array.');
33+
assert_same(null, JsonCodec::decode_array('not-json', null), 'Preserves nullable defaults.');
34+
35+
print "json-codec OK\n";

0 commit comments

Comments
 (0)