PHP's pathinfo in TypeScript

How to use

Install via yarn add locutus and import: import { pathinfo } from 'locutus/php/filesystem/pathinfo'.

Or with CommonJS: const { pathinfo } = require('locutus/php/filesystem/pathinfo')

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
1pathinfo('/www/htdocs/index.html', 1)'/www/htdocs'
2pathinfo('/www/htdocs/index.html', 'PATHINFO_BASENAME')'index.html'
3pathinfo('/www/htdocs/index.html', 'PATHINFO_EXTENSION')'html'
4pathinfo('/www/htdocs/index.html', 'PATHINFO_FILENAME')'index'
5pathinfo('/www/htdocs/index.html', 2 | 4){basename: 'index.html', extension: 'html'}
6pathinfo('/www/htdocs/index.html', 'PATHINFO_ALL'){dirname: '/www/htdocs', basename: 'index.html', extension: 'html', filename: 'index'}
7pathinfo('/www/htdocs/index.html'){dirname: '/www/htdocs', basename: 'index.html', extension: 'html', filename: 'index'}

Notes

  • Inspired by actual PHP source: php5-5.2.6/ext/standard/string.c line #1559 The way the bitwise arguments are handled allows for greater flexibility & compatability. We might even standardize this code and use a similar approach for other bitwise PHP functions Locutus tries very hard to stay away from a core.js file with global dependencies, because we like that you can just take a couple of functions and be on your way. But by way we implemented this function, if you want you can still declare the PATHINFO_* yourself, and then you can use: pathinfo(‘/www/index.html’, PATHINFO_BASENAME | PATHINFO_EXTENSION); which makes it fully compliant with PHP syntax.

Dependencies

This function uses the following Locutus functions:

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

import { basename } from '../filesystem/basename.ts'

type PathInfoOptionName =
| 'PATHINFO_DIRNAME'
| 'PATHINFO_BASENAME'
| 'PATHINFO_EXTENSION'
| 'PATHINFO_FILENAME'
| 'PATHINFO_ALL'
type PathInfoOptions = number | PathInfoOptionName | PathInfoOptionName[]
type PathInfoMap = {
dirname?: string
basename?: string
extension?: string
filename?: string
}

export function pathinfo(path: string, options?: PathInfoOptions): PathInfoMap | string | false {
// discuss at: https://locutus.io/php/pathinfo/
// original by: Nate
// revised by: Kevin van Zonneveld (https://kvz.io)
// improved by: Brett Zamir (https://brett-zamir.me)
// improved by: Dmitry Gorelenkov
// input by: Timo
// note 1: Inspired by actual PHP source: php5-5.2.6/ext/standard/string.c line #1559
// note 1: The way the bitwise arguments are handled allows for greater flexibility
// note 1: & compatability. We might even standardize this
// note 1: code and use a similar approach for
// note 1: other bitwise PHP functions
// note 1: Locutus tries very hard to stay away from a core.js
// note 1: file with global dependencies, because we like
// note 1: that you can just take a couple of functions and be on your way.
// note 1: But by way we implemented this function,
// note 1: if you want you can still declare the PATHINFO_*
// note 1: yourself, and then you can use:
// note 1: pathinfo('/www/index.html', PATHINFO_BASENAME | PATHINFO_EXTENSION);
// note 1: which makes it fully compliant with PHP syntax.
// example 1: pathinfo('/www/htdocs/index.html', 1)
// returns 1: '/www/htdocs'
// example 2: pathinfo('/www/htdocs/index.html', 'PATHINFO_BASENAME')
// returns 2: 'index.html'
// example 3: pathinfo('/www/htdocs/index.html', 'PATHINFO_EXTENSION')
// returns 3: 'html'
// example 4: pathinfo('/www/htdocs/index.html', 'PATHINFO_FILENAME')
// returns 4: 'index'
// example 5: pathinfo('/www/htdocs/index.html', 2 | 4)
// returns 5: {basename: 'index.html', extension: 'html'}
// example 6: pathinfo('/www/htdocs/index.html', 'PATHINFO_ALL')
// returns 6: {dirname: '/www/htdocs', basename: 'index.html', extension: 'html', filename: 'index'}
// example 7: pathinfo('/www/htdocs/index.html')
// returns 7: {dirname: '/www/htdocs', basename: 'index.html', extension: 'html', filename: 'index'}

const tmpArr: PathInfoMap = {}
let basenameValue: string | null = null
let extensionValue: string | false | null = null
let filenameValue: string | null = null

// Input defaulting & sanitation
if (!path) {
return false
}
const optionsInput = options ?? 'PATHINFO_ALL'

// Initialize binary arguments. Both the string & integer (constant) input is
// allowed
const OPTS: { [key in PathInfoOptionName]: number } = {
PATHINFO_DIRNAME: 1,
PATHINFO_BASENAME: 2,
PATHINFO_EXTENSION: 4,
PATHINFO_FILENAME: 8,
PATHINFO_ALL: 15,
}

let resolvedOptions = 0
if (typeof optionsInput === 'number') {
resolvedOptions = optionsInput
} else {
const optionList = Array.isArray(optionsInput) ? optionsInput : [optionsInput]
for (const option of optionList) {
const flag = OPTS[option]
if (flag) {
resolvedOptions = resolvedOptions | flag
}
}
}

// Internal Functions
const _getExt = function (pathValue: string): string | false {
const str = pathValue + ''
const dotP = str.lastIndexOf('.') + 1
return !dotP ? false : dotP !== str.length ? str.substr(dotP) : ''
}

// Gather path infos
if (resolvedOptions & OPTS.PATHINFO_DIRNAME) {
const dirName = path.replace(/\\/g, '/').replace(/\/[^/]*\/?$/, '') // dirname
tmpArr.dirname = dirName === path ? '.' : dirName
}

if (resolvedOptions & OPTS.PATHINFO_BASENAME) {
if (basenameValue === null) {
basenameValue = basename(path)
}
tmpArr.basename = basenameValue
}

if (resolvedOptions & OPTS.PATHINFO_EXTENSION) {
if (basenameValue === null) {
basenameValue = basename(path)
}
if (extensionValue === null) {
extensionValue = _getExt(basenameValue)
}
if (extensionValue !== false) {
tmpArr.extension = extensionValue
}
}

if (resolvedOptions & OPTS.PATHINFO_FILENAME) {
if (basenameValue === null) {
basenameValue = basename(path)
}
if (extensionValue === null) {
extensionValue = _getExt(basenameValue)
}
if (filenameValue === null) {
filenameValue = basenameValue.slice(
0,
basenameValue.length - (extensionValue ? extensionValue.length + 1 : extensionValue === false ? 0 : 1),
)
}

tmpArr.filename = filenameValue
}

// If array contains only 1 element: return string
const values = Object.values(tmpArr).filter((value): value is string => typeof value === 'string')
if (values.length === 1) {
return values[0] ?? ''
}

// Return full-blown array
return tmpArr
}

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


Star