Use PHP functions in JavaScript

JavaScript is_array

Returns true if variable is an array

1
2
3
4
56
7
8
9
1011
12
13
14
1516
17
18
19
2021
22
23
24
2526
27
28
29
3031
32
33
34
3536
37
38
39
4041
42
43
44
4546
47
48
49
5051
52
53
54
5556
57
58
59
6061
62
63
64
6566
67
68
69
7071
72
73
74
7576
77
78
79
80
function is_array (mixed_var) {
    // Returns true if variable is an array  
    // 
    // version: 1109.2015
    // discuss at: http://phpjs.org/functions/is_array    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Legaev Andrey
    // +   bugfixed by: Cord
    // +   bugfixed by: Manish
    // +   improved by: Onno Marsman    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Nathan Sepulveda
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: In php.js, javascript objects are like php associative arrays, thus JavaScript objects will also    // %        note 1: return true in this function (except for objects which inherit properties, being thus used as objects),
    // %        note 1: unless you do ini_set('phpjs.objectsAsArrays', 0), 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: is_array(function tmp_a(){this.name = 'Kevin'});    // *     returns 4: false
    var ini,
        _getFuncName = function (fn) {
            var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
            if (!name) {                return '(Anonymous)';
            }
            return name[1];
        },
        _isArray = function (mixed_var) {            // return Object.prototype.toString.call(mixed_var) === '[object Array]';
            // 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 (!mixed_var || typeof mixed_var !== 'object' || typeof mixed_var.length !== 'number') {
                return false;            }
            var len = mixed_var.length;
            mixed_var[mixed_var.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 !== mixed_var.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
                mixed_var.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 mixed_var[mixed_var.length];
            return false;
        };
     if (!mixed_var || typeof mixed_var !== 'object') {
        return false;
    }
 
    // BEGIN REDUNDANT    this.php_js = this.php_js || {};
    this.php_js.ini = this.php_js.ini || {};
    // END REDUNDANT
    
    ini = this.php_js.ini['phpjs.objectsAsArrays']; 
    return _isArray(mixed_var) ||
        // Allow returning true unless user has called
        // ini_set('phpjs.objectsAsArrays', 0) to disallow objects as arrays
        ((!ini || ( // if it's not set to 0 and it's not 'off', check for objects as arrays        (parseInt(ini.local_value, 10) !== 0 && (!ini.local_value.toLowerCase || ini.local_value.toLowerCase() !== 'off')))
        ) && (
        Object.prototype.toString.call(mixed_var) === '[object Object]' && _getFuncName(mixed_var.constructor) === 'Object' // Most likely a literal and intended as assoc. array
        ));
}
external links: original PHP docs | raw js source

Examples

» Example 1

Running

1
is_array(['Kevin', 'van', 'Zonneveld']);

Should return

1
true

» Example 2

Running

1
is_array('Kevin van Zonneveld');

Should return

1
false

Dependencies

No dependencies, you can use this function standalone.

Open syntax issues

php.js uses JsLint to help us keep our code consistent and prevent some common bugs.

Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.


Authors

Thanks to the following developers, you get to have is_array goodness in JavaScript.

Comments

Add Comment
Use:
[CODE]
your_stuff('here');
[/CODE]
for proper code formatting
By submitting code here you are allowing us to use it in php.js hence dual licensing it under the MIT and GPL licenses

Gravatar
Lcf.vs
2 Jul '11 Permalink

q  Me>

var test;
test=[]; // true
test=[[]]; // true
test=[new Array()]; // true
test=new Array(); // true
test=new Array([]); // true
test=new Array(new Array()); // true
test[1]=3; // true
test={}; //false
test[0]=1; //false



What else?

Gravatar
Me
2 Jul '11 Permalink

q  @Lcf.vs: You can find an answer to your question here http://phpjs.org/functions/is_array:437#comment_112008

Gravatar
Lcf.vs
2 Jul '11 Permalink

q  Why complicate?

function isArray(supposedArray){
	return supposedArray instanceof Array;
}

Gravatar
Nathan Sepulveda
16 Mar '11 Permalink

q  @Brett: Thanks!

Gravatar
Brett Zamir
15 Mar '11 Permalink

q  @Nathan (Sepulveda): Fixed in Git. Thanks!

Gravatar
Nathan Sepulveda
14 Mar '11 Permalink

q  I was wondering if you could put my last name on the credits for this function, I only say this because the name "Nathan" links to two different contributions, but I have only contributed on this function, and not to the htmlspecialchars function and I would like to make that distinction. Thanks.

Gravatar
Brett Zamir
2 Jan '11 Permalink

q  @abdelrahman salem: That's a good one, unless you pass in a string constructed by a String constructor:

is_array(new String('abc')); // true



(or if you override propertyIsEnumerable)

There seem to be no 100% perfect solutions for this one...

Gravatar
abdelrahman salem
2 Jan '11 Permalink

q  


function is_array(inputArray) {   	
    return inputArray && !(inputArray.propertyIsEnumerable('length')) && typeof inputArray === 'object' && typeof inputArray.length === 'number';
}

Gravatar
Brett Zamir
30 Nov '10 Permalink

q  @Nathan: I adapted your code a bit and added to Git, thanks for the patches. We still want to keep the INI settings so that people can force this function to disallow objects from being considered as arrays. I also kept part of the old way in the comments because the old way was also valid and even harder to get a false result, but I agree your solution is simpler.

Gravatar
Nathan
24 Nov '10 Permalink

q  Sorry for the reposts, feel free to remove the other one below, I didn't know the code would be hidden, and I forgot to remove a this:

function is_array (mixed_var)
{
    var getFuncName = function (fn) {
        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
        if (!name) {
            return '(Anonymous)';
        }        return name[1];
    };
 
    if (!mixed_var) {
        return false;
    }

    if(Object.prototype.toString.call(mixed_var) === "[object Array]")
    {
        return true;
    }
    else if(Object.prototype.toString.call(mixed_var) === "[object Object]" && 
                    getFuncName(mixed_var.constructor) === "Object")
    {
        return true;
    }
    
    return false;
}

Gravatar
Nathan
24 Nov '10 Permalink

q  REVISION:

function is_array (mixed_var)
{
    var getFuncName = function (fn) {
        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
        if (!name) {
            return '(Anonymous)';
        }        return name[1];
    };
 
    if (!mixed_var) {
        return false;
    }

    if(Object.prototype.toString.call(mixed_var) === "[object Array]")
    {
        return true;
    }
    else if(Object.prototype.toString.call(mixed_var) === "[object Object]" && this.getFuncName(mixed_var.constructor) === "Object")
    {
        return true;
    }
    
    return false;
}

Gravatar
Nathan
24 Nov '10 Permalink

q  @Brett: I posted some code earlier and it hasn't shown up, I used the constructor and was able to shorten the above function to only a few lines:

if(Object.prototype.toString.call(mixed_var) === "[object Array]")
        {
            return true;
        }
        else if(Object.prototype.toString.call(mixed_var) === "[object Object]")
        {
            if(getFuncName(mixed_var.constructor) === "Object")
            {
                return true;
            }
        }
        
        return false;


(of course, I need to add the getFuncName function, but you already have it)

Gravatar
Brett Zamir
24 Nov '10 Permalink

q  @Nathan: Pretty much, yes (by default). But this should probably be checking the constructor property instead to see whether it is a regular function or the built-in object constructor, since the current approach would allow some non-inheriting objects created with "new" to be considered as associative arrays (though maybe some would like it that way).

Another issue with associative arrays is ECMAScript's annoyingly unpredictable iteration order, so I'm hoping we can allow array() to allows objects to be built something like this, with multiple objects passed as arguments to preserve order: var arr = array({key1:'val1'}, {key2:'val2'});.

Yet another related issue is how IE doesn't really delete properties, as it remembers their iteration sequence, making it impossible for us to sort arrays in place (thus requiring us to deviate from PHP and return a copy of the array). The proposal I mentioned would deal with this problem too, albeit in a regrettably but necessarily ugly way.

Gravatar
Nathan
22 Nov '10 Permalink

q  @Zamir: I can see how instanceof would have have issues with a user defining their own Array object. As for frames, they just shouldn't be used, but, seeing as they are, and that they are available, one must program for them. As for associative arrays, according the function above, are we assuming that all literal objects (meaning "var myObj = {};") are considered assoc arrays, and that all non-literal objects (such as functions and javascript psuedo-classes (via the prototype) are to be then considered objects? The only exception here of course being the Array class. This makes sense to me, I just have never programmed for assoc arrays in javascript because I really just have never had the time, and now that I do, here I am.

Gravatar
Brett Zamir
22 Nov '10 Permalink

q  @Nathan: Although I can't see Andrey's comment here, what you cite is not supposed to work across frames because "Array" will be a different constructor on another frame: http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ (also if someone defined their own Array: "var Array = function () {}"). Also, in our case, we may treat objects as arrays, since we allow them potentially as associative arrays.

Gravatar
Nathan
21 Nov '10 Permalink

q  I agree with Andrey, I cannot find a single instance where:

function is_array(mixed_var)
{
    return mixed_var instanceof Array;
}



does not work.

Gravatar
Nathan
21 Nov '10 Permalink

q  I agree with Andrey, I cannot find a single instance where:

function is_array(mixed_var)
{

}

Gravatar
Brett Zamir
11 May '10 Permalink

q  @Mathias Bynens: php.js, in order to support use of object literals as associative arrays (to the extent possible), considers these as "arrays" (while attempting to exclude objects which have inherited properties and thus less likely to have been intended as an associative array). We also allow configuration for people who do not want to treat objects as arrays.

Gravatar
Mathias Bynens
10 May '10 Permalink

q  Wow, WTF!

What’s wrong with:

function isArray(obj) {
 return '[object Array]' === Object.prototype.toString.call(obj);
};

Gravatar
Brett Zamir
12 May '09 Permalink

q  Sorry, I neglected to add the "phpjs." prefix in a few cases. Fixed now in SVN.

Gravatar
Kevin van Zonneveld
11 May '09 Permalink

q  Great work Brett, I Gotta say!

Gravatar
Brett Zamir
10 May '09 Permalink

q  FYI, I've just added the ability (function in SVN) for you to set the ini (e.g., ini_set('phpjs.objectsAsArrays', 0);) to establish strict type checking for arrays (not any kind of object). However, the default behavior still treats objects as arrays (unless they are objects which inherit properties).

Gravatar
Brett Zamir
30 Apr '09 Permalink

q  Looks like we need one more check:

&& a.constructor !== String

since the rare

var str = new String();

form would otherwise qualify.

Actually if the same frame problem with Array occurs (presumably due to Array constructor being of a different identity), I've wondered why:

!a.constructor || a.constructor.name !== 'Array'

or in our case here:

!a.constructor || a.constructor.name !== 'String'

couldn't do the trick regardless of frames...

If this wouldn't work, we could use the same trick to check for methods that are only built-in on String (as opposed to Array).

Brett

Gravatar
Brett Zamir
30 Apr '09 Permalink

q  You know, unless I'm just getting dizzy from this topic, I think I may have found (or refound?) a foolproof way to establish that something is an array, and only an array (i.e., for strict checking mode).

typeof a === 'object' && a.hasOwnProperty('length') && !a.propertyIsEnumerable('length')

The first test excludes strings, the 2nd test excludes objects with a length property added further up the prototype chain, while the 3rd test excludes objects with a length property added directly on an instance, all while catching arrays. Am I missing something? I don't think there are any other built-in types with a length property...

Brett

Gravatar
Brett Zamir
30 Apr '09 Permalink

q  @KELAN, in the same line as Kevin's question, see also the discussion I had earlier with Luke here. I think my own proposed check for both splice and concat, etc. should be pretty safe, though feedback is welcome.

@Mk. Keck, sorry for the late reply. Rather than overload the arguments to the functions (to which PHP itself might add additional arguments in the future which could conflict with any we add), the approach we are taking for customizability is the same as for PHP: use ini, and to distinguish our own specific ini-setting needs, we can use "phpjs." as a prefix (as though phpjs were an extension to PHP).

So, we can modify things to work like this:

ini_set('phpjs.objectsAsArrays', false);

is_array({prop:'val'}); // false

(If you were curious about the technical details, this can work since is_array() will check for the global this.php_js.ini['phpjs.objectsAsArrays'].local_value set by ini_set() and act accordingly, or default to true if none had been set; 'this' will refer to the global window unless the namespaced version is used, in which case this.php_js will be an instance variable, rather than a global. ("php_js" is reserved as the single global we require in some functions in the non-namespaced version.)

How does that sound?

Gravatar
Kevin van Zonneveld
29 Apr '09 Permalink

q  @ KELAN: Thanks for contributing. I would like to make the same point as here:
http://phpjs.org/functions/view/422#comment_33054 though

Gravatar
KELAN
28 Apr '09 Permalink

q  

function is_array(mixed_var){
	if (mixed_var) return Object.prototype.toString.apply(mixed_var)==='[object Array]';
	else return false;
}

Gravatar
mk.keck
6 Mar '09 Permalink

q  Improved function is_array():
Why not let the user change dynamicly the behavior of is_array()?

[CODE="Javascript"]
function is_array() {
var a = arguments;
if (a.length < 1) {
return false;
}
// Check Value
var v = a[0];
// Check Strict
var s = ( (typeof(a[1]) !== 'undefined' && a[1]) ? 1 : 0 );
if (typeof(v) === 'object') {
if (v.hasOwnProperty) {
for (var k in v) {
// Checks whether the object has the specified
// property if not, we figure it's not an object
// in the sense of a php-associative-array.
if (false === v.hasOwnProperty(k)) {
return false;
}
}
}
// If (s > 0) then strict JavsScript-proof type checking
// is enabled. This will not support PHP associative
// arrays (JavaScript objects), however.
// Read discussion at:
// http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_is_array/
if (s > 0) {
if (v.propertyIsEnumerable('length') || typeof(v.length) !== 'number') {
return false;
}
}
return true;
}
return false;
}
[/CODE]

Gravatar
Brett Zamir
10 Feb '09 Permalink

q  Here's my stab at isArray() (I'm not using the PHP format since this doesn't return true for objects/associative arrays) in case any were interested.

function isArray (arr) {
if (arr instanceof Array || // catch most common occurrence and exist quickly if so
(
'splice' in arr.constructor.prototype &&
'concat' in arr.constructor.prototype && // not a word, so may be safer than splice, but can't replace it since concat present on string object
!(arr.propertyIsEnumerable('length')) &&
typeof arr.length === 'number'
)
) {
return true;
}
return false;
}

Gravatar
Brett Zamir
27 Jan '09 Permalink

q  Hi Luke,

Yes, thanks for pointing it out. But also note "Martin B." makes the same observation I did per the "Miller device" on the blog you cite. There's apparently really no foolproof/secure way to conclusively determine something is an array (and only an array) in JS.

Gravatar
Luke
26 Jan '09 Permalink

q  @Brett, FYI, Doug uses the Object.proto.toString method now.

http://blog.360.yahoo.com/blog-TBPekxc1dLNy5DOloPfzVvFIVOWMB0li?p=916

Gravatar
Kevin van Zonneveld
25 Jan '09 Permalink

q  @ Luke: Thanks for the heads-up. Interesting article. But for the record: it's not exactly our philosophy to obfuscate object types. I don't even think there is a WE in this matter.

But my view is: that if you're 'inside' php.js, you have a PHP mindset and expect associative arrays to be, well, just arrays.

You'd probably want this to just execute:
[CODE="Javascript"]
var keys = array('0', '1', '2');
var vals = array('a', 'b', 'c');

combined = array_combine(keys, vals);
if (is_array(combined)) { // WILL FAIL IF WE DENY OBJECTS!
print_r(combined);
}
[/CODE]

.. but it clearly doesn't if we don't allow objects to be arrays as well.. And so we've been struggling with imperfection since.

Luckily, if you need the JavaScript-point-of-view of a variable, you can also just use JavaScript code to establish that. If you want the php point-of-view, use php.js functions.

Gravatar
Brett Zamir
25 Jan '09 Permalink

q  Hi Luke,
Clever idea--I like it. Of course it's not foolproof if somebody overrides the Object prototype's toString() (e.g., to list all of its properties). I think an even more robust solution (and one unlikely to fail in an environment which played with built-in prototypes) might simply be to build on Crockford's approach and test for further methods (e.g., 'concat' is a pretty unlikely property) and/or to insist the method is on the prototype (excluding a user from having the property as a direct property). For example, the test, ('concat' in obj.constructor.prototype ), would catch arrays but not even {concat:'something'}. Someone could still do "delete Array.prototype.splice" but I think that would be much less likely than overriding Object.prototype.toString() which has some potential uses.

Gravatar
Luke
24 Jan '09 Permalink

q  While I entirely disagree with the philosophy of obfuscating a purposeful distinction in object types vis Array vs Object in the language you are entreating your consumers to write in, it has recently become best practice in js to check for Array type using this technique:

[CODE="Javascript"]
function isArray(a) {
return Object.prototype.toString.call(a) === '[object Array]';
}
[/CODE]

See this article for reference:
http://thinkweb2.com/projects/prototype/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

Gravatar
Onno Marsman
20 Jan '09 Permalink

q  Hadn't looked at this 'thread' for a while and I see there's still a question from Brett more or less open for me, about the hasOwnProperty solution and the answer to that is pretty much the same as my feelings for the whole framework/configuration idea:

I only use phpjs for the functions that are missing from javascript, and I also use it to promote javascript for other php-developers in my team because they can easily see in this library how some things can be done in javascript. About everything beyond that: I really don't care what happens: as long as I have simple functions that I can copy/paste and there aren't a lot of dependencies (which includes configuration for these functions I guess). The functions I'm talking about don't include is_array or exit or stuff like that, but I guess I've been clear on that.

Gravatar
Kevin van Zonneveld
15 Jan '09 Permalink

q  @ Brett Zamir: It would be cool to see other implementations coming to live. As long as I can focus on a simple robust base, people will have something consistent to build their spinoffs on.

So like I said: Personally I would like to focus on the main php.js library. Still a lot of work to be done there.

But I encourage anyone who takes that and puts it to other use. Even you :)

Gravatar
Brett Zamir
14 Jan '09 Permalink

q  Hi Kevin and Onno,

My feeling is that it's a toss-up. It doesn't bother me though because I really feel that after implementing more of the functions, it will be good to make a customizable version (on my own if you're not interested), since different people may want to handle things differently.

It's not like people can't study a little bit to set up something as potentially useful as this (and they should know what the code is doing before relying on it). A few main global configuration options (though ideally over-rideable on a function-by-function or function-group basis) like

1) When if at all to treat objects as associative arrays

2) Whether to follow PHP strictly or enable some useful customizations (not too many, but a few
that are just too natural/useful to pass up); basically the things in the functions which told people that they could uncomment the given lines (or the ones we wanted to add but felt would deviate too much from PHP--e.g., a file_get_contents() that could be configured to work with asynchronous Ajax, with Firefox (or possibly Explorer) local file access code, etc.)

