» PHP.JS Namespaced

435 PHP equivalents

PHP to Javascript Project: php.js

php.jsThis article is part of the 'Porting PHP to Javascript' Project, which aims to decrease the gap between developing for PHP & Javascript.

A lot of people are familiar with PHP's functions, and though Javascript functions are often quite similar, some functions may be missing or addressed differently. The Javascript implementations should be as compliant with the PHP versions as possible, a good indication is that the PHP function manual could also apply to the Javascript version.

Porting crucial PHP functions to Javascript can be fun & useful. Currently some PHP functions have been added, but readers are encouraged to contribute and improve functions by adding comments. Eventually the goal is to save all the functions in one php.js file and make it publicly available for your coding pleasure.

If you choose to contribute, let me know how you want to be credited in the function's comments. You may also want to subscribe to RSS so you receive updates whenever new functions are posted.

Thanks to a lot of extra effort by Michael White (http://crestidg.com) there now is a namespaced version of PHP.JS available for your coding pleasure.

Namespaces

Namespaces are used to group functions & objects together. In this case, all PHP.JS functions are contained in one big object: PHP_JS.

Look at this code (not namespaced):

/var/www/web5/web/code/php_equivalents/examples/php.example.js

Easy typing right? Just like you know from PHP.

Now look at this code (namespaced):

/var/www/web5/web/code/php_equivalents/examples/php.namespaced.example.js

Little bit different. All PHP.JS code is now stored in it's own object. Some people like that because it can then never conflict with any existing function names (either by you, your CMS, other libraries or even JavaScript itself). This becomes clear with abs:

a = Math.abs(-7.25); // Javascript's own abs() function
a = $P.abs(-7.25);   // php.js's abs() function
 
a = abs(-7.25);      // only recognized when using normal php.js

Other namespacing benefits

Namespacing makes PHP.JS more flexible and makes it easier for developers to extend and include this library. They have more control over this project because it listens to a single name. If you want to dig a little deeper, try wiki.

Unnamespaced PHP.JS

Both unnamespaced & namespaced versions will be maintained and updated.

Why? I know that there are a lot of people out there who do not want to switch to a namespaced variant because they just want to be able to call the functions directly. For the ease of typing, but also because it resembles PHP, and that's the whole point of this project, right?

Download php.js

You can do that on every function's page. But for your convenience:

Future features

We're currently working on two major project improvements.

Better Versioning

All functions are versioned, but we're working on solid versioning of the main php.js file as well, this feature will be ready really soon and announced here.

Lightweight

PHP.JS contains a lot of functions and you probably won't use them all in one site. So to people who are concerned with things like bandwidth I can recommend a couple of things:

The last suggestion is not very developer-friendly though. You will survive but still.. We've got the idea to create an online application with a lot of check boxes and a couple of default presets. To allow you to exclude all kinds of functions and totally customize your own PHP.JS. Your unique combination of functions will additionally generate a hash, so you can always enter that hash again to download the newest versions of your favorite functions.

Dedicated site

We're currently working on a dedicated site for PHP.JS. Moving this project away from my Blog will make it more accessible and help professionalize the project.

New to PHP.JS?

If you're new to PHP.JS, checkout an example. We are not trying to port or emulate the entire language or control structures of PHP. We don't see the need because JavaScript seems to have more elegant features in that category anyway.

However in our eyes, PHP does provide a large set of standard functions that make developing very easy, and some of them don't have good standard JavaScript implementations, though they often would be great to have client-side.

So in this project by also providing the functions separately, we hope to keep people from inventing the wheel and give them a head start.

About the project

The PHP.JS project started in December 2007. It quickly became populair and within 4 months PHP.JS supported over 120 PHP functions all translated to JavaScript altenatives. PHP.JS is open source and has received (and still is receiving) code contributions from 50+ developers around the world, who have all helped to make PHP.JS what it is today. A rich Javascript library that adds functionality & PHP compatibility to your projects.

Stay up to date

You can track my blog rss articles and rss comments. You may also find my rss bookmarks interesting. Or twitter Follow me on Twitter


Like this article?

   Then Dzone it!
Or use another bookmark button below to show your support &
help me spread the word.


tags: programming, javascript, php, phpjs
category: Programming - Javascript - PHP equivalents
read: 19,371 times

Add Comment

PHP.JS is outgroing this blog and moving to it's own space. Please leave your comment here: http://phpjs.org

Comments

#96. Kevin on 02 February 2009

Gravatar.com: Kevin@ Brett Zamir: I would be totally for expanding the _unported directory with actual code. I already did this for bcmath. Which can still not be ported in their current form. Unported can be a working place. And anyway I think it's a good idea to store gathered knowledge in the form of code there.
Because you can be sure that if somebody tries a port in the future, they will find the result of previous study in _unported.

#95. Brett Zamir on 02 February 2009

Gravatar.com: Brett ZamirRegarding your question about guaranteeing cross-behavior behavior, I actually can't guarantee it works in any browser. :) It should work, I think (at least for images with the abort version, and for unloading behaviors for the unload version), but at least in Firefox, it doesn't--I am guessing due to a bug.

A potential use of this, especially on privileged JavaScript, like in a Firefox extension, would be to sure that incrementor or serialization code could be saved -to file, even if the user didn't completely load the page (say by unloading before the script executed or pushing the stop button).

But since it doesn't work in Firefox, I was more just throwing the functions out there. I didn't know ahead of time whether they would work. Maybe we could start a folder for items like this (as well as for ones that can't work in the PHP-way like list() ), so that we can move them to not porting, but still have some documentation for study purposes (or to resurrect them if support changes)?

#94. Kevin on 01 February 2009

Gravatar.com: Kevin@ Brett Zamir: If I understand you correctly, we can't really guarantee anything concerning it's behavior crossbrowser-wise right? That bothers me.

And I can't see myself using these functions. But then again it may very well still be of use to many, and so it should be possible to add it if we attach clear warnings and don't include it in the default package.

But I have too little understanding of this side of JavaScript and so I think I should not be the person to making the final call.
... [more]
So if you still think there's a need: feel free to add these functions to the SVN repository.

#93. Brett Zamir on 31 January 2009

Gravatar.com: Brett ZamirI believe this should work, I believe (if you try to stop the loading of the page), but at least in Firefox, it does not. I don't think it should be limited to images (as it is supposedly in Explorer), since the spec at http://www.w3.org/TR/DOM-Level-3-Events/events.html#event-abort doesn't mention such a limit (though it does mention resource.

function ignore_user_abort () {
window.addEventListener('abort', function(e){
e.preventDefault();
}, false);
}


or also connection_aborted():

var connection_aborted = (function () {
var retVal=0;
window.addEventListener('abort', function(e){
retVal=1;
}, false);
return function () {return retVal;}
})();


The event references to "abort" above might simply be changed to "unload" to prevent unloading (or noting whether the user is leaving), and this more clearly pertains to the action of not just one resource inside a document; however, it will not pertain to the user stopping a load, and again, in Firefox, while the unload event fires, it doesn't seem to work with the DOM standard preventDefault().

If you don't like either of these approaches, my guess is that these are otherwise not portable.

#92. Kevin on 25 January 2009

Gravatar.com: Kevin@ Brett Zamir: Nice find Brett! I've added a comment on Steven's site to let him know.

#91. Brett Zamir on 23 January 2009

Gravatar.com: Brett ZamirHere's parse_url, largely based off of the very nice parseUri function by Steven Levithan:

// Based on http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js  (blog post at http://blog.stevenlevithan.com/archives/parseuri and demo at http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js ); also MIT license, so I guess you can just incorporate his name as far as the license?
 
// Does not replace invaild characters with '_' as in PHP, nor does it return false with a seriously malformed URL.
// Besides function name, is the same as parseUri besides the commented out portion and the additional section following, as well as our allowing an extra slash after the scheme/protocol (to allow file:/// as in PHP)
 
function parse_url (str, component) {
var o = parse_url.options,
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
uri = {},
i = 14;
while (i--) uri[o.key[i]] = m[i] || "";
// Uncomment the following to use the original more detailed (non-PHP) script
/*uri[o.q.name] = {};
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
if ($1) uri[o.q.name][$1] = $2;
});
return uri;*/

switch (component) {
case 'PHP_URL_SCHEME':
return uri.protocol;
case 'PHP_URL_HOST':
return uri.host;
case 'PHP_URL_PORT':
return uri.port;
case 'PHP_URL_USER':
return uri.user;
case 'PHP_URL_PASS':
return uri.password;
case 'PHP_URL_PATH':
return uri.path;
case 'PHP_URL_QUERY':
return uri.query;
case 'PHP_URL_FRAGMENT':
return uri.anchor;
default:
var retArr = {};
if (uri.protocol !== '') retArr.scheme=uri.protocol;
if (uri.host !== '') retArr.host=uri.host;
if (uri.port !== '') retArr.port=uri.port;
if (uri.user !== '') retArr.user=uri.user;
if (uri.password !== '') retArr.pass=uri.password;
if (uri.path !== '') retArr.path=uri.path;
if (uri.query !== '') retArr.query=uri.query;
if (uri.anchor !== '') retArr.fragment=uri.anchor;
return retArr;
}
}
parse_url.options = {
strictMode: false,
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
q: {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/\/?)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // Added one optional slash to post-protocol to catch file:/// (should restrict this)
}
};

#90. Kevin on 15 January 2009

Gravatar.com: Kevin@ Yeah: there are still some functions in PHP.JS that only accept numerically indexed arrays. They should be fixed and can be considered mistakes.

But I don't want to implement mistakes actively ;) let's get it right, and then add them.

#89. Brett Zamir on 15 January 2009

Gravatar.com: Brett ZamirHi Kevin... Actually the ext. dev. extension is not (to my knowledge) able to run sources. I just meant that if you go to View->Toolbars->Customize after installing, and drag the JS Env and/or XUL Ed/<A> buttons (JRX is handy too) to your toolbar, you have instant access to try out some JavaScript (in Spidermonkey). I like XUL Editor because it evaluates instantly, though JS Env is more handy if you don't want it evaluating instantly. The other buttons are for HTML editing (unfortunately, script tags are not added there by default), regular-expression testing, and other items like a JS Shell.

As far as usort(), yes, it only works on indexed arrays at present. I thought it would be ok, since sort() and rsort() also only work on indexed arrays, and this was based on sort(). However, I do hope we can use the bubble sort technique at some point to get these functions (and add uasort() too) all supporting associative arrays.

By the way, although I see array_udiff_uassoc() was fixed, it is still in the unported list.
... [more]
all the best, Brett

#88. Kevin on 14 January 2009

Gravatar.com: Kevin@ Brett Zamir: Hahaha LOL! Nice one :) Yeah copy & pasting has a way of numbing the brain :) fixed though.

