isset-php
v1.0.7
Published
Safe and simple PHP isset() for JavaScript
Downloads
135
Maintainers
Readme
isset-php
Safe and simple implementation of PHP isset()
for JavaScript.
Installation
This is a zero dependency library that is safe from both ReferenceError: is not defined and TypeError: cannot read property of undefined exceptions.
npm install --save isset-php
Description
TypeScript function signature:
isset(accessor: Function, ...accessors: Function[]): boolean;
Determine if a variable is considered set, this means if a variable is declared and is different than null
or undefined
.
If a variable has been unset with the delete
keyword, it is no longer considered to be set.
isset()
will return false
when checking a variable that has been assigned to null
or undefined
. Also note that a null character ("\0") is not equivalent to the JavaScript null
constant.
If multiple parameters are supplied then isset()
will return true
only if all of the parameters are considered set. Evaluation goes from left to right and stops as soon as an unset variable is encountered.
Parameters
accessor
Accessor function returning the variable to be checked.
accessors
Further accessor functions.
Return Values
Returns true
if all accessors return a value that is not null
or undefined
. false
otherwise.
Examples
Example #1 isset() Examples
const isset = require('isset-php')
let val = ''
// This will evaluate to true so the text will be printed.
if (isset(() => val)) {
console.log('This val is set so I will print.')
}
// Less compact but still viable except when trying to use `this` context
if (isset(function () { return val })) {
console.log('This val is set so I will also print.')
}
In the next examples we'll use console.log()
to output the return value of isset()
.
let a = 'test'
let b = 'anothertest'
console.log(isset(() => a)) // true
console.log(isset(() => a, () => b)) // true
// This might throw a parsing error: Deleting local variable in strict mode
delete a
console.log(isset(() => a)) // false
console.log(isset(() => a, () => b)) // false
let foo = null
console.log(isset(() => foo)) // false
This also work for elements in objects:
let a = { test: 1, hello: null, pie: { a: 'apple' } }
console.log(isset(() => a.test)) // true
console.log(isset(() => a.foo)) // false
console.log(isset(() => a.hello)) // false
// The key 'hello' equals null so is considered unset. If you want to check for
// null key values then try:
console.log(Object.prototype.hasOwnProperty.call(a, 'hello')) // true
// Checking deeper object values
console.log(isset(() => a.pie.a)) // true
console.log(isset(() => a.pie.b)) // false
console.log(isset(() => a.cake.a.b)) // false
Example #2 isset() on String Offsets
const expectedArrayGotString = 'somestring'
console.log(isset(() => expectedArrayGotString.some_key))
console.log(isset(() => expectedArrayGotString[0]))
console.log(isset(() => expectedArrayGotString['0']))
console.log(isset(() => expectedArrayGotString[0.5]))
console.log(isset(() => expectedArrayGotString['0.5']))
console.log(isset(() => expectedArrayGotString['0 Mostel']))
Output of the above example in PHP 5.4 and above:
false
true
true
true <-- PHP casts 0.5 to 0 for string offsets and throws a notice
false
false
Output of the above example in JavaScript:
false
true
true
false <-- JS does not cast 0.5 to 0
false
false
This is the only caveat with the JavaScript port as this behaviour is not emulated.
Explanation
Pulled from StackOverflow.
PHP
Note that in PHP you can reference any variable at any depth - even trying to access a non-array as an array will return a simple true
or false
:
// Referencing an undeclared variable
isset($some); // false
$some = 'hello';
// Declared but has no depth(not an array)
isset($some); // true
isset($some['nested']); // false
$some = ['nested' => 'hello'];
// Declared as an array but not with the depth we're testing for
isset($some['nested']); // true
isset($some['nested']['deeper']); // false
JavaScript
In JavaScript, we don't have that freedom, we'll always get an error if we do the same because JS is immediately attempting to access the value of deeper
before we can wrap it in our isset()
function so...
// Common pitfall answer(ES6 arrow function)
const isset = (ref) => typeof ref !== 'undefined'
// Same as above
function isset (ref) { return typeof ref !== 'undefined' }
// Referencing an undeclared variable will throw an error, so no luck here
isset(some) // Error: some is not defined
// Defining a simple object with no properties - so we aren't defining
// the property `nested`
let some = {}
// Simple checking if we have a declared variable
isset(some) // true
// Now trying to see if we have a top level property, still valid
isset(some.nested) // false
// But here is where things fall apart: trying to access a deep property
// of a complex object; it will throw an error
isset(some.nested.deeper) // Error: Cannot read property 'deeper' of undefined
// ^^^^^^ undefined
More failing alternatives:
// Any way we attempt to access the `deeper` property of `nested` will
// throw an error
some.nested.deeper.hasOwnProperty('value') // Error
// ^^^^^^ undefined
// Similar to the above but safe from objects overriding `hasOwnProperty`
Object.prototype.hasOwnProperty.call(some.nested.deeper, 'value') // Error
// ^^^^^^ undefined
// Same goes for typeof
typeof some.nested.deeper !== 'undefined' // Error
// ^^^^^^ undefined
And some working alternatives that can get redundant fast:
// Wrap everything in try...catch
try {
if (isset(some.nested.deeper)) {
// ...
}
} catch (e) {}
try {
if (some.nested.deeper !== undefined && some.nested.deeper !== null) {
// ...
}
} catch (e) {}
// Or by chaining all of the isset which can get long
isset(some) && isset(some.nested) && isset(some.nested.deeper) // false
// ^^^^^^ returns false so the next isset() is never run