Gravatar
Kevin van Zonneveld
14 Jan '09 Permalink

q  @ Onno Marsman & Brett Zamir:

What if we traverse the variable and verify that hasOwnProperty returns true for every element, and only then return true for is_array?

That way you can be pretty sure that this object is just a storage container, and nothing fancy otherwise.

Gravatar
Kevin van Zonneveld
31 Dec '08 Permalink

q  @ Onno Marsman: You are totally right. How about the current comment.

@ Brett Zamir: I didn't even know that, I'm amazed actually. Google's got some work to do on improving her engine :)

About the frameworks: Besides the obvious naming conflicts, the main reason I created the namespaced version was for others to be able to more easily extend & build on top of it.

Gravatar
Onno Marsman
30 Dec '08 Permalink

q  Ok, than we'll just leave it to return true on objects.


I do have a tiny little problem (I just can't help myself, sorry) with the comments: "Uncomment to disable support" It's not that it's not supported if you do that, it just behaves differently with all the pros en cons we discussed. If you do feel that it would be disabling support for ..., it wouldn't make any sense to put it there: who would want to disable support for something?

Gravatar
Brett Zamir
30 Dec '08 Permalink

q  Well, it's sure a good sign that people are interested in your project, if it's the third item on Google showing up for "PHP is_array" (after the PHP site and a mirror). Congratulations, wow! And that's without "JavaScript" even in the query (its the first with that one).

I think that's entirely reasonable to avoid the configuration, though I think it could be interesting to integrate it into a separate framework. Heck, maybe I might try something with it later on because I do think it could be very useful.

Gravatar
Kevin van Zonneveld
30 Dec '08 Permalink

q  @ Onno marsman, T. Wild, Brett Zamir: Thanks your insightful comments. Appreciated.

-- PHP.JS: Brett you have laid out what the project is about, and a couple of your paragraphs should probably make it to the phpjs.org site. Thanks. But in this particular case, you're preaching to the choir ;) We have all invested our precious free time in this project, because we are already convinced of it's purpose (and as it turns out, even more people see even more purposes for it, I have even heard people are trying to bring our PHP power to Adobe AIR ;)

-- Configurable PHP.JS:
My vision on the project: I think we should Not try to create a Framework. Let us stick with creating a Library. Others are invited to take our (namespaced / compiled) Library and extend it in whatever way they see fit, we should focus on delivering raw PHP power, as close to the original PHP as reasonably possible. This goal should not provide the need for configuration.

If it's even possible to have a setup file for a library (and still call it that), it would over-complicate & potentially make things unstable. I side with Onno on that one. Besides: there's lower hanging fruit still to be plucked.

-- InstanceOf:
T.Wild thanks for your url http://javascript.crockford.com/remedial.html . In these ways, working on PHP.JS has helped me to get a better understanding of both languages. That's very cool. I've changed the gettype function to fix the type problems stated in your find. I also implemented (& commented) it in the is_array function.

-- is_array:
We once made the decision that PHP.JS should accept associative arrays. I still think we can not make an exception now. Having said we should mimic PHP as much as reasonably possible, I would very much like this code:
[CODE="Javascript"]
var combined;
var keys = ['0', '1', '2'];
var vals = ['a', 'b', 'c'];

combined = array_combine(keys, vals);
if (is_array(combined)) {
print_r(combined);
}
[/CODE]
to produce:
[CODE="Text"]
Array
(
[0] => a
[1] => b
[2] => c
)
[/CODE]
And not ''. I realize that it's choosing between two evils. Maybe it's because I'd rather look at it from an enabling point-of-view, but in my opinion, having it return true on objects (!= 'classes') still is the lesser evil.

-- Paragraphing: There was a bug in my blog that stopped newlines whenever a CODE block was used. Fixed.

Gravatar
Onno Marsman
29 Dec '08 Permalink

q  Ah thanks, now at last I can make my posts readable. Let's see if this works...


About configuration: there's the danger of a lot of discussions being settled with "let's make it configurable" and that will make a lot of things unclear. Without wanting to start a discussion about that topic, in my opinion, this is exactly where most open source PHP CMS products known to me take a wrong turn. You get a lot of imaginary spin offs only by personal preference configuration which have to be maintained, and this causes a lot of bugs: a programmer tends to find and solve a bug only with his preferable configuration.


Especially with a project like this which is generally very simple, I think we should put a lot of effort in keeping it simple. If we have to make decisions we can't all agree with: too bad. Even if I would be the only one to think something should be solved differently and therefor it wouldn't be done that way, I probably wouldn't agree on making it configurable. This would be the case in the discussion about is_array right here: it's just not important enough.

I'm not saying configuration shouldn't be there at all. There might be some cases where configuration can be a good thing, although I can't think of one right now. Anyway, I think, we shouldn't use it for settling a discussion, and only use it when there is really no alternative.


About configuration by function parameters: like you said, this is a really bad idea and shouldn't be considered an option indeed.


Of course, about all these issues, I can only speak for myself.... I wonder what Kevin has to say about all of these things. He has a lot of reading to do ;)

