PHP's parse_url in TypeScript

How to use

Install via yarn add locutus and import: import { parse_url } from 'locutus/php/url/parse_url'.

Or with CommonJS: const { parse_url } = require('locutus/php/url/parse_url')

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
1parse_url('https://user:pass@host/path?a=v#a'){scheme: 'https', host: 'host', user: 'user', pass: 'pass', path: '/path', query: 'a=v', fragment: 'a'}
2parse_url('https://en.wikipedia.org/wiki/%22@%22_%28album%29'){scheme: 'https', host: 'en.wikipedia.org', path: '/wiki/%22@%22_%28album%29'}
3parse_url('https://host.domain.tld/a@b.c/folder'){scheme: 'https', host: 'host.domain.tld', path: '/a@b.c/folder'}
4parse_url('https://gooduser:secretpassword@www.example.com/a@b.c/folder?foo=bar'){ scheme: 'https', host: 'www.example.com', path: '/a@b.c/folder', query: 'foo=bar', user: 'gooduser', pass: 'secretpassword' }

Notes

Dependencies

This function uses the following Locutus functions:

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

import { ini_get } from '../info/ini_get.ts'

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
}

export function parse_url(str: string, component?: string): ParseUrlResult | string {
// discuss at: https://locutus.io/php/parse_url/
// original by: Steven Levithan (https://blog.stevenlevithan.com)
// reimplemented by: Brett Zamir (https://brett-zamir.me)
// input by: Lorenzo Pisani
// input by: Tony
// improved by: Brett Zamir (https://brett-zamir.me)
// note 1: original by https://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
// note 1: blog post at https://blog.stevenlevithan.com/archives/parseuri
// note 1: demo at https://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
// note 1: Does not replace invalid characters with '_' as in PHP,
// note 1: nor does it return false with
// note 1: a seriously malformed URL.
// note 1: Besides function name, is essentially the same as parseUri as
// note 1: well as our allowing
// note 1: an extra slash after the scheme/protocol (to allow file:/// as in PHP)
// example 1: parse_url('https://user:pass@host/path?a=v#a')
// returns 1: {scheme: 'https', host: 'host', user: 'user', pass: 'pass', path: '/path', query: 'a=v', fragment: 'a'}
// example 2: parse_url('https://en.wikipedia.org/wiki/%22@%22_%28album%29')
// returns 2: {scheme: 'https', host: 'en.wikipedia.org', path: '/wiki/%22@%22_%28album%29'}
// example 3: parse_url('https://host.domain.tld/a@b.c/folder')
// returns 3: {scheme: 'https', host: 'host.domain.tld', path: '/a@b.c/folder'}
// example 4: parse_url('https://gooduser:secretpassword@www.example.com/a@b.c/folder?foo=bar')
// returns 4: { scheme: 'https', host: 'www.example.com', path: '/a@b.c/folder', query: 'foo=bar', user: 'gooduser', pass: 'secretpassword' }

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',
]

// For loose we added one optional slash to post-scheme to catch file:/// (should restrict this)
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
}

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 url functions


Star