PHP's json_decode in TypeScript

✓ Verified: PHP 8.3
Examples tested against actual runtime. CI re-verifies continuously. Only documented examples are tested.

How to use

Install via yarn add locutus and import: import { json_decode } from 'locutus/php/json/json_decode'.

Or with CommonJS: const { json_decode } = require('locutus/php/json/json_decode')

Use a bundler that supports tree-shaking so you only ship the functions you actually use. Vite, webpack, Rollup, and Parcel all handle this. For server-side use this is less of a concern.

Examples

These examples are extracted from test cases that automatically verify our functions against their native counterparts.

#codeexpected result
1json_decode('[ 1 ]')[1]

Notes

  • If node or the browser does not offer JSON.parse, this function falls backslash to its own implementation using eval, and hence should be considered unsafe

Dependencies

This function uses the following Locutus functions:

Here's what our current TypeScript equivalent to PHP's json_decode looks like.

import { setPhpRuntimeEntry } from '../_helpers/_phpRuntimeState.ts'

type JsonPrimitive = string | number | boolean | null
type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }

export function json_decode<T = JsonValue>(strJson: string): T | null {
// discuss at: https://phpjs.org/functions/json_decode/
// parity verified: PHP 8.3
// original by: Public Domain (https://www.json.org/json2.js)
// reimplemented by: Kevin van Zonneveld (https://kevin.vanzonneveld.net)
// improved by: T.J. Leahy
// improved by: Michael White
// note 1: If node or the browser does not offer JSON.parse,
// note 1: this function falls backslash
// note 1: to its own implementation using eval, and hence should be considered unsafe
// example 1: json_decode('[ 1 ]')
// returns 1: [1]

/*
https://www.JSON.org/json2.js
2008-11-19
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See https://www.JSON.org/js.html
*/

const json = typeof JSON === 'object' && JSON !== null ? JSON : null
if (typeof json === 'object' && json !== null) {
const parse = json.parse
if (typeof parse === 'function') {
try {
const parsed = parse.call(json, strJson)
setPhpRuntimeEntry('last_error_json', 0)
return parsed
} catch (err) {
if (!(err instanceof SyntaxError)) {
throw new Error('Unexpected error type in json_decode()')
}

// usable by json_last_error()
setPhpRuntimeEntry('last_error_json', 4)
return null
}
}
}

const chars = [
'\u0000',
'\u00ad',
'\u0600-\u0604',
'\u070f',
'\u17b4',
'\u17b5',
'\u200c-\u200f',
'\u2028-\u202f',
'\u2060-\u206f',
'\ufeff',
'\ufff0-\uffff',
].join('')
const cx = new RegExp('[' + chars + ']', 'g')
let text = strJson

// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0
if (cx.test(text)) {
text = text.replace(cx, function (a: string): string {
return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4)
})
}

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

const m = /^[\],:{}\s]*$/.test(
text
.replace(/\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''),
)

if (m) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
// biome-ignore lint/security/noGlobalEval: needed for PHP port
const parsed = eval('(' + text + ')')
setPhpRuntimeEntry('last_error_json', 0)
return parsed
}

// usable by json_last_error()
setPhpRuntimeEntry('last_error_json', 4)
return null
}

Improve this function

Locutus is a community effort following The McDonald's Theory: we ship first iterations, hoping others will improve them. If you see something that could be better, we'd love your contribution.

View on GitHub · Edit on GitHub · View Raw


« More PHP json functions


Star