Gravatar
Brett Zamir
29 Dec '08 Permalink

q  Onno: I thought so, but you could have been just doing it as a learning exercise. But, honestly, I wasn't targeting it at you really, I just felt I had to get that off my chest. :) Sorry about that.


I don't think that configuration is making things complicated unless the default behavior is unreasonable. Although it's usually nicer to do it by passing in an argument (if you like the PHP-style that is), since we don't really have that option, I think offering a choice is convenient and allows the library to be shared more widely. Imagine, for example, just keeping your PHP functions followed by configuration setup (if you felt the need to customize) all in one file. You could just reuse that without needing to worry about it.


What do you think about the idea of adding a module property and constants?


For paragraphing, as I only discovered this last post, was to make two lines between each.

Gravatar
Onno Marsman
28 Dec '08 Permalink

q  Brett: I'm of course talking about this function (and some other) and not the complete library, why else would I bother to participate in any way? You're defending the need of the whole library to me: you really don't need to, I share your opinion about that obviously.
--------
The in_array function seems useful to me too when you consider the whole frame/window issue, but that's a problem I don't think I will encounter very often. And when it would return true on objects (as it does now) that whole problem wouldn't even exist, and the function would seem useless to me anyway. And that's why I'm saying I probably would never use this function. My point on configuration making things complicated remains.
------
One more question to you, Brett: could you please tell me how to post in paragraphs on this site? My posts continue to result in large ugly blobs of text.

