PHP.JS
PHP.JS is an open source project in which we try to port PHP functions to
JavaScript.
By including the PHP.JS library in your own projects, you can use your
favorite PHP functions client-side.
No server component required. To use PHP.JS you can either:
- Download the default package
- Customize & download a package (only select what you need)
- Goto one of the Functions and just Copy-Paste it
Using PHP.JS may speed up development for PHP developers who are increasingly confronted with client-side technology.
It also offers added functionality because JavaScript does not natively support higher-level functions such as: md5(), strip_tags(), strtotime(), number_format(), wordwrap().
PHP.JS is nothing fancy like jQuery—we're just offering PHP functions, with all of their original flaws and benefits for whomever needs them.
Featured Functions
md5()
Generate hashes client side to take load off of your server.
date()
Date-formatting, just like you know it from PHP
base64_decode()
Safe & easy data transport, decode messages in JavaScript that where encoded by PHP.
The History of PHP.JS
Developer Kevin van Zonneveld was once working on a project with a lot of client(JS) /
server(PHP) interaction, and he found himself coding PHP functions (like base64_decode &
urldecode) in JavaScript to smoothen communication between the two languages.
He stored the stored the functions in a file called php.js which was included in the
project. But even when the project was done, it remained fun trying to port PHP functions
to JavaScript, and so the library grew.
Kevin decided to share the little library on his blog, triggering the enthusiasm
of a lot of PHP developers longing for PHP functionality in JavaScript. The project was
open sourced in 2008, and many people contributed their own functions in the comments sections
of Kevin's blog.
It was decided that the library deserved a bigger home and a face of its own, and so
the PHP.JS core team (which at that time consisted of Michael White, Felix Geisendörfer,
Philip Peterson and Kevin) developed the phpjs.org website.
Different core members have come & gone but there has always been a select group pushing
the project forward.
Late 2008
Brett Zamir started
contributing and did't stop. In April 2009 he was responsible for over 245
different PHP functions and has had many ideas considering PHP.JS' future.
Because the library became too big to include at once, and having users copy-paste
functions to their projects was nothing short of creating a maintenance hell,
Kevin started working on a
compiler tool that
allows programmers to select ONLY the functions they need, and wrap them
up in a single php.js file.
This took away overhead and even allowed for easy upgrading.
And that's where we are now.
We are still trying to port and perfect more & more functions.
Want to help out & become a part of PHP.JS history? Why not add a comment with
new or better code? It's that easy.
Comments
Hi all. Contributing a much improved Levenshtein implementation. I happened to need to implement this the other day in C++, and it was trivial to port to JS. It's boith faster and massively more memory efficient.
With a little bit of cunning you can hold all the interim results in a 2xN matrix instead of NxN; leads to HUGE speedups as strings get larger (eg: when comparing two 100char strings your resultset is only 200 elements instead of 10,000)
Benchmarking shows this version is faster in all cases on IE and Chrome, and faster in all cases on FF (3.5) once you're dealing with comparisons of two 6-char strings or longer.
Memory allocation improvements speaks for themself, performance improvements range from 50% to 350% depending on browser/string input combinations. In general, the larger the strings, the larger the improvement.
(Kept the IE string/split trick in place). Hope it's useful...
Cheers.
function levenshtein(s1,s2){
if (s1 == s2) {
return 0; }
var s1_len = s1.length;
var s2_len = s2.length;
if (s1_len === 0) {
return s2_len; }
if (s2_len === 0) {
return s1_len; }
var split = false;
try{
split=!('0')[0];
} catch(e){
split=true; // Earlier IE may not support access by string index
}
if (split){
s1 = s1.split('');
s2 = s2.split('');
}
var v0 = new Array(s1_len+1);
var v1 = new Array(s1_len+1);
var s1_idx=0, s2_idx=0, cost=0;
for (s1_idx=0; s1_idx<s1_len+1; s1_idx++) {
v0[s1_idx] = s1_idx;
}
var char_s1='', char_s2='';
for (s2_idx=1; s2_idx<=s2_len; s2_idx++) {
v1[0] = s2_idx;
char_s2 = s2[s2_idx - 1];
for (s1_idx=0; s1_idx<s1_len;s1_idx++) {
char_s1 = s1[s1_idx];
cost = (char_s1 == char_s2) ? 0 : 1;
var m_min = v0[s1_idx+1] + 1;
var b = v1[s1_idx] + 1;
var c = v0[s1_idx] + cost;
if (b < m_min) {
m_min = b; }
if (c < m_min) {
m_min = c; }
v1[s1_idx+1] = m_min;
}
var v_tmp = v0;
v0 = v1;
v1 = v_tmp;
}
return v0[s1_len];
}
@Raja mm, in the sidebar of the site, you should see "Who uses PHP.JS", but other than that, PHP.JS basically just uses (or can use) the PHP documentation itself ( http://www.php.net/manual/en/funcref.php ) as well as a few test cases of its own (available on each of our function pages).
So, PHP.JS is not really a framework which requires functions to be used together (outside of some functions which may call on others, or some functions which coordinate between each other with a global variable, such as how our ini_set() implementation can make functions dependent on ini behaviors), so each function really should be mostly sufficient unto itself.
But please feel free to raise any specific questions...
@ Brett Zamir: Some compelling arguments. However I really felt Douglass wasn't open to our proposal. If you'd like to give it another shot with I'm feel free, but I just patched the compiler to circumvent it.
I hope Doug may be open to at least patches which added an option (especially if non-default) to tolerate (as he states it in other cases) features we want (as for JSLint), or to optionally adjust things more strictly as with JSMin (probably not too many such cases).
While I stand in really great appreciation for his work in supporting best practices (and many other things), I really don't like it when (especially universally appealing) projects seal off your choices entirely, even if well-intentioned, as no one can anticipate all use cases. (That's one reason I'm glad you're open to our making available even the experimental PHP.JS functions in some manner.) (Even here, I wonder if he's just not inclined to go through the trouble himself, while still being potentially open to a patch.)
While it's great to make certain things more difficult for people, if they'd be too tempted to fall into traps (I know I've benefited from JSLint doing this to me, or IDE's, etc.), I don't think it is reasonable to make things impossible. Fixing this won't dramatically increase the size of any library using JSMin (we had one line of code in our whole library, entailing one additional space for us), and there's nothing wrong with having a script tag contain script contents. Actually, his own Yahoo performance team points out (at http://developer.yahoo.com/performance/rules.html and http://yuiblog.com/blog/2006/11/28/performance-research-part-1/ ) that reducing the number of HTTP requests is top of the list of enhancing performance, so in cases where a script only applies to one page at a site, using a script tag in the head (not obtrusive, like inline events mind you) would even be ideal. This is not to mention the use case I ran into, testing--it's a pain to have to save and reference an external file all the time.
I think this is something which should be doable with an optional patch, and if we do the work, argue our case, and are open to not being the default behavior, it almost always seems there ends up being a place for it. :)
(As for jslint, I really hope to add an option at some point for allowing switch fall-throughs--switches are just too elegant when used properly.)
@ Brett Zamir: Okay that sounds great! Storing such a file in _expirimental is okay. It's totally separated from any intelligence like compilers & stuff. I also pointed the ohloh.net source scanning to the ./functions/ dir. Much better.
About the jsmin 'bug' Douglass Crockford thinks jsmin should never be used inline and hence will not fix it.
Obviously we all agree that it's bad practice, but it's also an easy fix. So what the question really comes down to is this:
- Do we want people their code to break in an early stage when they try bad practice, so they find the good practice and have become a better coder that instant
- Do we want to support maximum fault tolerance so php.js will never break (even in the worst practice scenarios)
Kinda hard decision, but hopefully every developers will test their code before actually bringing it to production and find out they're doing it the wrong way before harm is done.
So now I'm thinking maybe we shouldn't even try to support JSMin features that JSMin itself doesn't want to support.
Sorry, one other thought--if we implement the output buffering in echo (the functions are already ready), we can use output buffering to capture and insert the result rather than needing to replace our implementation of echo with some other (though I still like the processing instruction). Also for Mozilla, E4X will work nicely for inner-tag evaluations such as for attributes, attribute values, or element-names; otherwise, one will have to use echo() which is still not too bad...
One inadequacy as a templating solution though--real processing instructions can't occur anywhere and everywhere, like in the middle of a tag or an attribute value--only where elements or comments could go... But it still might be compelling for the likes of Firefox extensions, especially as the latter already works with E4X which could mix nicely with the above.
(Btw, is there a problem for me to put an XHTML file in SVN under experimental->language? I've got it working now with E4X too.)
@Kevin, Thanks, but if you do a replace, make sure it works for any case of "]]>" (not just "]]>>>"), as the likes of the following will also cause the problem:
if (a[b[0]]> 5)
Also, while I'm not recommending this for Mirko (as it only currently works with true XHTML--though there's a site I can't access now which supposedly offers an HTML implementation of XPath), the following code can trigger the evaluation of blocks (processing instructions in XML)--with PHP.JS limitations of course (like echo needing parentheses).
One nice thing about this approach is that it allows a pseudo-standard (i.e., the PHP way) to make templates in JavaScript, and also have it work in true XHTML (which does not allow document.write()). (A pity the PHP-echo short tags = ?> aren't valid XML, though can work--see below.)
However, this approach is necessarily insecure in relying on eval(), so there must be no direct use of user content (cross-site scripting (XSS)) in the processing instructions output to the user from the site (nor the ability for user data to insert processing instructions or other tags, of course). But it really does mimic PHP quite nicely.
Save the following with the ".xhtml" extension (won't work in Explorer but works for me in Mozilla-Firefox/Opera/Safari):
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Test replacing PHP blocks</title>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
// To work with echo(), we need a different implementation of echo() which returns the string (and doesn't append to the DOM, since we want to append it)
function echo (str) {
return str;
}
// Alternatively to echo(), or in addition, we could just define our own underscore (_) function which does this, thus not interfering with PHP
function _ (str) {
return str;
}
function populatePHPBlocks () {
var getXPathNodes = function (xpathText) {
if (document.evaluate) {
var procInsts = [];
var pis = document.evaluate(xpathText, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var pi = pis.iterateNext();
while(pi) {
procInsts.push(pi);
pi = pis.iterateNext();
}
return procInsts;
}
else {
try {
return document.documentElement.selectNodes(xpathText);
}
catch(e) {
throw "XPath is not supported by this browser.";
}
}
};
var procInsts = [];
var procInsts = getXPathNodes('//processing-instruction("php")');
for (var i=0; i < procInsts.length; i++) {
var txt = document.createTextNode(eval(procInsts[i].data));
procInsts[i].parentNode.replaceChild(txt, procInsts[i]);
}
var procInsts = [];
var procInsts = getXPathNodes('//processing-instruction("_")');
for (var i=0; i < procInsts.length; i++) {
var txt = document.createTextNode(eval('echo('+procInsts[i].data+')'));
procInsts[i].parentNode.replaceChild(txt, procInsts[i]);
}
}
window.addEventListener('load', function () {
populatePHPBlocks();
}, false);
//--><!]]></script>
</head>
<body>
<p><?_ 'He'+'llo' ?></p>
<p>Static text in between "Hello" and "world!"</p>
<p><?php _('world!'); ?></p>
<p><?php echo('Finished!'); ?></p>
</body>
</html>
@ Brett Zamir: OK that's pretty nasty. What I could do as a quick & dirty workround, is have the compiler replace
]]>>>
with
]] >>>
after minimizing. I've sent a mail to Douglas Crokford to inform him about the issue.
@ T. Wild: Thanks for chiming in!
@ mirko: There was a problem with the comment system that removed your tags. Fixed that, and we can now see that you're not doing it the right way. You should remember that you are still in JavaScript. The only thing we port is the functions. So after properly including a (small customized) package, you should be able to do:
echo('Hello');
But not
echo 'echo';
And also not
<?php echo('Hello'); ?>
from within a .js file.
@Kevin, I've wrapped it for now in parentheses (in SVN), which the minimizer will hopefully not remove, but I think the issue should still be addressed...
@Redge, the license is MIT, included with the packages and on the developer page: http://phpjs.org/pages/develop . Still, if Kevin is open to it, a dedicated "license" page might not be a bad idea.
@mirko, you might try pasting your code at http://pastebin.com/ and just giving us the URL to it. Have you checked whether the file is loading at all (e.g., using "alert()")? Have you looked in your browser's Error Console (in Firefox, it is available via the menu Tools->Error Console)?
@Kevin, I see that the minimized files can create a problem for true XHTML files if included within an XML CDATA section. In array_multisort(), there is this expression, "(((flags[arguments[j]] >>> 4) & (lFlag >>> 4)) > 0)", which when condensed, gives the CDATA closing sequence "]]>". While it is unlikely people will use a full minimized version within a single page (though I came across the issue while putting the file contents for convenience in my Firefox XUL Editor which I use for testing JavaScript), let alone a true XHTML page, I think it's better to fix it. Report the bug to the minimizer? Where are the file to do this?
@mirko
if you file is called test.js then you want to use
... type="text/javascript" src="test.js" ...
You should probably also check the path to the test.js file as well. The easiest thing to do is probably put the test.js file in the website root and to use
... type="text/javascript" src="/test.js" ...
Hope this helps a bit.
Great stuff! Nice work.
One question though: under what license are you releasing? You might want to display such information prominently.
Redge
I put
... script type="text/javascript" src="php.full.min.js"....
< ? php echo "hello"; ? >
the file is called test.php
but doesn't work.
@ shin: The PHP documentation should suffice, as we try to stick to their implementation as much as possible. So basically you can just take some functions you need (either by hand or with the compiler), and use them just like you would in PHP.
Also, examples available on most function pages.
Was there anything else that you had in mind specifically?
As far as sigils, redundant but perhaps visually convenient for those without syntax-coloring editors to distinguish variables from functions, etc. :) Actually, I find that somewhat annoying now when I work in PHP, but thankfully we don't have that problem in JavaScript--though if someone does actually like using it, JavaScript does permit the dollar sign in identifiers (as we do with the few cases of functions setting PHP globals: i.e., $http_response_header and optionally $php_errormsg )--it can be convenient and familiar to refer to $_GET in JavaScript for example, if import_request_variables() is used to set it).
I fully agree with you about the inconsistent conventions with regard to underscores and abbreviations--it's a real pain for learning the language the first time around. But again, many of us have "grown up on" PHP. We haven't all graduated with CS degrees or been studying since we were kids, nor do we have time to do so now, or, if we are going in that direction, we still like to build on what we know. Now that we're here, we want to use something convenient--something which we already know. PHP and JavaScript are such frequent combinations, it makes it quite natural to borrow the vocabulary we've already learned from the one to the other--all with the flexibility to adapt as we deem fit. If we start our own lexicon from scratch, we're introducing yet another JavaScript utility library--which requires added study to figure out the API, while for our main audience of PHP programmers (and there are quite a few), it's a no-brainer. And as a bonus for us, PHP is already well-documented, working in most cases just as well for us.
Obviously many people deal with the quirks of PHP and manage to remember addslashes has no underscore, while str_replace() does--language evolution, human or computer, is usually pretty messy and we're always stuck with the debate over the relative merits of streamlining and building from the ground up, or building on convention. Should a new language make an inherently safe "switch" structure (which explicitly requires fall-throughs perhaps), or should it just build on the well-familiar if unsafe C tradition? You could no doubt make a good case for the former, but sometimes people are more familiar with quirks and bad design that we consider it more advantageous to build upon them.
And we might also argue that we're helping people learn the PHP functions as well--not an entirely circular advantage given the independent usefulness of knowing PHP for the sake of programs already using it or for building a new one if you like PHP.
And finally, our license is MIT, so anyone can adapt our code to use other names or conventions.
We are admittedly probably stuck with not being able to add "missing" functions like hex2bin or br2nl ourselves which might fill in certain gaps in a PHP way, but would probably confuse some people who try to find the PHP docs for them (though the PHP docs already have suggested implementations for these already in the PHP docs' comments or on blogs, so they'd no doubt be able to find a "definition" for these non-functions anyways, but I don't think Kevin will want to add such functions). In any case, it's quite easy for someone to add their own implementation or provide one in JS for our users in the comments pages.
I think the other complaints of PHP do not pertain to our project at least. I believe PHP may be getting closures at some point, so maybe that will help with some scoping flexibility... At least in PHP you can encapsulate by refraining from using the global keyword. It's a lot easier to accidentally define a global in JS. The nice thing is that, using JavaScript, we can take advantage of all of its features like convenient closures, while falling back on our familiar PHP lexicon for frequently recurring utility-type tasks.
I'll happily extend your list of criticisms of PHP.
PHP's sigils are superfluous. Perl (from which PHP derives their use) utilizes sigils with respect to typing in order to denote scalar variables ($), arrays (@) and hashes (%). What does PHP use the $ sigil for? Everything but references (&). Why? Because.
PHP's standard library is extremely disorganized. What's the function to put a string into hexadecimal? bin2hex. What's the function to put a string into base64? base64_encode. How do you turn a base64 string back into a "binary" string? base64_decode. How do you turn a hexadecimal string back into a "binary" string? Oh wait... an explicit function doesn't exist for that. Not to mention the various inconsistent ways in which functions are named: afnctn(), afunction(), a_function(), et cetera ad nauseum. You already mentioned the inconsistent ordering of arguments.
PHP doesn't support integeral values larger than that of a signed 32 bit integer. Even better, when you overflow an int, you get a float back! Did you know pack() and unpack() support arguments for the unsigned ints that PHP doesn't support? Genius!
PHP doesn't use lexical scoping. Do you know how downright stupid and annoying that is?
PHP's thread support sucks. Period.
According to Cal Henderson at Flickr, PHP "leaks memory like a sieve." (http://www.afup.org/IMG/pdf/flickr_php.pdf) (of course this is an implementation issue)
Now, which of these most concerned me when I read about PHP.JS? The second one. If you want to make something like Boost for JavaScript, please do so. But again, why in the *world* would you model it after PHP's standard library? I would ask you to reconsider the focus of your project, but it's unlikely you'd do so.
Cheers.
@MercifulSweet: Many of the criticisms I have seen around PHP revolve around: Namespaces, Unicode, register globals, and magic quotes.
Besides the fact that these issues are now mostly addressed in PHP (or to be resolved in PHP6) and, more significantly here, we don't really have these problems in JavaScript, as the library does not pollute the global namespace (except in the non-namespaced version of course or in a few cases if the function itself is designed to do so), and we do not prohibit people from using a namespace (on the contrary, Kevin's compiler offers a means to put the functions in a namespace). Actually, we do have a slight Unicode challenge for the rare characters outside of the Basic Multilingual plane due to inadequacies in JavaScript, but that is a problem that can be fixed and would be common for any JavaScript library (I actually doubt any general purpose JS libraries really address this yet).
The other criticisms I've seen were more criticisms of dynamic languages of a whole (non-static typing and all) or the RoR convention-over-criticism criticism, which seems to my limited familiarity with RoR seems to mostly relate to database conventions which is not much relevant for us at the moment in JavaScript (and PHP can get around it to some extent with frameworks, though this is not really a discussion here of PHP).
The only legitimate category of complaint left against PHP to my knowledge is that some functions seem to have an inconsistent ordering of arguments (e.g., needle and then haystack or vice versa). As Kevin said, we don't want to introduce our own idiosyncrasies here, so we stick with PHP's convention (though we could even add our own "ini" setting to fix that too if we wanted). It's like the English language--it is a very haphazardly developed language, borrowing from various languages in an inconsistent manner (Latin rules imposed on a Germanic language, influenced heavily by French, etc.), yet many people choose to use it over a more regular language like Esperanto because it is already so widely used.
Anyways, if you have any real specific criticisms that relate to this project, then I think we'd be eager to hear them, especially if it may offer a means of improving the situation for our users. But bear in mind, besides a few functions being admittedly mostly only useful for study purposes, all we are doing is giving access to a variety of common needs, such as in manipulating arrays/objects, etc. using a familiar vocabulary. We're not tieing anyone into some convoluted framework. If you don't like the functions you see someone else using, just tweak them to your needs. Let's avoid the language wars, which can make people unnecessarily on edge, and just constructively consult about specifics we can improve upon or at least learn from, in an objective, non-partisan fashion.
Thanks everybody!
@ MercifulSweet: Ah, another PHP basher. How hip of you! : ) Well for one thing: Cause it's very practical and can be a huge time saver for PHP developers (Apparently there still are some left). The reason we reimplement some of PHP's quirkiness is obvious: Would it make it any better if developers would have to watch out for quirks/exceptions of our own with every statement they type?
This is an atrocious idea. Sure, create a peer-reviewed open source library if you please, but why the hell would you model it after one of the worst organized, obviously-not-well-thought-out standard libraries in existence?
I'm by no means an expert, but I did turn David Flanagan's awesome "JavaScript: The Definitive guide" into a personal notebook... :)
@Ole, yes, and hopefully I can take a look at that function later too, if you can't figure out how to get it to work with IPv6. :) (We really are trying to get any new functions fully working before adding them.)
One thing I strongly recommend for JS developers is https://addons.mozilla.org/en-US/firefox/addon/7434 and adding its XUL and/or JS Env buttons to your FF toolbar (Regex and HTML are useful too). You can easily test such JavaScript in a flash without needing to save a file, etc., and if you use the XUL Editor, if you put JavaScript there, it gets evaluated immediately, so no need to even refresh a page or anything---VERY nice for quick development and testing. I can't overestimate how useful this is for learning JS. With that extension installed (esp. with the toolbar buttons), there's really no excuse for one not to confirm any JS behavior we're unsure of.
Doing that, I noticed from Console2 that fromCharcode should be fromCharCode. Also, I don't see what the benefit of that for loop is...Seems those functions cancel each other out, returning the same string. Best wishes, Brett
@Brett,
Ok so you mean when I would call a phpjs-specific function in
another phpjs-function I should do that like:
function inet_ntop(str) {
var i = 0;
var str_out = "";
if(str.length == 4) {
//IPv4
for(i in str) {
str_out += String.fromCharcode(str[i].charCodeAt(0));
}
return this.long2ip(str_out);
} else if(str.length == 16) {
//IPv6
return 0;
}
}
No, I think that's indeed the only way we can get the modification date of a remote file.
I changed your implementation a little to behave as in PHP (returning a PHP timestamp), and making it work with a (bugfixed) get_headers (fixed in SVN). We are adding "this." now to all dependency calls, so that people can freely copy functions between the namespaced version to the non-namespaced one (in other words, in the non-namespaced version, 'this' refers to the window object, so it will work as before there, but in the object context, it will look for the function as a method on the object).
function filemtime(file) {
// + original by: Ole Vrijenhoek (http://www.nervous.nl/)
// + bugfixed by: Brett Zamir (http://brettz9.blogspot.com)
// - depends on: get_headers
// % note 1: Looks for Last-Modified in response header.
// * example 1: filemtime('http://www.un.org');
// * returns 1: 1241532483
var headers = {};
headers = this.get_headers(file, 1);
return (headers && headers['Last-Modified'] && Date.parse(headers['Last-Modified'])/1000) || false;
}
Another untested, probably locks-up browser....
Mayby there is another better way to get the modification time of a file.
// Looks for Last-Modified in response header.
function filemtime(file) {
var headers = {};
var aSearch = 0;
headers = get_headers(file);
if(headers) {
aSearch = array_search("Last-Modified", headers);
if(aSearch) {
return headers[aSearch];
} else {
return false;
}
} else {
return false;
}
}
Hm just thinking about document.lastModified but I don't think that can be used cross-file.
Hi,
I was working on inet_ntop, but I am stucked with IPv6.
Here is my untested version.
function inet_ntop(str) {
var i = 0;
var str_out = "";
if(str.length == 4) {
//IPv4
for(i in str) {
str_out += String.fromCharcode(str[i].charCodeAt(0));
}
return long2ip(str_out);
} else if(str.length == 16) {
//IPv6
return 0;
}
}
No, not for other types either.... Scalar values are all safe, unless they are explicitly referenced within the function by the object on which they are (such as window.prop, myObj.prop, etc.). It is only objects' properties (arrays, RegExp, Date are all objects) which are by reference.
We simply can't change the type of the original variable, unless we reach out of the function (like window.prop) and refer to it--and there's nothing we can do to prevent that anyways--overwriting the argument is just not relevant here--it only changes the type inside the function, which may not always be a bad thing.
So, to summarize, the only danger unique to altering the argument by reference would be like this:
function modify (arr) {
arr[0] = 5; // Modify a property of the argument
myArr[1] = 6; // Modify the global directly (not relevant to argument over-writing)
arr = 'dog';
alert(arr); // will be 'dog' here now, but notice once this function returns that only the first two statements above will have had any effect on the global myArr.
}
myArr = [];
modify(myArr);
alert(myArr[0]); // 5
alert(myArr[1]); // 6
You could argue that we should stick to the same type within the function, but that's not all that necessary with a dynamically typed language.
But overwriting the variable will not have any effect on our users at all. This is not like the global 'keyword' in PHP (nor is it like by-reference (&) passing). Variables are ALWAYS passed in by value in JavaScript (i.e., copied) and there's nothing that the function can do to change that, though the properties of objects are passed in by reference.
@ Brett Zamir: Ok but what about other types like integers. The fact remains that we (at least) change the type of the original variable. In most cases it will be fine, but I think we shouldn't have to do it, and it may cause unexpected behavior & bugs in the code of our users.
@Kevin, to add to my last point, in the case you gave, if an array were passed in and the function tried to convert it to a string, the original array would not be altered, but the variable inside the function would be redefined to work as a string.
@Kevin, in the example you give, the original array will not get converted (nor would an object). Only a part of Arrays and objects are passed by reference (and this is always, not optionally like in PHP): their _properties_--not the whole object. So you can't delete the array or turn it into the string 'pumpkin'. However, you can alter the original arrays properties. Maybe it would be helpful to use a term like "properties-by-reference", because mostly only the properties can get altered. An exception to this would be when you use specific array-specific methods on the variable (like reverse()) which do work by reference.
So, it is far less likely that functions will unintentionally manipulate the value passed in, though we can still try to follow that rule if you like, to be extra cautious.
@ Ben Pettit @ Brett Zamir: Cool guys, I do have one suggestion though. I think we should avoid sanitizing the arguments themselves. Cause when people accidentally pass an array to our functions, the array itself will get converted to a string because JavaScript by default passes arguments by reference. (correct me if i'm wrong here)
I suggest an approach like this:
http://trac.plutonia.nl/projects/phpjs/changeset?new=927%40trunk%2Ffunctions%2Fxml%2Futf8_encode.js&old=899%40trunk%2Ffunctions%2Fxml%2Futf8_encode.js
So we can still sanitize the argument without touching (or even 'corrupting') the user data.
What do you think?
Cool. Thanks Brett.
It seems to work ok now.
Ahh, I just notice the example in the namespaced full source js and it give example of $P.strip_tags(...).
I downloaded the minified version (without those example) and just guessed how I should use it.. silly me :P
Just use "$P":
var x = $P.strip_tags('some html here');
That big blog encapsulated in (function(){})(); means nothing will go into the global namespace, except for any assignments within that block which refer to the global 'window' object (or if we used "this" at the top level of that namespace, since that also refers to the window, outside of an object).
However, Kevin, can we go ahead and remove that auto-assignment and instead assign the PHP_JS class to the window or this object (perhaps allowing assignment to $P as an option during packaging)? The big advantage of a namespace is that we won't force people to use our name. :)
Although people can of course change it, it's still a good idea, I think, to impress upon people that they can assign it however they want, and really should be forced to assign it in their own way so that:
1) different libraries joined together will be less likely to refer to the same PHP_JS object which might have different functions on each object.
2) we can avoid conflicts with differently-configurable instances (e.g., those using ini_set()) running in different libraries.
If you do want to do this, Hendra, you can change this line at the end of the script:
window.$P=PHP_JS();
to
window.PHP_JS=PHP_JS;
You can then instantiate with either of these forms:
var $myPHP = PHP_JS();
var $myPHP = new PHP_JS();
I'm quite new to javascript namespace thing. But if I want to use it, how should I call the function?
This doesn't seem to work:
var x = PHP_JS.strip_tags('some html here');
Firebug shows it as PHP_JS is not defined.
I also tried this and fail with same issue:
var phpjs = new PHP_JS();
var z = phpjs.strip_tags('some html here');
@ozizus, that is an interesting project, thanks for pointing it out. One thing you might be interested in is that, as Andrea Giammarchi asserts in the comments at http://webreflection.blogspot.com/2009/03/jsphp-javascript-object-like-php-class.html , when the runkit and operator extensions may be working in PHP, it will be possible to fully create a JS-like environment in PHP. In that case, it would be much simpler to tokenize some JavaScript and convert it (and cache it!) into the equivalent PHP, though it would depend on those two PHP extensions. Actually, runkit is probably more important (e.g., to translate the behavior of closures), as I presume the operator extension would only allow things like using the '+' sign to concatenate strings (and a tokenizing translator script could do this translating too).
You know what? It would be so fine if somebody revived j4p5 project. it is the other way of what you are doing... interpret javascript by php... then one could write the website server side by javascript, and host it to any php host...
I can make the change, but out of curiosity, is this just a bug in their JS implementation? I would think the next line would force any string objects to be converted to string literals anyways...
Hi, I had to update the transport.min.js file to work in adobe javascript, i just added a new first line for encode_utf8 and decode_utf8
function utf8_encode ( string ) {
// Encodes an ISO-8859-1 string to UTF-8
//
// version: 812.316
// discuss at: http://phpjs.org/functions/utf8_encode
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: sowberry
// + tweaked by: Jack
// + bugfixed by: Onno Marsman
// + improved by: Yves Sucaet
// + bugfixed by: Onno Marsman
// + adobe js by: Ben Pettit
// * example 1: utf8_encode('Kevin van Zonneveld');
// * returns 1: 'Kevin van Zonneveld'
string = string.valueOf(); // <- I added this line -ben pettit.
string = (string+'').replace(/\r\n/g, "\n").replace(/\r/g, "\n");
var utftext = "";
var start, end;
var stringl = 0;

Alexander M Beedie
Jul 4th
During the test FF3.5 experiences a >300MB memory allocation spike and takes twice the time using the current algorithm; takes about 1MB of memory with the new version below.
Chrome 3 experiences roughly a 100MB allocation spike that oscillates between 30-100MB for a while (probably some kind of incremental allocation/cleanup strategy) with the current algorithm. As with FF, the new algorithm uses < 1MB, but this time the speedup is larger -- 6 times faster.
Also, apologies for not formatting the function according to the dev guidelines, my bad -- only found them after posting, whoops...