Regarding Ext.Dev.: I installed it but got confused a bit. Couldn't find where to run my sources. Anyway I will have to give it some more time I guess.

About usort. Could it be that it only works on indexed arrays?

#87. Brett Zamir on 14 January 2009

Gravatar.com: Brett ZamirRegarding your joke earlier about catching up with Onno, I may still have a ways to go for that, but at least I chose long function names to make it look like I've caught up with him.... ;)

#86. Brett Zamir on 14 January 2009

Gravatar.com: Brett ZamirSounds like you have a nifty setup... If you still want the Ext. Developer's extension, I'd download it from the addons site (I think his personal site isn't on a secure site, so it's not working from there as of FF3): https://addons.mozilla.org/en-US/firefox/addon/7434

Thanks for adding all of the functions. Two small issues:

1) array_udiff_uassoc() lists array_uintersect_uassoc instead (and I guess maybe that's also why the former still shows in the unported list).
... [more]
2) I also added usort on http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_ksort/

I know it was a lot to keep track of with all the pasting, etc. Best, Brett

#85. Kevin on 14 January 2009

Gravatar.com: Kevin@ Brett Zamir: OKAY I've added every function. I will have too look into the rhino thing.

Tried but didn't quite get Extension Developer's Extension. Anyway I mapped testlast.sh toi my netbeans 'Run Project' button. Which will scan for the source file with the most recent modification date, and feed that to phpjstest.php

So basically I can work on source & test it all in my IDE.

#84. Brett Zamir on 13 January 2009

Gravatar.com: Brett ZamirMaybe it is a Rhino bug. If you do the following (the examples as you have them) with the function at http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_array_uintersect/ (i.e., as you have it), you'll see 'red':

$array1 = {a: 'green', b: 'brown', c: 'blue', 0: 'red'}
$array2 = {a: 'GREEN', B: 'brown', 0: 'yellow', 1: 'red'}
 
alert(array_uintersect($array1, $array2, function(f_string1, f_string2){var string1 = (f_string1+'').toLowerCase(); var string2 = (f_string2+'').toLowerCase(); if(string1 > string2) return 1; if(string1 == string2) return 0; return -1;})[0]);


(Not sure if I've recommended it before, but the Extension Developer's Extension for Firefox makes for very quick JS testing in the browser. I prefer using the XUL editor for JavaScript to get immediate results from alerts, but there is a JS environment (whose print() feature is nice when alert is too cumbersome)--both of which are very convenient to add as custom toolbar buttons. I swear this has contributed more to my productivity in JS than almost anything)

#83. Kevin on 13 January 2009

Gravatar.com: Kevin@ Brett Zamir: Let's make sure we're looking at the same thing, cause it's still doing it here.

cd _tools
./phpjstest.php array_uintersect debug


The output gives an array in which the fist element has key: 0 and value: <empty array>