Gravatar
Brett Zamir
28 Dec '08 Permalink

q  Hi Onno: Well, for this function alone it would no doubt be overkill (the OOP way), but I don't think that PHP-JS is merely useful in helping students of PHP transition to JS, though that is a good benefit.


What is useful, I think, is that PHP has defined a vocabulary for a wide range of standard processing people want to do on Strings, Arrays, etc., functions which are completely lacking in JavaScript--a language that was standardized early on, and had little time to acquire even basic utility facilities, despite it being a flexible language).


PHP provides a kind of expressive vocabulary to be able to do things, and with which many users may already be familiar with the terminology. No doubt a large part of what people like about PHP is its large number of functions.


And for is_array(), I think many experienced programmers would be glad to save themselves the trouble of having to type the same long fail-safe string over and over again that you all were discussing (to avoid the different window/frame problem).


Personally speaking, I'm more drawn to the utility (and elegance) of other functions like array_values() or array_keys(), or even in_array(). I don't want to have to write a for loop every time I need one of them or even when I can use "indexOf() !== -1", in_array() is so much more elegant. Seriously, what's wrong with saving yourself time and making your code more intelligible?


Of course, some will look down on this because either this is not the "JavaScript way" (if it's OOP, saves lines of code, and doesn't have any negative side effects, I don't see how it isn't) or because it pays homage to a language which is just too darn easy to learn and do useful things with.


