PHP's is_array in TypeScript

How to use

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

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

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
1is_array(['Kevin', 'van', 'Zonneveld'])true
2is_array('Kevin van Zonneveld')false
3is_array({0: 'Kevin', 1: 'van', 2: 'Zonneveld'})true
4ini_set('locutus.objectsAsArrays', 0) is_array({0: 'Kevin', 1: 'van', 2: 'Zonneveld'})false
5is_array(function tmp_a (){ this.name = 'Kevin' })false

Notes

  • In Locutus, javascript objects are like php associative arrays, thus JavaScript objects will also return true in this function (except for objects which inherit properties, being thus used as objects), unless you do ini_set(‘locutus.objectsAsArrays’, 0), in which case only genuine JavaScript arrays will return true

Dependencies

This function uses the following Locutus functions:

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

import { getPhpObjectEntry } from '../_helpers/_phpRuntimeState.ts'
import type { PhpAssoc, PhpRuntimeValue } from '../_helpers/_phpTypes.ts'
import { ini_get } from '../info/ini_get.ts'

type IsArrayValue = PhpRuntimeValue
type ArrayLikeAssoc = PhpAssoc<IsArrayValue> & { length: number }

const hasNumericLength = (value: IsArrayValue): value is ArrayLikeAssoc =>
value !== null && typeof value === 'object' && typeof getPhpObjectEntry(value, 'length') === 'number'

export function is_array(mixedVar: IsArrayValue): boolean {
// discuss at: https://locutus.io/php/is_array/
// original by: Kevin van Zonneveld (https://kvz.io)
// improved by: Legaev Andrey
// improved by: Onno Marsman (https://twitter.com/onnomarsman)
// improved by: Brett Zamir (https://brett-zamir.me)
// improved by: Nathan Sepulveda
// improved by: Brett Zamir (https://brett-zamir.me)
// bugfixed by: Cord
// bugfixed by: Manish
// bugfixed by: Brett Zamir (https://brett-zamir.me)
// note 1: In Locutus, javascript objects are like php associative arrays,
// note 1: thus JavaScript objects will also
// note 1: return true in this function (except for objects which inherit properties,
// note 1: being thus used as objects),
// note 1: unless you do ini_set('locutus.objectsAsArrays', 0),
// note 1: in which case only genuine JavaScript arrays
// note 1: will return true
// example 1: is_array(['Kevin', 'van', 'Zonneveld'])
// returns 1: true
// example 2: is_array('Kevin van Zonneveld')
// returns 2: false
// example 3: is_array({0: 'Kevin', 1: 'van', 2: 'Zonneveld'})
// returns 3: true
// example 4: ini_set('locutus.objectsAsArrays', 0)
// example 4: is_array({0: 'Kevin', 1: 'van', 2: 'Zonneveld'})
// returns 4: false
// example 5: is_array(function tmp_a (){ this.name = 'Kevin' })
// returns 5: false

const _getFuncName = function (fn: IsArrayValue): string {
const name = /\W*function\s+([\w$]+)\s*\(/.exec(String(fn))
if (!name) {
return '(Anonymous)'
}
return name[1] ?? '(Anonymous)'
}
const _isArray = function (mixedVar: IsArrayValue): boolean {
// return Array.isArray(mixedVar);
// The above works, but let's do the even more stringent approach:
// (since Object.prototype.toString could be overridden)
// Null, Not an object, no length property so couldn't be an Array (or String)
if (!hasNumericLength(mixedVar)) {
return false
}
const candidate = mixedVar
const len = candidate.length
candidate[candidate.length] = 'bogus'
// The only way I can think of to get around this (or where there would be trouble)
// would be to have an object defined
// with a custom "length" getter which changed behavior on each call
// (or a setter to mess up the following below) or a custom
// setter for numeric properties, but even that would need to listen for
// specific indexes; but there should be no false negatives
// and such a false positive would need to rely on later JavaScript
// innovations like __defineSetter__
if (len !== candidate.length) {
// We know it's an array since length auto-changed with the addition of a
// numeric property at its length end, so safely get rid of our bogus element
candidate.length -= 1
return true
}
// Get rid of the property we added onto a non-array object; only possible
// side-effect is if the user adds back the property later, it will iterate
// this property in the older order placement in IE (an order which should not
// be depended on anyways)
delete candidate[candidate.length]
return false
}

if (!mixedVar || typeof mixedVar !== 'object') {
return false
}

const isArray = _isArray(mixedVar)

if (isArray) {
return true
}

const iniVal = ini_get('locutus.objectsAsArrays') || 'on'
if (iniVal === 'on') {
const asString = Object.prototype.toString.call(mixedVar)
const asFunc = _getFuncName(getPhpObjectEntry(mixedVar, 'constructor'))

if (asString === '[object Object]' && asFunc === 'Object') {
// Most likely a literal and intended as assoc. array
return true
}
}

return false
}

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