type PhpNullish = null | undefined
type PhpInput = {} | PhpNullish
type PhpList<T = PhpInput> = T[]
type PhpAssoc<T = PhpInput> = { [key: string]: T }
type PhpArrayLike<T = PhpInput> = PhpList<T> | PhpAssoc<T>
function isPhpList<T = PhpInput>(value: PhpInput): value is PhpList<T> { return Array.isArray(value) }
function isObjectLike(value: PhpInput): value is PhpArrayLike<PhpInput> { return typeof value === 'object' && value !== null }
function isPhpAssocObject<T = PhpInput>(value: PhpInput): value is PhpAssoc<T> { return isObjectLike(value) && !isPhpList(value) }
interface IniEntry { local_value?: PhpInput }
type LocaleEntry = PhpAssoc<PhpInput> & { sorting?: (left: PhpInput, right: PhpInput) => number }
type LocaleCategoryMap = PhpAssoc<string | undefined>
interface LocutusRuntimeContainer { php?: PhpAssoc<PhpInput> }
type GlobalWithLocutus = { $locutus?: LocutusRuntimeContainer [key: string]: PhpInput }
interface PhpRuntimeState { ini: PhpAssoc<IniEntry | undefined> locales: PhpAssoc<LocaleEntry | undefined> localeCategories: LocaleCategoryMap pointers: PhpList<PhpInput> locale_default: string | undefined }
const isIniBag = (value: PhpInput): value is PhpAssoc<IniEntry | undefined> => isPhpAssocObject<IniEntry | undefined>(value)
const isLocaleBag = (value: PhpInput): value is PhpAssoc<LocaleEntry | undefined> => isPhpAssocObject<LocaleEntry | undefined>(value)
const isLocaleCategoryBag = (value: PhpInput): value is LocaleCategoryMap => isPhpAssocObject<string | undefined>(value)
const globalContext: GlobalWithLocutus = typeof window === 'object' && window !== null ? window : typeof global === 'object' && global !== null ? global : {}
const ensurePhpRuntimeObject = (): PhpAssoc<PhpInput> => { let locutus = globalContext.$locutus if (typeof locutus !== 'object' || locutus === null) { locutus = {} globalContext.$locutus = locutus }
let php = locutus.php if (typeof php !== 'object' || php === null) { php = {} locutus.php = php }
return php }
function ensurePhpRuntimeState(): PhpRuntimeState { const php = ensurePhpRuntimeObject() const iniValue = php.ini const localesValue = php.locales const localeCategoriesValue = php.localeCategories const pointersValue = php.pointers
const ini = isIniBag(iniValue) ? iniValue : {} const locales = isLocaleBag(localesValue) ? localesValue : {} const localeCategories = isLocaleCategoryBag(localeCategoriesValue) ? localeCategoriesValue : {} const pointers: PhpList<PhpInput> = Array.isArray(pointersValue) ? pointersValue : []
if (iniValue !== ini) { php.ini = ini } if (localesValue !== locales) { php.locales = locales } if (localeCategoriesValue !== localeCategories) { php.localeCategories = localeCategories } if (pointersValue !== pointers) { php.pointers = pointers }
const localeDefaultValue = php.locale_default const localeDefault = typeof localeDefaultValue === 'string' ? localeDefaultValue : undefined
return { ini, locales, localeCategories, pointers, locale_default: localeDefault, } }
function ini_get(varname: string): string {
const runtime = ensurePhpRuntimeState() const entry = runtime.ini[varname]
if (entry && entry.local_value !== undefined) { if (entry.local_value === null) { return '' } return String(entry.local_value) }
return '' }
type ParseUrlQueryMap = Record<string, string>
type ParseUrlValue = string | ParseUrlQueryMap
type ParseUrlResult = { source?: string scheme?: string authority?: string userInfo?: string user?: string pass?: string host?: string port?: string relative?: string path?: string directory?: string file?: string query?: string fragment?: string [key: string]: ParseUrlValue | undefined }
function parse_url(str: string, component?: string): ParseUrlResult | string {
let query: string
const mode = ini_get('locutus.parse_url.mode') || 'php'
const key = [ 'source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment', ]
const parser = { php: new RegExp( [ '(?:([^:\\/?#]+):)?', '(?:\\/\\/()(?:(?:()(?:([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?))?', '()', '(?:(()(?:(?:[^?#\\/]*\\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)', ].join(''), ), strict: new RegExp( [ '(?:([^:\\/?#]+):)?', '(?:\\/\\/((?:(([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?))?', '((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)', ].join(''), ), loose: new RegExp( [ '(?:(?![^:@]+:[^:@\\/]*@)([^:\\/?#.]+):)?', '(?:\\/\\/\\/?)?', '((?:(([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?)', '(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))', '(?:\\?([^#]*))?(?:#(.*))?)', ].join(''), ), }
const parserMode = mode === 'php' || mode === 'strict' || mode === 'loose' ? mode : 'php' const selectedParser = parser[parserMode] const m = selectedParser.exec(str) const uri: ParseUrlResult = {} if (!m) { return uri } let i = 14
while (i--) { const keyName = key[i] if (keyName && m[i]) { uri[keyName] = m[i] } }
if (component) { return String(uri[component.replace('PHP_URL_', '').toLowerCase()] ?? '') }
if (mode !== 'php') { const name = ini_get('locutus.parse_url.queryKey') || 'queryKey' const queryParser = /(?:^|&)([^&=]*)=?([^&]*)/g const queryObj: ParseUrlQueryMap = {} uri[name] = queryObj const queryKeyName = key[12] query = String((queryKeyName ? uri[queryKeyName] : '') || '') query.replace(queryParser, function ($0: string, $1: string, $2: string): string { if ($1) { queryObj[$1] = $2 } return $0 }) }
delete uri.source return uri }
|