It's like people who will respect you if you learn ancient Egyptian but think nothing of you learning Spanish. If it's useless and difficult, then it deserves praise. Or when people prefer the status quo of not having an official world auxiliary language because they think it is charming that we can't communicate with each other (or just expect that everyone should spend all of their time mastering various languages, rather than working for a global agreement to have one language (whether English, Esperanto, or whatever could garner the most support) be taught along with native languages in schools around the world). Does humanity really need scores of words for "apple" when we could settle on one language in addition to our native one? (thousands of words, I know, but I'm only talking about reducing lingua francas, not native languages) Does inter-communication need to be only for those with privilege and too much free time?


Why does a JavaScript library (that has no JavaScript standard to work from) need to start from scratch as far as terminology as well as functionality? Isn't it helpful to be able to piggy-back on something already existing?


Sorry for this diatribe (I'm not at all responding to your honest question), but this just raised the topic for me of all the maligning people do
in other discussions I've had because human beings like to lord over the "right way", and conversely, proponents of practicality are often too cowed to defend the useful albeit ordinary, while others are afraid to think for themselves and resist the impulse to second-guess oneself when everyone else is criticizing something you find useful. Criticism itself is usually such a waste of time, where we should be honestly discussing in a humble manner (like your nice polite but frank question) which way is better.


I'm doing JavaScript for full-time paid work (building Firefox extensions), and I've unabashedly been using some of these utilities. I particularly like array_unique(), trim, and Mozilla equivalents I've made for file_get_contents() and file_put_contents().


Oh, and I see I'm using your min() function too! :) Given that you found a need to write a good many lines of code for that function, do you really want to rewrite that each time you need it or give it a non-PHP name? :)

Gravatar
Onno Marsman
28 Dec '08 Permalink

q  Brett: Isn't this making things a bit to complicated for something that's just meant to help a PHP programmer make the step to JS? I mean: no experienced JS programmer is ever gonna use this function anyway and I don't think a not so experienced one would wanna find out how to configure something like this. I think it would even be easier and clearer to fall back to "instanceof Array" or "instanceof Object", whichever one they need.
PHP doesn't have this kind of configuration either and if we would introduce it I doubt anybody would use it.

Gravatar
Brett Zamir
28 Dec '08 Permalink

q  Sorry, my OOP code had a bug...Here's a fix:

[CODE="Javascript"]function PHP_JS (config) {
this.JS = {};
for (var php_js_method in this) { // Add JS config property for all PHP-JS functions
if (typeof this[php_js_method] === 'function') {
this.JS[php_js_method] = {};
}
}
for (var method in config) {
var configObj = config[method];
this.JS[method] = configObj;
}
}
PHP_JS.prototype = {
is_array : function (mixed_var) {
if (this.JS.is_array.objectsAsArrays) {
return (mixed_var instanceof Object);
}
return mixed_var && !(mixed_var.propertyIsEnumerable('length')) && typeof mixed_var === 'object' && typeof mixed_var.length === 'number';
}
}

var PHP1 = new PHP_JS({is_array:{objectsAsArrays:true}});
var PHP2 = new PHP_JS({is_array:{objectsAsArrays:false}});
alert( PHP1.is_array({}) ); // true
alert( PHP2.is_array({}) ); // false
PHP1.JS.is_array.objectsAsArrays = false; // Can still reconfigure if needed too
PHP2.JS.is_array.objectsAsArrays = true;
alert( PHP1.is_array({}) ); // false
alert( PHP2.is_array({}) ); // true
[/CODE]

Gravatar
Brett Zamir
28 Dec '08 Permalink

q  In order for PHP-JS to work as a bona-fide framework (which I think it could well become), there is a need for configurability, as seen in this discussion. And while a decision must still be made as to default behavior, I think that we can take advantages features of JavaScript which PHP does not have, in order to allow that configurability without adding additional arguments to the functions (which might have additional ones assigned by PHP in the future): adding properties to the functions themselves. (My apologies if others have suggested this.)

My suggestion is to reserve "JS" as an object for configurability. For example, one might do:

[CODE="Javascript"]function is_array (mixed_var) {
if (arguments.callee.JS.objectsAsArrays) {
return (mixed_var instanceof Object);
}
return mixed_var && !(mixed_var.propertyIsEnumerable('length')) && typeof mixed_var === 'object' && typeof mixed_var.length === 'number';
}
is_array.JS = {};

is_array.JS.objectsAsArrays = true;
alert( is_array({}) ); // true
is_array.JS.objectsAsArrays = false;
alert( is_array({}) ); // false[/CODE]

The internal code would simply check for "if ({func_name}.JS.{prop_name})", (adding the empty JS object right after the function declaration) and act accordingly. I believe this could really be useful, especially for functions with no easy parallel in native JavaScript, but where the nature of JavaScript suggests different possible behaviors for the functions (or for adding other ideas PHP didn't think of).

We might even handle this in an OOP way (to more easily allow different configurations for the functions in different contexts), if the "namespace" for our PHP-JS objects were to be given by a constructor function. For example,

[CODE="Javascript"]
var PHP1 = new PHP_JS({is_array:{objectsAsArrays:true}});
var PHP2 = new PHP_JS({is_array:{objectsAsArrays:false}});
alert( PHP1.is_array({}) ); // true
alert( PHP2.is_array({}) ); // false
PHP2.JS.is_array.objectsAsArrays = true; // Can still reconfigure if needed too
alert( PHP2.is_array({}) ); // true

function PHP_JS (config) {
this.JS = {};
for (var php_js_method in this) { // Add JS config property for all PHP-JS functions
if (typeof this[php_js_method] === 'function') {
this.JS[php_js_method] = {};
}
}
for (var method in config) {
var configObj = config[method];
this.JS[method] = configObj;
}
}
PHP_JS.prototype = {
is_array : function (mixed_var) {
if (arguments.callee.JS.objectsAsArrays) {
return (mixed_var instanceof Object);
}
return mixed_var && !(mixed_var.propertyIsEnumerable('length')) && typeof mixed_var === 'object' && typeof mixed_var.length === 'number';
}
}[/CODE]

Whether using the OOP approach or not, your configuration questions (objectsAsArrays, functionsDisallowed, etc.) could be handled.

For either of these approaches (of adding properties to the functions or namespace object), PHP constants could be added relating to that function (thus not requiring defining every possible PHP constant or polluting the global namespace further), or meta-data could be attached relating to that function vis-a-vis PHP such as to indicate to which module it belongs (or put the constants within the module information). Thus, a property could be used to indicate to which PHP module a given function belonged (if one was not already using namespaces to do so). Then one could do things like: extension_loaded(), get_loaded_extensions(), get_extension_funcs(), and get_defined_constants(true) as these functions could reflect upon the meta-data stored for each function.

Although the following is not strictly PHP behavior since Array functions are not an extension in PHP (though we can override this with configuration as described above), we could do things like:

[CODE="Javascript"]if (!extension_loaded('array')) {
function is_array () {
....
}
function in_array () {
....
}
....etc.
}[/CODE]

Gravatar
Onno Marsman
27 Dec '08 Permalink

q  Wow, that's really weird! I guess your find is a better implementation than "instanceof Array" then. I'm curious about what Kevin thinks about all of this now.


Contribute a New function

More functions

In this category

doubleval
empty
floatval
get_defined_vars
get_resource_type
gettype
import_request_variables
intval
» is_array
is_binary
is_bool
is_buffer
is_callable
is_double
is_float
is_int
is_integer
is_long
is_null
is_numeric
is_object
is_real
is_resource
is_scalar
is_string
is_unicode
isset
print_r
serialize
settype
strval
unserialize
var_dump
var_export

Support us

spread the word:


Use any PHP function in JavaScript


These kind folks have already donated: AYHAN BARI*, Nikita Ekshiyan, Nikita Ekshiyan, Petr Pavel, @HalfWinter, Paulo Freitas, Andros Peña Romo, @andorosu, Raimund Szabo, Nitin Gupta, @nikosdion, Anonymous, Anonymous and Shawn Houser.
<your name here>

Click here to lend your support to: phpjs and make a donation at www.pledgie.com !