Skip to content

Commit 7b08d35

Browse files
fix: resolve UTF-8 string parsing ''String too short'' error in PHP serializer
- Fix string parsing logic to properly handle UTF-8 byte-to-character conversion - Update unserializeValue() to count actual UTF-8 bytes using TextEncoder - Add comprehensive test case for the exact problematic serialized string - Ensures PHP''s byte-length-based serialization format works with multi-byte characters Fixes issue where complex serialized strings with UTF-8 characters like ''测试 café 🚀'' would fail with ''String too short'' error during unserialization. Co-authored-by: Nadim Tuhin <nadimtuhin@users.noreply.github.com>
1 parent 2f7bc57 commit 7b08d35

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

src/components/PhpSerializer.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,5 +482,32 @@ describe('PhpSerializer', () => {
482482
expect(phpOutput.value).toContain('s:2:"id";i:123');
483483
expect(phpOutput.value).toContain('s:4:"name";s:8:"John Doe"');
484484
});
485+
486+
test('handles UTF-8 strings in complex serialized data without "String too short" error', () => {
487+
const phpInput = screen.getAllByRole('textbox')[0];
488+
const phpOutput = screen.getAllByRole('textbox')[1];
489+
490+
// The exact serialized string that was causing the "String too short" error
491+
const problematicSerializedString = `a:5:{s:4:"user";O:4:"User":1:{s:1:"0";a:8:{s:2:"id";i:123;s:4:"name";s:8:"John Doe";s:5:"email";s:16:"john@example.com";s:6:"active";b:1;s:7:"balance";d:99.99;s:11:"preferences";N;s:4:"tags";a:2:{i:0;s:9:"developer";i:1;s:5:"admin";}s:8:"metadata";a:3:{s:10:"created_at";s:10:"2024-01-15";s:10:"last_login";s:10:"2024-07-16";s:11:"login_count";i:42;}}}s:8:"settings";a:3:{s:5:"theme";s:4:"dark";s:13:"notifications";b:1;s:8:"language";s:5:"en-US";}s:5:"items";a:3:{i:0;s:5:"item1";i:1;s:5:"item2";i:2;s:5:"item3";}s:15:"special_numbers";a:3:{s:8:"infinity";d:INF;s:17:"negative_infinity";d:-INF;s:12:"not_a_number";d:NAN;}s:9:"utf8_test";s:17:"测试 café 🚀";}`;
492+
493+
// This should not throw "String too short" error anymore
494+
fireEvent.change(phpOutput, { target: { value: problematicSerializedString } });
495+
496+
// Verify no error is displayed
497+
expect(screen.queryByText(/Error:/)).not.toBeInTheDocument();
498+
expect(screen.queryByText(/String too short/)).not.toBeInTheDocument();
499+
500+
// Verify the UTF-8 string is correctly parsed
501+
expect(phpInput.value).toContain("'utf8_test' => '测试 café 🚀'");
502+
503+
// Verify other complex elements are parsed correctly
504+
expect(phpInput.value).toContain('new User([');
505+
expect(phpInput.value).toContain("'name' => 'John Doe'");
506+
expect(phpInput.value).toContain("'email' => 'john@example.com'");
507+
expect(phpInput.value).toContain("'special_numbers' => [");
508+
expect(phpInput.value).toContain("'infinity' => INF");
509+
expect(phpInput.value).toContain("'negative_infinity' => -INF");
510+
expect(phpInput.value).toContain("'not_a_number' => NAN");
511+
});
485512
});
486513
});

src/components/PhpSerializer.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,11 +416,36 @@ const PhpSerializer = () => {
416416
if (colonPos === -1) throw new Error('Invalid string format');
417417
const lengthStr = input.slice(2, colonPos);
418418
if (!/^\d+$/.test(lengthStr)) throw new Error('Invalid string length');
419-
const length = parseInt(lengthStr, 10);
419+
const byteLength = parseInt(lengthStr, 10);
420420

421421
if (input[colonPos + 1] !== '"') throw new Error('String must start with quote');
422422
const startPos = colonPos + 2;
423-
const endPos = startPos + length;
423+
424+
// Find the actual end position by counting bytes, not characters
425+
// Since JavaScript strings are UTF-16, we need to convert to bytes and count
426+
const remainingInput = input.slice(startPos);
427+
const encoder = new TextEncoder();
428+
let charIndex = 0;
429+
let currentByteLength = 0;
430+
431+
// Find where we reach the target byte length
432+
while (currentByteLength < byteLength && charIndex < remainingInput.length) {
433+
const char = remainingInput[charIndex];
434+
const charBytes = encoder.encode(char).length;
435+
if (currentByteLength + charBytes <= byteLength) {
436+
currentByteLength += charBytes;
437+
charIndex++;
438+
} else {
439+
break;
440+
}
441+
}
442+
443+
// Check if we have the exact byte length
444+
if (currentByteLength !== byteLength) {
445+
throw new Error('String byte length mismatch');
446+
}
447+
448+
const endPos = startPos + charIndex;
424449

425450
if (input.length < endPos + 2) throw new Error('String too short');
426451
if (input[endPos] !== '"' || input[endPos + 1] !== ';') {

0 commit comments

Comments
 (0)