UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

159 lines (158 loc) 6.48 kB
import { getPhpGlobalScope } from "../_helpers/_phpRuntimeState.js"; import { isPhpAssocObject } from "../_helpers/_phpTypes.js"; const DANGEROUS_PARSE_STR_KEYS = new Set(['__proto__', 'constructor', 'prototype']); function isDangerousParseStrKey(key) { return DANGEROUS_PARSE_STR_KEYS.has(key); } export function parse_str(str, array) { // discuss at: https://locutus.io/php/parse_str/ // original by: Cagri Ekin // improved by: Michael White (https://getsprink.com) // improved by: Jack // improved by: Brett Zamir (https://brett-zamir.me) // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) // bugfixed by: Brett Zamir (https://brett-zamir.me) // bugfixed by: stag019 // bugfixed by: Brett Zamir (https://brett-zamir.me) // bugfixed by: MIO_KODUKI (https://mio-koduki.blogspot.com/) // reimplemented by: stag019 // input by: Dreamer // input by: Zaide (https://zaidesthings.com/) // input by: David Pesta (https://davidpesta.com/) // input by: jeicquest // bugfixed by: Rafał Kukawski // note 1: When no argument is specified, will put variables in global scope. // note 1: When a particular argument has been passed, and the // note 1: returned value is different parse_str of PHP. // note 1: For example, a=b=c&d====c // example 1: var $arr = {} // example 1: parse_str('first=foo&second=bar', $arr) // example 1: var $result = $arr // returns 1: { first: 'foo', second: 'bar' } // example 2: var $arr = {} // example 2: parse_str('str_a=Jack+and+Jill+didn%27t+see+the+well.', $arr) // example 2: var $result = $arr // returns 2: { str_a: "Jack and Jill didn't see the well." } // example 3: var $abc = {3:'a'} // example 3: parse_str('a[b]["c"]=def&a[q]=t+5', $abc) // example 3: var $result = $abc // returns 3: {"3":"a","a":{"b":{"c":"def"},"q":"t 5"}} // example 4: var $arr = {} // example 4: parse_str('a[][]=value', $arr) // example 4: var $result = $arr // returns 4: {"a":{"0":{"0":"value"}}} // example 5: var $arr = {} // example 5: parse_str('a=1&a[]=2', $arr) // example 5: var $result = $arr // returns 5: {"a":{"0":"2"}} const strArr = String(str).replace(/^&/, '').replace(/&$/, '').split('&'); const sal = strArr.length; let i = 0; let j = 0; let ct = 0; let lastObj = {}; let obj = {}; let chr; let tmp = []; let key = ''; let value = ''; let postLeftBracketPos = 0; let keys = []; let keysLen = 0; const _fixStr = function (str) { return decodeURIComponent(str.replace(/\+/g, '%20')); }; const target = array || getPhpGlobalScope(); for (i = 0; i < sal; i++) { tmp = (strArr[i] ?? '').split('='); key = _fixStr(tmp[0] ?? ''); value = tmp.length < 2 ? '' : _fixStr(tmp[1] ?? ''); while (key.charAt(0) === ' ') { key = key.slice(1); } const nullByteIndex = key.indexOf('\x00'); if (nullByteIndex > -1) { key = key.slice(0, nullByteIndex); } if (key && key.charAt(0) !== '[') { keys = []; postLeftBracketPos = 0; for (j = 0; j < key.length; j++) { if (key.charAt(j) === '[' && !postLeftBracketPos) { postLeftBracketPos = j + 1; } else if (key.charAt(j) === ']') { if (postLeftBracketPos) { if (!keys.length) { keys.push(key.slice(0, postLeftBracketPos - 1)); } keys.push(key.substr(postLeftBracketPos, j - postLeftBracketPos)); postLeftBracketPos = 0; if (key.charAt(j + 1) !== '[') { break; } } } } if (!keys.length) { keys = [key]; } let primaryKey = keys[0] ?? ''; for (j = 0; j < primaryKey.length; j++) { chr = primaryKey.charAt(j); if (chr === ' ' || chr === '.' || chr === '[') { primaryKey = primaryKey.substring(0, j) + '_' + primaryKey.substring(j + 1); } if (chr === '[') { break; } } keys[0] = primaryKey; let hasDangerousKey = false; for (const rawKey of keys) { const normalizedKey = rawKey.replace(/^['"]/, '').replace(/['"]$/, ''); if (isDangerousParseStrKey(normalizedKey)) { hasDangerousKey = true; break; } } if (hasDangerousKey) { continue; } obj = target; let skipAssignment = false; for (j = 0, keysLen = keys.length; j < keysLen; j++) { key = (keys[j] ?? '').replace(/^['"]/, '').replace(/['"]$/, ''); lastObj = obj; if ((key === '' || key === ' ') && j !== 0) { // Insert new dimension ct = -1; for (const objKey of Object.keys(obj)) { if (+objKey > ct && /^\d+$/.test(objKey)) { ct = +objKey; } } key = String(ct + 1); } if (isDangerousParseStrKey(key)) { skipAssignment = true; break; } // if primitive value, replace with object const current = obj[key]; if (!isPhpAssocObject(current)) { obj[key] = {}; } const next = obj[key]; if (!isPhpAssocObject(next)) { break; } obj = next; } if (skipAssignment || isDangerousParseStrKey(key)) { continue; } lastObj[key] = value; } } }