Could it be that rhino is doing this? There have been some issues (https://bugzilla.mozilla.org/show_bug.cgi?id=419090)

#82. Brett Zamir on 13 January 2009

Gravatar.com: Brett ZamirHmm... array_uintersect() as you have it (and your examples) is working for me. I also checked my next example (according to the PHP manual's examples), and that was working too... (scratches head)

#81. Kevin on 13 January 2009

Gravatar.com: Kevin@ Brett Zamir: Amazing job man.

With the array functions I had to stop at array_uintersect because that returned:

[2] => (
[3] => [0] => Array
[4] => (
[5] => )
[6] => [a] => green
[7] => [b] => brown
[8] => )


Whereas the PHP manual says the first element's value should be 'red'. Once that bug is taken care off I'll add the others as well.

I made strval depend on gettype (which depends on is_array), to avoid any future inconsistency issues.

And you are right: those are important issues we need to address!

#80. Brett Zamir on 12 January 2009

Gravatar.com: Brett ZamirA few thoughts...

1) I think the most serious need to address sooner rather than later (my own additions included) is to avoid the problem PHP is having in converting its functions to fully support Unicode. Although JavaScript is a bit better off because it uses (usually) fixed width characters, in the case of certain characters (outside of the BMP (Basic Multi-lingual Plane)), these can be twice as long, so sometimes a "character" must be considered to be two characters long, if PHP-JS is to handle them properly.

(See "fixedFromCharCode()" at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/fromCharCode and a similar discussion at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/charCodeAt#Description )
... [more]
2) All "instanceof Array" tests (I've been adding some myself I know) should be changed into is_array() dependencies

3) One very interesting application of a full version of PHP-JS (esp. a while down the road) in case you hadn't considered it, would be as an importable module in Mozilla: https://developer.mozilla.org/en/Using_JavaScript_code_modules . Mozilla/Firefox only loads these once, so if one extension uses it, any number of others can without a loading cost.

#79. Brett Zamir on 12 January 2009

Gravatar.com: Brett ZamirHere's also strval():


function strval (str) {
// Comment out the entire switch if you want JS-like behavior instead of PHP behavior
switch (typeof str) {
case 'boolean':
if (str === true) return '1';
return '';
case 'object':
if (str instanceof Array) return 'Array';
if (str === null) return '';
return 'Object';
case 'number':
case 'string':
default:
break;
}
return str+'';
}

#78. Brett Zamir on 11 January 2009

Gravatar.com: Brett ZamirI responded earlier but must have been cut out by your spam filter or something...

I joked that I'd be find breathing, eating, and sleeping JavaScript :) Seriously, I was just taking the day off, but needed some fun brain engagement...

Here are sixteen array functions, the diff and intersect ones (they were all pretty closely related):

// These only output associative arrays (would need to be all numeric and counting from zero to be numeric)
function array_intersect () {
var arr1 = arguments[0], retArr = {};
arr1keys:
for (var k1 in arr1) {
arrs:
for (var i=1; i < arguments.length; i++) {
var arr = arguments[i];
for (var k in arr) {
if (arr[k] === arr1[k1]) {
if (i === arguments.length-1) {
retArr[k1] = arr1[k1];
}
continue arrs; // If the innermost loop always leads at least once to an equal value, continue the loop until done
}
}
continue arr1keys; // If it reaches here, it wasn't found in at least one array, so try next value
}
}
return retArr;
}
$array1 = {"a" : "green", 0:"red", 1:"blue"};
$array2 = {"b" : "green", 0:"yellow", 1:"red"};
$array3 = ["green", "red"];
$result = array_intersect($array1, $array2, $array3);


If you change the line:

if (arr[k] === arr1[k1]) {


to:

if (arr[k] === arr1[k1] && k === k1) {


you can have array_intersect_assoc() as well.

If you change the line instead to:

if (k === k1) {


you can have array_intersect_key().


Now for the diffs:

function array_diff () {
var arr1 = arguments[0], retArr = {};
arr1keys:
for (var k1 in arr1) {
for (var i=1; i < arguments.length; i++) {
var arr = arguments[i];
for (var k in arr) {
if (arr[k] === arr1[k1]) {
continue arr1keys; // If it reaches here, it was found in at least one array, so try next value
}
}
retArr[k1] = arr1[k1];
}
}
return retArr;
}



And if you change the line:

if (arr[k] === arr1[k1]) {


to:

if (arr[k] === arr1[k1] && k === k1) {


...you get array_diff_assoc


If you change it instead to:

if (k === k1) {


...you get array_diff_key.


Now for the single callback intersect types...

function array_uintersect () {
var arr1 = arguments[0], retArr = {}, cb = arguments[arguments.length-1];
cb = (typeof cb === 'string') ? window[cb] : (cb instanceof Array) ? window[cb[0]][cb[1]] : cb;
 
arr1keys:
for (var k1 in arr1) {
arrs:
for (var i=1; i < arguments.length-1; i++) {
var arr = arguments[i];
for (var k in arr) {
if (cb(arr[k], arr1[k1]) === 0 ) {
if (i === arguments.length-2) {
retArr[k1] = arr1[k1];
}
continue arrs; // If the innermost loop always leads at least once to an equal value, continue the loop until done
}
}
continue arr1keys; // If it reaches here, it wasn't found in at least one array, so try next value
}
}
return retArr;
}


If you change this line:

if (cb(arr[k], arr1[k1]) === 0 ) {


to:

if (cb(k, k1) === 0 ) {


... you get array_intersect_ukey

Or if you changet the line to:

if (arr[k] === arr1[k1] && cb(k, k1) === 0 ) {


... you get array_intersect_uassoc

and if you change the line to:

if (cb(arr[k], arr1[k1]) === 0 && k === k1) {


... you get array_uintersect_assoc


Now for the single callback diff types:

function array_udiff () {
var arr1 = arguments[0], retArr = {}, cb = arguments[arguments.length-1];
cb = (typeof cb === 'string') ? window[cb] : (cb instanceof Array) ? window[cb[0]][cb[1]] : cb;
arr1keys:
for (var k1 in arr1) {
for (var i=1; i < arguments.length-1; i++) {
var arr = arguments[i];
for (var k in arr) {
if (cb(arr[k], arr1[k1]) === 0) {
continue arr1keys; // If it reaches here, it was found in at least one array, so try next value
}
}
retArr[k1] = arr1[k1];
}
}
return retArr;
}


If you change this line

if (cb(arr[k], arr1[k1]) === 0) {


to:

if (cb(k, k1) === 0) {


...you get array_diff_ukey.

If you change it instead to:

if (arr[k] === arr1[k1] && cb(k, k1) === 0) {


...you get array_diff_uassoc.

And if you change it to:

if (cb(arr[k], arr1[k1]) === 0 && k === k1) {


...you get array_udiff_assoc


And now for the double callback intersect function:

function array_uintersect_uassoc () {
var arr1 = arguments[0], retArr = {}, cb = arguments[arguments.length-1], cb0 = arguments[arguments.length-2];
cb = (typeof cb === 'string') ? window[cb] : (cb instanceof Array) ? window[cb[0]][cb[1]] : cb;
cb0 = (typeof cb0 === 'string') ? window[cb0] : (cb0 instanceof Array) ? window[cb0[0]][cb0[1]] : cb0;
 
arr1keys:
for (var k1 in arr1) {
arrs:
for (var i=1; i < arguments.length-2; i++) {
var arr = arguments[i];
for (var k in arr) {
if (cb0(arr[k], arr1[k1]) === 0 && cb(k, k1) === 0) {
if (i === arguments.length-3) {
retArr[k1] = arr1[k1];
}
continue arrs; // If the innermost loop always leads at least once to an equal value, continue the loop until done
}
}
continue arr1keys; // If it reaches here, it wasn't found in at least one array, so try next value
}
}
return retArr;
}



And, finally, for the double callback diff function:

function array_udiff_uassoc () {
var arr1 = arguments[0], retArr = {}, cb = arguments[arguments.length-1], cb0 = arguments[arguments.length-2];
cb = (typeof cb === 'string') ? window[cb] : (cb instanceof Array) ? window[cb[0]][cb[1]] : cb;
cb0 = (typeof cb0 === 'string') ? window[cb0] : (cb0 instanceof Array) ? window[cb0[0]][cb0[1]] : cb0;
 
arr1keys:
for (var k1 in arr1) {
for (var i=1; i < arguments.length-2; i++) {
var arr = arguments[i];
for (var k in arr) {
if (cb0(arr[k], arr1[k1]) === 0 && cb(k, k1) === 0) {
continue arr1keys; // If it reaches here, it was found in at least one array, so try next value
}
}
retArr[k1] = arr1[k1];
}
}
return retArr;
}

#77. Kevin on 08 January 2009

Gravatar.com: Kevin@ Brett Zamir: You. are. awesome. (remember too breathe, eat & sleep Brett ;)

#76. Brett Zamir on 08 January 2009

Gravatar.com: Brett ZamirActually, I guess my first example might have been just as well or better--anybody?

Here's a strtok implementation:

$string = "\t\t\t\nThis is\tan example\nstring\n";
/* Use tab and newline as tokenizing characters as well */
$tok = strtok($string, " \n\t");
$b = '';
while ($tok !== false) {
$b += "Word="+$tok+"\n";
$tok = strtok(" \n\t");
}
alert($b)
 
function strtok (str, tokens) {
if (tokens === undefined) {
tokens = str;
str = strtok.leftOver;
}
if (str.length === 0) {
return false;
}
if (tokens.indexOf(str[0]) !== -1) {
return strtok(str.substr(1), tokens);
}
for (var i=0; i < str.length; i++) {
if (tokens.indexOf(str[i]) !== -1) {
break;
}
}
strtok.leftOver = str.substr(i+1);
return str.substring(0, i);
}

#75. Brett Zamir on 08 January 2009

Gravatar.com: Brett ZamirSorry again.. That was inheriting not cloning--here it is again...

function clone (o) {
var newObj = {};
for (var p in o) {
newObj[p] = o[p];
}
if (newObj.__clone && typeof newObj.__clone === 'function') {
newObj.__clone();
}
return newObj;
}

#74. Brett Zamir on 08 January 2009

Gravatar.com: Brett ZamirSince you have echo, would you be open to a 'clone'?

var test = clone({letters:'abc', __clone : function(){this.letters='xyz'}});
 
function clone (o) {
var F = function() {};
F.prototype = o;
if (F.prototype.__clone && typeof F.prototype.__clone === 'function') {
F.prototype.__clone();
}
return new F();
}


The above may need some further tweaking (e.g., I don't know if the __clone() method itself should be removed from the new object).

#73. Brett Zamir on 08 January 2009

Gravatar.com: Brett ZamirAnother...

alert(   str_shuffle('abcde')    );
 
function str_shuffle (str) {
if (str == undefined) {
throw 'Wrong parameter count for str_shuffle()';
}
function getRandomInt(max) {
return Math.floor(Math.random() * (max + 1));
}
var newStr = '';
while (str.length) {
rand = getRandomInt(str.length-1);
newStr += str[rand];
str = str.substring(0, rand)+str.substr(rand+1);
}
return newStr;
}

#72. Brett Zamir on 08 January 2009

Gravatar.com: Brett ZamirHere's strrchr...

$text = "Line 1\nLine 2\nLine 3";
$last = strrchr($text, 10).substr(1); // 'Line 3'
alert($last)
 
function strrchr (haystack, needle) {
if (typeof needle !== 'string') {
needle = String.fromCharCode(parseInt(needle, 10));
}
needle = needle[0];
var pos = haystack.lastIndexOf(needle);
if (pos === -1) {
return false;
}
return haystack.substr(pos);
}

#71. Brett Zamir on 07 January 2009

Gravatar.com: Brett ZamirSorry, here it is shortened...

$trans = {"hello" : "hi", "hi" : "hello"};
alert( strtr("hi all, I said hello", $trans) ); // hello all, I said hi
 
function strtr (str, from, to) {
if (typeof from === 'object') {
for (var fr in from) {
str = str.replace(fr, from[fr]);
}
return str;
}
var lgth = to.length;
if (from.length < to.length) {
lgth = from.length;
}
for (var i=0; i < lgth; i++) {
str = str.replace(from[i], to[i]);
}
return str;
}

#70. Brett Zamir on 07 January 2009

Gravatar.com: Brett ZamirHere's strtr()... This is just a little too fun... :)

var $addr = "äaabaåccasdeöoo";
$addr = strtr($addr, "äåö", "aao");
alert($addr); // aaabaaccasdeooo
 
$trans = {"hello" : "hi", "hi" : "hello"};
alert( strtr("hi all, I said hello", $trans) );
 
function strtr (str, from, to) {
var newStr = str;
if (typeof from === 'object') {
for (var fr in from) {
newStr = newStr.replace(fr, from[fr]);
}
return newStr;
}
else {
var lgth;
if (from.length < to.length) {
lgth = from.length;
}
else {
lgth = to.length;
}
}
for (var i=0; i < lgth; i++) {
newStr = newStr.replace(from[i], to[i]);
}
return newStr;
}

#69. Kevin on 07 January 2009

Gravatar.com: Kevin@ Brett Zamir: Thanks again Brett, the function works perfectly! About list: I really appreciate the creativity you display with your approaches, but in this case I don't feel comfortable with the proposed exception and I think I'll stick with your first suggestion as to move it to notporting.

#68. Brett Zamir on 07 January 2009

Gravatar.com: Brett ZamirSorry, you can remove the "alert('a')" at the end of extract() :)

#67. Brett Zamir on 07 January 2009

Gravatar.com: Brett ZamirBy the way, thanks for adding the array traversal functions...Some of the PHP equivalents do seem to add functionality not possible in JavaScript, since JavaScript doesn't seem to have a way, for example, to go backwards with the iterator...

So, if I can push my luck, how about reconsidering register_shutdown_function()? Why was that rejected earlier? Given my interpretation of "shutdown" as relating to a window closing, I think that is consistent with other functions here.

I think you should change list() to "do not port" because I just don't see any way JavaScript can do such a thing, since functions are not allowed as l-values in JavaScript... (unless you wanted to reserve the list() page to point out the existence of array destructuring for people looking for an equivalent).

Also, again, not a big deal, but under my name, I'm credited twice for array_fill_keys.

I still mean to get back to you about get_defined_constants().

Here's extract() too by the way...

// Only works by extracting into global context (whether called in the global scope or within a function); also, the EXTR_REFS flag I believe can't be made to work
function extract (arr, type, prefix) {
if (arr instanceof Array && (type !== 'EXTR_PREFIX_ALL' && type !== 'EXTR_PREFIX_INVALID')) {
return 0;
}
var chng = 0;
 
for (var i in arr) {
var validIdent = /^[_a-zA-Z$][\w|$]*$/; // TODO: Refine regexp to allow JS 1.5+ Unicode identifiers
var prefixed = prefix+'_'+i;
try {
switch (type) {
case 'EXTR_PREFIX_SAME' || 2:
if (this[i] !== undefined) {
if (prefixed.match(validIdent) != null) {
this[prefixed] = arr[i];
++chng;
}
}
else {
this[i] = arr[i];
++chng;
}
break;
case 'EXTR_SKIP' || 1:
if (this[i] === undefined) {
this[i] = arr[i];
++chng;
}
break;
case 'EXTR_PREFIX_ALL' || 3:
if (prefixed.match(validIdent) != null) {
this[prefixed] = arr[i];
++chng;
}
break;
case 'EXTR_PREFIX_INVALID' || 4:
if(i.match(validIdent) != null) {
if (prefixed.match(validIdent) != null) {
this[prefixed] = arr[i];
++chng;
}
}
else {
this[i] = arr[i];
++chng;
}
break;
case 'EXTR_IF_EXISTS' || 6:
if (this[i] !== undefined) {
this[i] = arr[i];
++chng;
}
break;
case 'EXTR_PREFIX_IF_EXISTS' || 5:
if (this[i] !== undefined && prefixed.match(validIdent) != null) {
this[prefixed] = arr[i];
++chng;
}
break;
case 'EXTR_REFS' || 256:
throw 'The EXTR_REFS type will not work in JavaScript';
break;
case 'EXTR_OVERWRITE' || 0:
// Fall-through
default:
this[i] = arr[i];
++chng;
break;
}
}
catch (e) { // Just won't increment for problem assignments
alert('a')
}
}
return chng;
}
 
size = "large";
var_array = {"color" : "blue",
"size" : "medium",
"shape" : "sphere"};
alert(extract(var_array, 'EXTR_PREFIX_SAME', "wddx")); // Returns 3
 
alert(color+size+shape+wddx_size+'\n');

#66. Kevin on 31 December 2008

Gravatar.com: KevinBrett makes a good point: People will find uses in ways you didn't foresee, I have been surprised only too many times. If it can be used, it will be used. And abused. That's where we come in. Because if it can go wrong, it will go wrong. And we have to protect (unexperienced) developers against themselves. So that's the real question: not if it will be used, but how it could possibly be used and what can go wrong.

We've had the pleasure of dealing with a lot of functions that do not pose such dilemmas like: str_replace, addslashes. Those are merely a technical challenge.

But my point is: if it's technically possible, doesn't mean we should always do it. When porting functions to client side we have to ask ourselves questions like:
... [more] - is there a clear & (reasonably) undisputed outcome
- can it do any (unintended) harm
- etc

Now, back to exit: Besides the fact that this would be problematic for my testsuite (no reason not to port), for me this is a function that's in the gray area, it's not as 'black' as getmygid (which will never ever be ported), it's not as 'white' as str_replace. It has it's sharp edges. But I would be willing to add your implementation to the library, and see what responses we get. Maybe exclude it from the default compile.

#65. Brett Zamir on 31 December 2008

Gravatar.com: Brett ZamirAs in PHP, I think exit() is handy for quick debugging. That's mostly how I'd use it. Of course, if you're in a function, you can often use return (of course the function may be continue to be handled, but so might an error be caught).

One can just use throw '', as mentioned, but it is annoying that it pollutes your Error Console at a time when you're already looking trying to figure out errors.

While it is nice to consider the potential uses of course, I tend to think:

1) People are usually creative enough to think of uses for functionalities that we do not envision. How many times do I find myself wishing a programmer or API creator had just implemented the spec (or otherwise offer greater access) and not tried to anticipate all uses or prevent what in their opinion was "bad" coding (though which actually had its uses). We consumers should be able to take the risks we want, as long as they are wholly unnecessary ones.
2) For the sake of students of JavaScript, seeing the closest equivalents for familiar constructs is itself instructive.
3) Including functions may lead to proposals for better implementations

Very good point about events continuing to fire--Although the following wouldn't stop events like setInterval(), synthetic events, etc., it registers capturing event listeners for all of the major events on the window object, with these stopping further propagation (could be made to prevent default behavior too by uncommenting e.preventDefault()).

function exit (status) {
if (typeof status === 'string') {
alert(status);
}
window.addEventListener('error', function (e) {e.preventDefault();e.stopPropagation();}, false);
var handlers = [
'copy', 'cut', 'paste',
 
'beforeunload', 'blur', 'change', 'click', 'contextmenu', 'dblclick', 'focus', 'keydown', 'keypress', 'keyup', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'resize', 'scroll',
 
'DOMNodeInserted', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument', 'DOMNodeInsertedIntoDocument', 'DOMAttrModified', 'DOMCharacterDataModified', 'DOMElementNameChanged', 'DOMAttributeNameChanged', 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', 'online', 'offline', 'textInput',
 
'abort', 'close', 'dragdrop', 'load', 'paint', 'reset', 'select', 'submit', 'unload'
];
function stopPropagation (e) {
e.stopPropagation();
// e.preventDefault(); // Stop for the form controls, etc., too?
}
for (var i=0; i < handlers.length; i++) {
window.addEventListener(handlers[i], stopPropagation, true);
}
throw '';
}


Maybe there's a better way (any DOM copy/import operations that could be done for the window or document which lose events in the process?)

#64. Onno Marsman on 30 December 2008

Gravatar.com: Onno Marsmanexit or die by throwing something would not stop events from firing. Most javascript programs use events to keep on going and throwing something won't stop that, you could even catch an exit like this if you'd want to... I don't see any similarity with PHP here in which an exit does make sense because it's not event driven. And I would like to repeat an argument that I tend to use a lot: who would use this and in which context?


But very creative indeed.

... [more]
I think window.close() would be even more like exit in the sense that it says: "Just stop everything now!" but I wouldn't want to go there ;)

#63. Brett Zamir on 30 December 2008

Gravatar.com: Brett ZamirHi Kevin,

On a slippery slope to what? Are you worried about code that is more likely to be platform-specific?

In any case, I think you can include or exclude whatever you like, though I think it'd be great if you were able to still leave the functions that don't make it into the library in the comments at this site (or to your new site), so people searching the site might still find them if they wanted to use them.
... [more]
My personal thought is that people can always ignore what they don't want to use (and given the size and potential size of the project, I think people will have to pick only the ones they want anyways). But it's your call...

#62. Kevin on 30 December 2008

Gravatar.com: Kevin@ Brett Zamir: Previous discussions by the php.js core team lead to the fact that functions like die & register_shutdown_function are not in '_unported' but in '_notporting' instead.

I do think you have been very creative by taking the 'throw' approach to implement an exit function, and I guess it could work fine.

On the other hand, I feel we start down a slippery slope with these functions and my inner programmer is telling me to stick with notporting.
... [more]
Good arguments may very well change that. So bring it on.

#61. Brett Zamir on 20 December 2008

Gravatar.com: Brett ZamirLastly, before I hopefully exit from my comments on this, die() could also be defined the same, as it is an alias of exit()...

Brett

#60. Brett Zamir on 20 December 2008

Gravatar.com: Brett ZamirAlternatively, if one didn't wish errors to be polluting the Error console or give a chance to recover, the following could work:


exit('abc');
alert('Code never makes it here');
 
function exit (status) {
if (typeof status === 'string') {
alert(status);
}
window.addEventListener('error', function (e) {e.preventDefault();}, false);
throw '';
}

#59. Brett Zamir on 20 December 2008

Gravatar.com: Brett ZamirTo build on the last one a little, if you were afraid of try blocks catching the error (or not catching the error), this version throws a new class "ExitError" so it may be explicitly caught (or let to exit unlike perhaps other errors or exceptions).

function exit (status) {
window['ExitError'] = function (status) {
this.status = status;
... [more] };
if (typeof status === 'string') {
alert(status);
}
throw new ExitError(status);
}

try {
exit("It's all over");
}
catch (e) {
if (!(e instanceof Error)) {
throw e; // Can catch all other errors, but let 'exit' work
}
}

try {
exit("It's all over");
}
catch (e) {
if (e instanceof ExitError) {
alert('We can also catch the exit() if we want to');
}
}

#58. Brett Zamir on 20 December 2008

Gravatar.com: Brett ZamirHere's another one relating to exiting... (window.close() would not work since that is only for windows opened with window.open()). Although this is kind of cheating (by throwing an error), I think it gets the job done.

function exit (status) {
if (typeof status === 'string') {
alert(status);
... [more] }
throw status;
}
exit('abc');

#57. Brett Zamir on 20 December 2008

Gravatar.com: Brett ZamirArgh, sorry (with all the double-takes I do, you can see why I like wikis...) :)

Here's one to handle the function as a string:

function register_shutdown_function (cb) {
var args = Array.prototype.slice.call(arguments, 1);
if (typeof cb === 'string') {
cb = eval(cb);
}
window.addEventListener('unload', function () {cb.apply(null, args);}, false);
}

#56. Brett Zamir on 20 December 2008

Gravatar.com: Brett ZamirYeah, the 'caller' property on function is not standard (though it works in Explorer supposedly, so maybe that's why Rhino didn't like it.

Here's another one...

register_shutdown_function(function(first, middle, last) {alert('Goodbye '+first+' '+middle+' '+last+'!');}, 'Kevin', 'van', 'Zonneveld');
 
function register_shutdown_function (cb) {
var args = Array.prototype.slice.call(arguments, 1);
window.addEventListener('unload', function () {cb.apply(null, args);}, false);
}


I could try something with Mozilla-specific code when the whole application is shutting down, if you were open to using browser-specific stuff (for my uses, that is more cool for file_get_contents() type of functions too).

#55. Kevin on 17 December 2008

Gravatar.com: Kevin@ Brett Zamir: Wow, good work Brett! Especially he splice function, everything worked instantly. Perfect. The test-cases of the func_* functions won't work in the suite, however. Could be because of rhino.

#54. Brett Zamir on 17 December 2008

Gravatar.com: Brett ZamirThis is a bit better (and I've added two other functions, func_get_args() and func_get_arg(), below)

try {
func_num_args()
}
catch(e) {
alert(e)
}
 
function foo() {
$numargs = func_num_args();
alert("Number of arguments: "+$numargs+"\n");
}
 
function func_num_args () {
if (!arguments.callee.caller) {
throw ('Either you are using this in a browser which does not support the "caller" property on the Function object or you are calling this from a global context');
}
return arguments.callee.caller.arguments.length
}


function foo() {
$numargs = arguments.length;
if ($numargs >= 2) {
alert("Second argument is: " + func_get_arg(1) + "\n");
}
}
foo (1, 2, 3);
 
 
function func_get_arg (num) {
if (!arguments.callee.caller) {
throw ('Either you are using this in a browser which does not support the "caller" property or you are calling this from a global context');
}
if (num > arguments.callee.caller.arguments.length - 1) {
throw 'Argument number is greater than the number of arguments actually passed';
}
return arguments.callee.caller.arguments[num];
}


function foo() {
$numargs = arguments.length;
$arg_list = func_get_args();
for (var i = 0; i < $numargs; i++) {
alert("Argument "+i+" is: " + $arg_list[i] + "\n");
}
}
foo(1, 2, 3);
 
function func_get_args () {
if (!arguments.callee.caller) {
throw ('Either you are using this in a browser which does not support the "caller" property or you are calling this from a global context');
}
return Array.prototype.slice.call(arguments.callee.caller.arguments);
}

#53. Brett Zamir on 17 December 2008

Gravatar.com: Brett ZamirHere's another one in addition to array_splice: func_num_args()...


test ('a', 'b');
function a () {
alert(func_num_args()); // Gives '2'
}
 
function func_num_args () {
return arguments.callee.caller.arguments.length; // May not work in all JS implementations
}

#52. Brett Zamir on 17 December 2008

Gravatar.com: Brett ZamirThanks, Kevin, I love that unported list! I wish all projects were as orgainzed as this one (Btw, array_merge_recursive shouldn't be in the array section any longer though as someone did that recently; also on this page with the gold stars, under my name, you've listed me twice for the same function; I think it's very nice how you are recognizing people, btw)

I made an unported list myself for the arrays before I knew about your list... With the addition of the following submission, all remaining PHP array functions are about diffs, intersects, sorts, or seem to be not really suitable in JavaScript (except maybe for study purposes). Note that the following works with associative arrays (as with regular ones): as input and, if needed, as output. Again, I've commented out sections dealing with array-like objects in case anyone wants to add the code back in.


// Samples adapted from PHP manual
 
$input = ["red", "green", "blue", "yellow"];
$output = array_splice($input, 2);
// $input is now ["red", "green")
// $output is now ["blue", "yellow"]
 
$input = ["red", "green", "blue", "yellow"];
$output = array_splice($input, 1, -1);
// $input is now ["red", "yellow")
// $output is now ["green", "blue"]
 
$input = ["red", "green", "blue", "yellow"];
$output = array_splice($input, 1, $input.length, "orange");
// $input is now ["red", "orange")
// $output is now ["green", "blue", "yellow"]
 
$input = ["red", "green", "blue", "yellow"];
$output = array_splice($input, -1, 1, ["black", "maroon"]);
// $input is now ["red", "green",
// "blue", "black", "maroon")
// $output is now ["yellow"]
 
$input = ["red", "green", "blue", "yellow"];
$output = array_splice($input, 3, 0, "purple");
// $input is now ["red", "green",
// "blue", "purple", "yellow");
// $output is now []
 
// Own sample with associative array (PHP does not preserve numeric keys in input or output)
 
$input = {4:"red", 'abc':"green", 2:"blue", 'dud':"yellow"};
$output = array_splice($input, 2);
// $input is now {'abc':"green", 0:"red"} // Note: Order does get shifted in associative array input with numeric indices, since PHP behavior doesn't preserve keys, but I understand order is not reliable anyways
// $output is now {0:"blue", 'dud':"yellow"}
 
 
/* Dependency: is_int() */
function array_splice (arr, offst, lgth, replacement) {
function checkToUpIndices (arr, ct, key) {
// Deal with situation, e.g., if encounter index 4 and try to set it to 0, but 0 exists later in loop (need to
// increment all subsequent (skipping current key, since we need its value below) until find unused)
if (arr[ct] !== undefined) {
var tmp = ct;
ct += 1;
if (ct === key) {
ct += 1;
}
ct = checkToUpIndices(arr, ct, key);
arr[ct] = arr[tmp];
delete arr[tmp];
}
return ct;
}
if (replacement && !(typeof replacement === 'object')) {
replacement = [replacement];
}
if (lgth === undefined) {
lgth = offst >= 0 ? arr.length - offst : -offst;
}
else if (lgth < 0) {
lgth = (offst >= 0 ? arr.length - offst : -offst) + lgth;
}
 
if (!(arr instanceof Array)) {
/*if (arr.length !== undefined) { // Deal with array-like objects as input
delete arr.length;
}*/

var lgt =0, ct=-1, rmvd = [], rmvdObj = {}, repl_ct=-1, int_ct=-1, returnArr = true, rmvd_ct=0, rmvd_lgth=0;
// rmvdObj.length = 0;
for (var key in arr) { // Can do arr.__count__ in some browsers
lgt += 1;
}
offst = (offst >= 0) ? offst : lgt + offst;
for (var key in arr) {
ct += 1;
if (ct < offst) {
if (is_int(key)) {
int_ct += 1;
if (parseInt(key, 10) === int_ct) { // Key is already numbered ok, so don't need to change key for value
continue;
}
checkToUpIndices(arr, int_ct, key); // Deal with situation, e.g.,
// if encounter index 4 and try to set it to 0, but 0 exists later in loop
arr[int_ct] = arr[key];
delete arr[key];
}
continue;
}
if (returnArr && is_int(key)) {
rmvd.push(arr[key]);
rmvdObj[rmvd_ct++] = arr[key]; // PHP starts over here too
}
else {
rmvdObj[key] = arr[key];
returnArr = false;
}
rmvd_lgth += 1;
// rmvdObj.length += 1;
 
if (replacement && replacement[++repl_ct]) {
arr[key] = replacement[repl_ct]
}
else {
delete arr[key];
}
}
// arr.length = lgt - rmvd_lgth + (replacement ? replacement.length : 0); // Make (back) into an array-like object
return returnArr ? rmvd : rmvdObj;
}
 
if (replacement) {
replacement.unshift(offst, lgth);
return Array.prototype.splice.apply(arr, replacement);
}
return arr.splice(offst, lgth);
}

#51. Kevin on 10 December 2008

Gravatar.com: Kevin@ Paulo Ricardo F. Santos: Hey Paulo, I was in the hospital, sorry for the delay man. Added your function. thx!

@ Brett Zamir: If you want you can also track our 'unported' function list:
http://trac.phpjs.org/projects/phpjs/browser/trunk/_unported

#50. Paulo Ricardo F. Santos on 04 December 2008

Gravatar.com: Paulo Ricardo F. SantosI'm again just to point out that both gettype() and is_float() have the same problem of is_int() - n.0 === n. ;)

#49. Paulo Ricardo F. Santos on 04 December 2008

Gravatar.com: Paulo Ricardo F. SantosMore functions ported:

// this one lacks only resource type
function gettype(mixed_var)
{
switch (type = typeof mixed_var) {
case 'boolean':
case 'string':
return type;
break;
case 'number':
return (is_float(mixed_var)) ? 'double' : 'integer';
break;
case 'object':
return (mixed_var instanceof Array) ? 'array' : ((mixed_var === null) ? 'NULL' : 'object');
break;
}
 
return 'unknown type';
}
 
// alias
function is_double(mixed_var)
{
return is_float(mixed_var);
}
 
function is_float(mixed_var)
{
return parseFloat(mixed_var * 1) != parseInt(mixed_var * 1);
}
 
// alias
function is_integer(mixed_var)
{
return is_int(mixed_var);
}
 
// alias
function is_long(mixed_var)
{
return is_float(mixed_var);
}
 
function is_scalar(mixed_var)
{
return /boolean|number|string/.test(typeof mixed_var);
}

Whew! :P

#48. Brett Zamir on 03 December 2008

Gravatar.com: Brett ZamirWhoops--not sure how I missed you already had compact()--however, maybe the eval() ability adds something potentially (albeit with reservations)...

#47. Brett Zamir on 03 December 2008

Gravatar.com: Brett ZamirCongrats on passing the 200 mark!

Here's compact(), but with what I believe is an inevitable limitation on scope. Example is based on the PHP manual's, but with two extra bits of data added.

var city  = "San Francisco";
var state = "CA";
var event = "SIGGRAPH";
var location_vars = ["city", "state"];
var obj = {
prop: 'val'
};
var arr = [3, 4, 5];
var result = compact('arr[2]', 'obj.prop', "event", "nothing_here", location_vars);
 
// Gives:
{
'arr[2]': '5',
'obj.prop': 'val',
'event': 'SIGGRAPH',
'city': 'San Francisco',
'state': 'CA'
}


// Will only work for variables in the global scope
function compact () {
var a = Array.prototype.concat.apply(null, arguments);
a.shift()
for (var i=0, o={}; i < a.length; i++) {
try {
/* Alternative to eval(), but doesn't work with object accessors, etc.
if (window[a[i]] !== undefined) {
o[a[i]] = window[a[i]];
}
*/

o[a[i]] = eval(a[i]);
}
catch(e) {
}
}
return o;
}

#46. Kevin on 03 December 2008

Gravatar.com: Kevin@ Paulo Ricardo F. Santos: Excellent! Added!

#45. Paulo Ricardo F. Santos on 03 December 2008

Gravatar.com: Paulo Ricardo F. SantosMore two functions:

function microtime(get_as_float)
{
var now = new Date().getTime() / 1000;
var s = parseInt(now);
 
return (get_as_float) ? now : (Math.round((now - s) * 1000) / 1000) + ' ' + s;
}

As far I've tested, it's Ok. The only difference is that JS supports only 3 decimal digits for milliseconds. ;/

function getdate(timestamp)
{
var _w = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var _m = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
var d = (typeof timestamp == 'number') ? new Date(timestamp * 1000) : new Date();
var w = d.getDay();
var m = d.getMonth();
var y = d.getFullYear();
var r = [];
 
r['seconds'] = d.getSeconds();
r['minutes'] = d.getMinutes();
r['hours'] = d.getHours();
r['mday'] = d.getDate();
r['wday'] = w;
r['mon'] = m + 1;
r['year'] = y;
r['yday'] = Math.floor((d - (new Date(y, 0, 1))) / 86400000);
r['weekday'] = _w[w];
r['month'] = _m[m];
r[0] = parseInt(d.getTime() / 1000);
 
return r;
}

This one seems to match the exact PHP behavior. As far I known, there's no locale influence, so, it might be correct.

As always, improvements are welcome. :)

Thanks for your thanks, Kevin. I'm glad to contribute with this brilliant project. =)

#44. Kevin on 03 December 2008

Gravatar.com: Kevin@ Paulo Ricardo F. Santos: Thanks for all of your hard work Paulo!

#43. Paulo Ricardo F. Santos on 03 December 2008

Gravatar.com: Paulo Ricardo F. SantosOne more contribution - yep, I'm addicted:

function get_headers(url, format)
{
var req = null;
 
try {
req = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
req = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
try {
req = new XMLHttpRequest();
} catch(e) {
return false;
}
}
}
 
req.open('HEAD', url, false);
req.send(null);
 
tmp = req.getAllResponseHeaders().split('\n');
headers = {0 : req.status + ' ' + req.statusText};
 
for (i in tmp) {
pair = tmp[i].split(':', 2);
headers[(format) ? pair[0] : headers.length] = (pair[1]+'').replace(/^\s+|\s+$/g, '');
}
 
return headers;
}

In my tests all it's Ok, lacks only the HTTP/version of HTTP response (key 0). Anyway, it's open to improvements. :)

8)

#42. Paulo Ricardo F. Santos on 02 December 2008

Gravatar.com: Paulo Ricardo F. SantosHmm, to match the correct PHP behaviour, define() might be as follows:

function define(name, value)
{
if (/boolean|number|null|string/.test(typeof value) !== true) {
return false;
}

return (window[name] = value) !== undefined;
}


Sorry for the flood. =/

#41. Paulo Ricardo F. Santos on 02 December 2008

Gravatar.com: Paulo Ricardo F. SantosOops, the last one is bogus, this one fixes the problem:

function constant(name)
{
if (window[name] === undefined) {
return null;
}
 
return window[name];
}


Also, another contribution:

function define(name, value)
{
if (value === undefined) {
return false;
}

return (window[name] = value) !== undefined;
}


It lacks the third parameter - it's not possible to implement in JS - but remains useful, a time that could be used to declare constants in browsers that don't support the 'const' keyword. :)

#40. Paulo Ricardo F. Santos on 02 December 2008

Gravatar.com: Paulo Ricardo F. SantosOne more:

function constant(name)
{
return window[name] || null;
}


=)

#39. Paulo Ricardo F. Santos on 02 December 2008

Gravatar.com: Paulo Ricardo F. SantosAnother one, to quote meta characters:

function quotemeta(str)
{
return (str+'').replace(/([.\\+*?[^\]$()])/g, '\\$1');
}


;-)

#38. Paulo Ricardo F. Santos on 01 December 2008

Gravatar.com: Paulo Ricardo F. SantosAnother minor contribution (an alias):

function chop(str, charlist)
{
return rtrim(str, charlist || null);
}


;)

#37. Kevin on 01 December 2008

Gravatar.com: Kevin@ Paulo Ricardo F. Santos: Thanks a lot man! Nice piece of code! There is one issue with it, but I think it's better that I put it on your function's comment page.

#36. Paulo Ricardo F. Santos on 01 December 2008

Gravatar.com: Paulo Ricardo F. SantosHere's my first function:

function chunk_split(body, chunklen, end)
{
if (chunklen < 1) {
return false;
}
 
var result = '', chunklen = chunklen || 76, end = end || '\r\n';
 
while (body.length > chunklen) {
result += body.substring(0, chunklen) + end;
body = body.substring(chunklen);
}
 
return result + body + end;
}


Improvements are welcome. :)

#35. Kevin on 18 November 2008

Gravatar.com: Kevin@ Brett Zamir: Very neat! Thanks man. Examples work perfectly.

#34. Brett Zamir on 16 November 2008

Gravatar.com: Brett ZamirHere's another one with code to test first, again based on the PHP manual examples.

function odd($var) {
return($var & 1);
}
 
function even($var) {
return(!($var & 1));
}
 
$array1 = {"a":1, "b":2, "c":3, "d":4, "e":5};
$array2 = [6, 7, 8, 9, 10, 11, 12];
 
// Use PHP-JS print_r() or something else on this for your testing?
array_filter($array1, odd);
array_filter($array2, even);


function array_filter (arr, cb) {
var retObj={};
for (var k in arr) {
if (cb(arr[k])) {
retObj[k] = arr[k];
}
}
return retObj;
}

#33. Brett Zamir on 16 November 2008

Gravatar.com: Brett ZamirForgot to note that there is again a dependency on is_int()

#32. Brett Zamir on 16 November 2008

Gravatar.com: Brett ZamirHi Kevin,

Following is an array_merge() to work with associative arrays/objects as well as arrays. If only arrays are supplied, it returns an array.

I've also included two equivalent examples based on the PHP manual page for you to test with (if you like):

$array1 = {"color" : "red", 0:2, 1:4};
$array2 = {0:"a", 1:"b", "color" : "green", "shape" : "trapezoid", 2:4};
$result = array_merge($array1, $array2);
 
$array1 = {};
$array2 = {1 : "data"};
$result = array_merge($array1, $array2);




function array_merge() {
var args = Array.prototype.slice.call(arguments);
for (var i=0, retArr=true; i < args.length; i++) {
if (!(args[i] instanceof Array)) {
retArr=false;
break;
}
}
if (retArr) {
return args;
}
var retObj = {};
for (var i=0, ct=0; i < args.length; i++) {
if (args[i] instanceof Array) {
for (var j=0; j < args[i].length; j++) {
retObj[ct++] = args[i][j];
}
}
else {
for (var k in args[i]) {
if (is_int(k)) {
retObj[ct++] = args[i][k];
}
else {
retObj[k] = args[i][k];
}
}
}
}
return retObj;
}


Thanks again for your work!

#31. Brett Zamir on 15 November 2008

Gravatar.com: Brett ZamirNo need for patience on my part--glad to know it goes through some good review... Thanks again for all of your work on it!

#30. bassam essa on 14 November 2008

Gravatar.com: bassam essagood work, thanks for every one

#29. Kevin on 13 November 2008

Gravatar.com: Kevin@ Brett Zamir: Sorry, I totally missed that last block :| Thanks for your patience, the function is now in php.js. Thanks!

#28. Brett Zamir on 11 November 2008

Gravatar.com: Brett ZamirAlso, shouldn't is_int() be kept in place of !isNaN()?

A key could be a float, so I think that ought to be treated as an object (i.e., preserve the key as a float regardless of the preserve_keys argument) rather than as an array?

#27. Brett Zamir on 10 November 2008

Gravatar.com: Brett ZamirHi Kevin,

You need to keep the text I had at the end of the function to handle plain arrays (which keeps them as plain arrays):

if (lgth === undefined) {
return arr.slice(offst);
}
else if (lgth >= 0) {
return arr.slice(offst, offst + lgth);
}
else {
return arr.slice(offst, lgth);
}


If you want to delete or comment out the

if ('callee' in arr && 'length' in arr) {
arr = Array.prototype.slice.call(arr);
}


...go ahead. That was intended for the arguments object (which wouldn't get changed by reference). That is another contribution which is intended to handle the JavaScript-style array-like objects (arguments at least), so to mimic the PHP-style without fear of unintended effects, you could comment that out as well (My personal preference is to comment out rather than delete since it could be handy to have there for people who want to use the PHP-equivalent functions on common JavaScript constructs).

best wishes, Brett

#26. Kevin on 09 November 2008

Gravatar.com: Kevin@ Brett Zamir: The function now looks like this:

function array_slice(arr, offst, lgth, preserve_keys) {
// http://kevin.vanzonneveld.net
// + original by: Brett Zamir
// * example 1: array_slice(["a", "b", "c", "d", "e"], 2, -1);
// * returns 1: {0: 'c', 1: 'd'}
// * example 2: array_slice(["a", "b", "c", "d", "e"], 2, -1, true);
// * returns 2: {2: 'c', 3: 'd'}
 
if ('callee' in arr && 'length' in arr) {
arr = Array.prototype.slice.call(arr);
}

if (!(arr instanceof Array) || (preserve_keys && offst != 0)) { // Assoc. array as input or if required as output
var lgt =0, newAssoc = {};
for (var key in arr) {
//if (key !== 'length') {
lgt += 1;
newAssoc[key] = arr[key];
//}
}
arr = newAssoc;

offst = (offst < 0) ? lgt + offst : offst;
lgth = lgth == undefined ? lgt : (lgth < 0) ? lgt + lgth - offst : lgth;

var assoc = {};
var start = false, it=-1, arrlgth=0, no_pk_idx=0;
for (var key in arr) {
++it;
if (arrlgth >= lgth) {
break;
}
if (it == offst){
start = true;
}
if (!start) {
continue;
}
++arrlgth;
if (!isNaN(key) && !preserve_keys) {
assoc[no_pk_idx++] = arr[key];
} else {
assoc[key] = arr[key];
}
}
//assoc.length = arrlgth; // Make as array-like object (though length will not be dynamic)
return assoc;
}
}


But the first example is failing. And though the length item may be handy, it's asstraying from the PHP-way, which may leed to unexpected behaviour when developers implement this.

Also i notices the:
if ('callee' in arr && 'length' in arr) {
arr = Array.prototype.slice.call(arr);
}


Which changes the original argument arr by references. Also unexpected & undesired behaviour I think.

#25. Brett Zamir on 04 November 2008

Gravatar.com: Brett ZamirIncidentally, if you did decide to allow array-like objects, adding the following code at the very top of the function could work for the 'arguments' object:

if ('callee' in arr && 'length' in arr) {
arr = Array.prototype.slice.call(arr);
}


but I don't know how one could reliably iterate through array-like objects like DOM Node lists without also grabbing the methods (one could test that the property was numeric, but one would need code to distinguish between such cases as these where the non-numeric items should be avoided and other cases (as in associative arrays) where they would be kept).

#24. Brett Zamir on 04 November 2008

Gravatar.com: Brett ZamirHi Kevin,

If you don't want a length property added to the object (or don't want to accept them), just remove this line:

assoc.length = arrlgth; // Make as array-like object (though length will not be dynamic)


and the 'if' condition (but not the code inside):

if (key !== 'length') {
}


However, I thought it could be useful to allow for array-like objects, which, while they don't preserve the 'length' property dynamically or have array methods, still can be iterated (like the arguments object): http://books.google.com/books?id=VOS6IlCsuU4C&pg=PA122&lpg=PA122&dq=definitive+guide+javascript+%22array-like+objects%22&source=bl&ots=wHOcIU9yyX&sig=0fzUC3jnWDanqoOXvf4kY-4QtfA&hl=en&sa=X&oi=book_result&resnum=5&ct=result#PPA122,M1

But, I'd understand it if you wanted to forgo that...

#23. Kevin on 03 November 2008

Gravatar.com: Kevin@ Brett Zamir: Hi Brett. I've tried to implement it, but running an example:

array_slice(["a", "b", "c", "d", "e"], 2, -1, true)


returned:
Array
(
[2] => c
[3] => d
[length] => 2
)


Instead of the expected:
{2: 'c', 3: 'd'}

#22. Brett Zamir on 02 November 2008

Gravatar.com: Brett ZamirOk, trying once more (and fixing bug with previous)...

/*Dependency: is_int() */
function array_slice(arr, offst, lgth, preserve_keys) {
if (!(arr instanceof Array) || (preserve_keys && offst != 0)) { // Assoc. array as input or if required as output
var lgt =0, newAssoc = {};
for (var key in arr) {
if (key !== 'length') {
lgt += 1;
newAssoc[key] = arr[key];
}
}
arr = newAssoc;
 
offst = (offst < 0) ? lgt + offst : offst;
lgth = lgth == undefined ? lgt : (lgth < 0) ? lgt + lgth - offst : lgth;
 
var assoc = {};
var start = false, it=-1, arrlgth=0, no_pk_idx=0;
for (var key in arr) {
++it;
if (arrlgth >= lgth) {
break;
}
if (it == offst){
start = true;
}
if (!start) {
continue;
}
++arrlgth;
if (is_int(key) && !preserve_keys) {
assoc[no_pk_idx++] = arr[key];
}
else {
assoc[key] = arr[key];
}
}
assoc.length = arrlgth; // Make as array-like object (though length will not be dynamic)
return assoc;
}
if (lgth === undefined) {
return arr.slice(offst);
}
else if (lgth >= 0) {
return arr.slice(offst, offst + lgth);
}
else {
return arr.slice(offst, lgth);
}
}

#21. Brett Zamir on 02 November 2008

Gravatar.com: Brett ZamirSorry, resubmitting function with code coloring on (also in case you otherwise lose spacing), as well as examples:

$input = ['a', 'b', 'c', 'd', 'e'];
$output = array_slice($input, 2); // returns 'c', 'd', and 'e'
$output = array_slice($input, -2, 1); // returns 'd'
$output = array_slice($input, 0, 3); // returns 'a', 'b', and 'c'
$output = array_slice($input, 2, -1); // returns array with 'c' and 'd'
$output = array_slice($input, 2, -1, true); // returns array-like object: {2:'c', 3:'d', length:2}
 
function array_slice(arr, offst, lgth, preserve_keys) {
if (preserve_keys && offst != 0) { // Can go with genuine array if offst is 0
offst = (offst < 0) ? arr.length - offst : offst;
lgth = !lgth ? arr.length : (lgth < 0) ? arr.length + lgth : lgth;
var assoc = {};
for (var i=offst, arrlgth=0; i < lgth; i++, arrlgth++) {
assoc[i] = arr[i];
}
assoc.length = arrlgth; // Make as array-like object (though length will not be dynamic)
return assoc;
}
if (lgth === undefined) {
return arr.slice(offst);
}
else if (lgth >= 0) {
return arr.slice(offst, offst + lgth);
}
else {
return arr.slice(offst, lgth);
}
}

#20. Brett Zamir on 02 November 2008

Gravatar.com: Brett ZamirWell, here's one example where I think a wiki would have been useful--incomplete code that could have invited others to build upon it (or the original author to correct/improve upon it). I really think a wiki holds the best promise of ensuring anyone can contribute at any time and see their contribution immediately available and integrated to make a available as a starting point for others (though I think it would still be great if you were to continue to compile "authorized" versions).

Anyways, as far as array_slice, in the case of this function, PHP also does not support associative arrays. However, I did add the one missing argument, preserve_keys, which must return an array-like object in JavaScript (though it is still a numeric array in PHP) if this argument is true (and the offset is not 0).

function array_slice(arr, offst, lgth, preserve_keys) {
... [more] if (preserve_keys && offst != 0) { // Can go with genuine array if offst is 0
offst = (offst < 0) ? arr.length - offst : offst;
lgth = !lgth ? arr.length : (lgth < 0) ? arr.length + lgth : lgth;
var assoc = {};
for (var i=offst, arrlgth=0; i < lgth; i++, arrlgth++) {
assoc[i] = arr[i];
}
assoc.length = arrlgth; // Make as array-like object (though length will not be dynamic)
return assoc;
}
if (lgth === undefined) {
return arr.slice(offst);
}
else if (lgth >= 0) {
return arr.slice(offst, offst + lgth);
}
else {
return arr.slice(offst, lgth);
}
}

#19. p on 22 October 2008

Gravatar.com: phmm,

trying to run some simple samples with the namespaced version but only getting error....
- FF Errorconsole says : $P is not defined
- IE says $P is null or not an object
... [more] The UNnamespaced version works with no errors

I'm probably overlooking something (simple) but dunno what....... :-(

#18. Kevin on 20 October 2008

Gravatar.com: Kevin@ Brett Zamir: Thank you for the functions. Unfortunately they do not support associative arrays (javascript objects). And we really need that for php compatibility.
About the wiki: We're working on http://phpjs.org, which will have a lot of cool features once it's done. We also have a developer wiki, so we're pretty set on that. Thanks though.

#17. Brett Zamir on 18 October 2008

Gravatar.com: Brett ZamirHi,

Just wanted to offer the following functions. Perhaps someone clever can think of a better way to do them, but I offer them for what it's worth...

function array_slice(arr, offst, lgth) {
if (lgth === undefined) {
return arr.slice(offst);
}
else {
if (lgth > 0) {
return arr.slice(offst, offst + lgth);
}
else if (lgth < 0) {
return arr.slice(offst, lgth);
}
}
}


function array_merge(arr) {
var args = Array.prototype.slice.call(arguments, 1);
return Array.prototype.concat.apply(arr, args);
}


Again, I make my offer to use the wiki at http://javascript.wikia.com/wiki/PHP-Javascript . Thanks for collecting these great treasures!

#16. Stefan Sturm on 06 October 2008

Gravatar.com: Stefan Sturm@Kevin:

Great to hear...

Thanks for your work,
... [more] Stefan Sturm

#15. Kevin on 06 October 2008

Gravatar.com: Kevin@ Stefan Sturm: We have plans of changing to docBlock. We first have to change all of our test / compiling scripts to use one class. Then we can make the changes to that class, then we can change all the source files. So it's quite a big job for which I would require some vacancy. But know that it's definitely on our minds okay?

#14. Stefan Sturm on 05 October 2008

Gravatar.com: Stefan SturmHello,

whats about changing the doc style, so we can use it modern IDE'S like Netbeans?

Greetings,
... [more] Stefan Sturm

#13. Kevin on 02 October 2008

Gravatar.com: Kevin@ Chris: Thank you but I feel that won't be nescessary. We working on http://phpjs.org, which will obviously have better features than my blog, and will be automatically synced with source changes in SVN.

#12. Chris on 02 October 2008

Gravatar.com: ChrisThe associated link points to a wiki page dedicated to this cause, courtesy of the wiki's founder. I have recently adopted the wiki and am gladly offering it as a means to take a load off of Kevin, speed up development, and (most importantly ;) gain contributors.

#11. Kevin on 27 August 2008

Default avatar:Kevin@ Jackson: Thank you. I've been busy writing a new testsuite that should help development a lot. Expect more updates.

#10. Jackson on 14 August 2008

Default avatar:JacksonHey, I love this project very much, very convenient, please keep going to update, thanks :)

#9. Kevin on 16 June 2008

Default avatar:Kevin@ Charel B: Well there is file_get_contents()... ;) Thanks.

#8. Charel B on 15 June 2008

Default avatar:Charel BCould you also implement the PHP File System for js? that would be great :D

var d=document;
var w=write;
d.w("Great Work");

#7. Sincklation on 17 April 2008

Default avatar:SincklationGreat Work !

#6. Kevin on 08 April 2008

Default avatar:Kevin@ Weston Ruter: Yep, we're in the midst of moving php.js to its own publicly available SVN repository (not Google, but still). We're also still in negotiation and hope to obtain the phpjs.com domain name, so we can build a dedicated site around this project. It should resemble the php.net site and offer special features like customizing your own php.js version for the functions you need, etc.

#5. Weston Ruter on 08 April 2008

Default avatar:Weston RuterWhat about hosting this PHP.JS project on Google Code? It'd be great to have a more dedicated location to this great effort.

#4. Kevin on 02 April 2008

Default avatar:Kevin@ _argos: Good to have you back waldo :) Two great contributions to this project, awesome! thanks

#3. _argos (waldo malqui silva) on 01 April 2008

Default avatar:_argos (waldo malqui silva)Hi Kevin:

I'm back :p, for my work I need these 2 functions ip2long and long2ip I sent U.

function ip2long ( ip_address ) {
var output = false;
 
if ( ip_address.match ( /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) ) {
var parts = ip_address.split ( '.' );
var output = 0;
 
output = ( parts [ 0 ] * Math.pow ( 256, 3 ) ) +
( parts [ 1 ] * Math.pow ( 256, 2 ) ) +
( parts [ 2 ] * Math.pow ( 256, 1 ) ) +
( parts [ 3 ] * Math.pow ( 256, 0 ) );
}
 
return output;
}
 
function long2ip ( proper_address ) {
var output = false;
 
if ( !isNaN ( proper_address ) && ( proper_address >= 0 || proper_address <= 4294967295 ) ) {
output = Math.floor (proper_address / Math.pow ( 256, 3 ) ) + '.' +
Math.floor ( ( proper_address % Math.pow ( 256, 3 ) ) / Math.pow ( 256, 2 ) ) + '.' +
Math.floor ( ( ( proper_address % Math.pow ( 256, 3 ) ) % Math.pow ( 256, 2 ) ) / Math.pow ( 256, 1 ) ) + '.' +
Math.floor ( ( ( ( proper_address % Math.pow ( 256, 3 ) ) % Math.pow ( 256, 2 ) ) % Math.pow ( 256, 1 ) ) / Math.pow ( 256, 0 ) );
}
 
return output
}

#2. Dharmavirsinh Jhala on 29 March 2008

Default avatar:Dharmavirsinh JhalaGreat work...!
PHP.JS has been part of my common javascript library and now it has namespace too..
http://blogs.digitss.com

#1. BTM on 29 March 2008

Default avatar:BTMVery nice, I can see that I will be using some of the funcions ported in my projects from now on :D