PHP's var_dump in TypeScript

How to use

Install via yarn add locutus and import: import { var_dump } from 'locutus/php/var/var_dump'.

Or with CommonJS: const { var_dump } = require('locutus/php/var/var_dump')

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
1var_dump(1)'int(1)'
2const simpleCircular = {} simpleCircular.self = simpleCircular var_dump(simpleCircular)'array(1) {\n [self] =>\n Circular Reference Detected\n}\n'

Notes

  • For returning a string, use var_export() with the second argument set to true

Dependencies

This function uses the following Locutus functions:

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

const visitedObjects = new Map<object, true>() // Initialize a map to track visited objects

import { getPhpObjectEntry } from '../_helpers/_phpRuntimeState.ts'
import { type PhpAssoc, type PhpRuntimeValue, toPhpArrayObject } from '../_helpers/_phpTypes.ts'
import { echo } from '../strings/echo.ts'

type DumpValue = PhpRuntimeValue
type DomLikeNode = {
nodeName: string
nodeType?: number
namespaceURI?: string
nodeValue?: DumpValue
}

const hasNodeName = (value: DumpValue): value is DomLikeNode => {
return typeof value === 'object' && value !== null && 'nodeName' in value
}

const isLocutusResource = (
value: DumpValue,
getFuncName: (fn: { toString: () => string }) => string,
): value is { var_dump: () => string } => {
if (typeof value !== 'object' || value === null || !('var_dump' in value) || !('constructor' in value)) {
return false
}
const maybeResource = value
const varDump = getPhpObjectEntry(maybeResource, 'var_dump')
const constructorValue = getPhpObjectEntry(maybeResource, 'constructor')
return (
typeof varDump === 'function' &&
typeof constructorValue === 'function' &&
getFuncName(constructorValue) === 'LOCUTUS_Resource'
)
}

export function var_dump(...args: DumpValue[]): string {
// discuss at: https://locutus.io/php/var_dump/
// original by: Brett Zamir (https://brett-zamir.me)
// improved by: Zahlii
// improved by: Brett Zamir (https://brett-zamir.me)
// note 1: For returning a string, use var_export() with the second argument set to true
// example 1: var_dump(1)
// returns 1: 'int(1)'
// example 2: const simpleCircular = {}
// example 2: simpleCircular.self = simpleCircular
// example 2: var_dump(simpleCircular)
// returns 2: 'array(1) {\n [self] =>\n Circular Reference Detected\n}\n'

let output = ''
const padChar = ' '
const padVal = 4
const _getFuncName = function (fn: { toString: () => string }): string {
const name = /\W*function\s+([\w$]+)\s*\(/.exec(fn.toString())
if (!name) {
return '(Anonymous)'
}
return name[1] ?? '(Anonymous)'
}

const _repeatChar = function (len: number, padCharacter: string): string {
let str = ''
for (let i = 0; i < len; i++) {
str += padCharacter
}
return str
}
const _getInnerVal = function (val: DumpValue, thickPad: string): string {
let ret = ''
if (val === null) {
ret = 'NULL'
} else if (typeof val === 'boolean') {
ret = 'bool(' + val + ')'
} else if (typeof val === 'string') {
ret = 'string(' + val.length + ') "' + val + '"'
} else if (typeof val === 'number') {
if (Number.isInteger(val)) {
ret = 'int(' + val + ')'
} else {
ret = 'float(' + val + ')'
}
} else if (typeof val === 'undefined') {
// The remaining are not PHP behavior because these values
// only exist in this exact form in JavaScript
ret = 'undefined'
} else if (typeof val === 'function') {
const funcLines = val.toString().split('\n')
ret = ''
for (let i = 0, fll = funcLines.length; i < fll; i++) {
const line = funcLines[i] ?? ''
ret += (i !== 0 ? '\n' + thickPad : '') + line
}
} else if (val instanceof Date) {
ret = 'Date(' + val + ')'
} else if (val instanceof RegExp) {
ret = 'RegExp(' + val + ')'
} else if (hasNodeName(val)) {
// Different than PHP's DOMElement
switch (val.nodeType) {
case 1:
if (typeof val.namespaceURI === 'undefined' || val.namespaceURI === 'https://www.w3.org/1999/xhtml') {
// Undefined namespace could be plain XML, but namespaceURI not widely supported
ret = 'HTMLElement("' + val.nodeName + '")'
} else {
ret = 'XML Element("' + val.nodeName + '")'
}
break
case 2:
ret = 'ATTRIBUTE_NODE(' + val.nodeName + ')'
break
case 3:
ret = 'TEXT_NODE(' + val.nodeValue + ')'
break
case 4:
ret = 'CDATA_SECTION_NODE(' + val.nodeValue + ')'
break
case 5:
ret = 'ENTITY_REFERENCE_NODE'
break
case 6:
ret = 'ENTITY_NODE'
break
case 7:
ret = 'PROCESSING_INSTRUCTION_NODE(' + val.nodeName + ':' + val.nodeValue + ')'
break
case 8:
ret = 'COMMENT_NODE(' + val.nodeValue + ')'
break
case 9:
ret = 'DOCUMENT_NODE'
break
case 10:
ret = 'DOCUMENT_TYPE_NODE'
break
case 11:
ret = 'DOCUMENT_FRAGMENT_NODE'
break
case 12:
ret = 'NOTATION_NODE'
break
}
}
return ret
}

const _formatArray = function (
obj: DumpValue,
curDepth: number,
padVal: number,
padChar: string,
visitedObjectMap: Map<object, true>,
): string {
if (curDepth > 0) {
curDepth++
}

const basePad = _repeatChar(padVal * (curDepth - 1), padChar)
const thickPad = _repeatChar(padVal * (curDepth + 1), padChar)
let str = ''
let val = ''

if (typeof obj === 'object' && obj !== null) {
if (visitedObjectMap.has(obj)) {
// Circular reference detected, return a placeholder or a message
return 'Circular Reference Detected\n'
} else {
// Mark this object as visited by adding it to the map
visitedObjectMap.set(obj, true)
}

if (isLocutusResource(obj, _getFuncName)) {
return obj.var_dump()
}
let lgth = 0
const objRecord = toPhpArrayObject<DumpValue>(obj)
for (const someProp in objRecord) {
if (Object.hasOwn(objRecord, someProp)) {
lgth++
}
}
str += 'array(' + lgth + ') {\n'
for (const key in objRecord) {
if (!Object.hasOwn(objRecord, key)) {
continue
}
const objVal = objRecord[key]
if (
typeof objVal === 'object' &&
objVal !== null &&
!(objVal instanceof Date) &&
!(objVal instanceof RegExp) &&
!hasNodeName(objVal)
) {
str += thickPad
str += '['
str += key
str += '] =>\n'
str += thickPad
str += _formatArray(objVal, curDepth + 1, padVal, padChar, visitedObjectMap)
} else {
val = _getInnerVal(objVal, thickPad)
str += thickPad
str += '['
str += key
str += '] =>\n'
str += thickPad
str += val
str += '\n'
}
}
str += basePad + '}\n'
} else {
str = _getInnerVal(obj, thickPad)
}
return str
}

output = _formatArray(args[0], 0, padVal, padChar, visitedObjects)
for (let i = 1; i < args.length; i++) {
output += '\n' + _formatArray(args[i], 0, padVal, padChar, visitedObjects)
}

echo(output)

// Not how PHP does it, but helps us test:
return output
}

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


Star