Saturday, July 5, 2008

PHP POST Variable Not Detecting Dynamically Created Form Elements Through Javascript

Right now I'm working on a project where I need to generate an unlimited number of form elements. Let's say it's a T-shirt store. And we need to be able to add any number of sizes for each t-shirt design. We could do that a number of ways, but a simple way would be to add a Javascript link:
<a href="#" onclick="addSize('sizes');return false;">Add Size</a>
You would probably set up the elements in a table, like so:
<table id="sizes">
<tr>
<th>Size</th>
<th>Quantity</th>
</tr>
<tr>
<td><input name="txtSize[]" type="text"></td>
<td><input name="txtQuantity[]" type="text"></td>
</tr>
</table>
I like using the [] to make the form elements an array. I believe most browsers will automatically make a form element an array if it has the same name, but I like to be doubly sure. Then you could use the HTML DOM functions insertRow and insertCell.
function addSize(tableId) {
tableObj = document.getElementById(tableId);
numRows = tableObj.rows.length;
lastRow = tableObj.rows[numRows - 1];
numCells = lastRow.cells.length;
newRow = tableObj.insertRow(numRows);
for(var i = 0; i < numCells; i++) {
oldCellHTML = lastRow.cells[i].innerHTML;
newRow.insertCell(i);
newCell = newRow.cells[i];
newCell.innerHTML = oldCellHTML;
}
}
All seems to work fine, you see the new fields populate. But once you post the form, you may run into the problem I did. There was only one element in each fields array.

I started looking into it, and couldn't figure it out. So I started writing this simple example, and it worked fine. But my actual project still didn't work. So I was on the hunt again to figure out why.

I found this post, which helped me find the issue with my original project. Turns out it all had to do with how you nest your form tag. My mistake was that I was putting my form tag between the table and tr tag, instead of nesting the entire table inside the form.

Private Variables Accessible via Print_r and Other Functions

I've recently been learning OOP (object oriented programming), and have started to adopt the common security practices that come with it in PHP 5. Like private, protected and public variables. Or rather, making variables private or protected (for inherited classes), and using get and set methods to limit access to those variables to certain users. Essentially, private variables cannot be read or written to except for within the object (via get and set methods). Protected variables are only accessible to the object or any object that inherits (extends) this class.

The php manual says this about the private and protected keywords.
The visibility of a property or method can be defined by prefixing the declaration with the keywords: public, protected or private. Public declared items can be accessed everywhere. Protected limits access to inherited and parent classes (and to the class that defines the item). Private limits visibility only to the class that defines the item.
http://www.php.net/manual/en/language.oop5.visibility.php
Now, I've recently discovered that this isn't exactly true. In general, it is true, but not exactly. The wording here is VERY misleading, and I know I"m not the only one that thinks so. Just how many people have been fooled by this, I'm not sure.

Try the following code, which is how I discovered the problem in the first place.
final class Database {
private $connection;
private $database;
private $host;
private $user;
private $password;

public function __construct($host = null, $database = null, $user = null, $password = null) {
// Some code that validates input
$this->connection = null;
$this->database = $database;
$this->host = $host;
$this->user = $user;
$this->password = $password;
$this->connect();

}

private function connect() {
$this->connection = mysql_pconnect($this->host, $this->user, $this->password);
if($this->connection) {
mysql_select_db($this->database, $this->connection);
}
return $this->connection;
}
}

$db = new Database('localhost', 'myDatabase', 'myUser', 'myPass');
print_r($db);
The following outputs:
Database Object
[connection:private] => Resource id #5
[database:private] => myDatabase
[host:private] => localhost
[user:private] => myUser
[password:private] => myPass
)
As you can see, those private variables aren't so private anymore. A regular print of any one of those variables fails, but a print_r of the object itself shows everything. Now, some of you might be thinking, "so don't print_r anything you want private", and you are missing the point. Part of the joy of OOP is that YOU aren't the only one developing your application. You may have a team of developers, and maybe you don't want anybody else on the team to have direct Database access, so you write the Database object that limits their access to it.

The following is a bug report on this, which was basically ignored, despite the obvious concern. http://bugs.php.net/bug.php?id=39118 Which also brings up that var_dump and var_export both do the same thing.

This sounds like a bug to me, as this defeats the purpose of private and protected variables. True, you can disable print_r, var_dump and var_export, through your php.ini but that seems like a crappy solution, as it eliminates alot of functionality. Just because you don't want to show private and protected variables, doesn't mean you don't want to have to disable print_r and write a new function to do the same thing, without showing private and protected variables. Because you can't just rewrite print_r, even if it is disabled because:
PHP does not support function overloading, nor is it possible to undefine or redefine previously-declared functions.
http://us2.php.net/manual/en/language.functions.php

Note: A function name may exist even if the function itself is unusable due to configuration or compiling options
http://us2.php.net/manual/en/function.function-exists.php

So there really isn't a way for a developer to change the way the behavior of the print_r function when it comes to private and protected variables. And if you thought the magic function __toString might do it, nope, it only works when concatinating strings or with echo and print.

And you can't really expect PHP to change the way print_r works, unless they finally do admit it is a bug. So the more logical remedy would be to have a magic function that would be called whenever the print_r, var_dump or var_export functions was called, similar to __toString. That way, print_r would still function as it does now, with the ability for the developer to increase their applications security without reducing functionality.

Saturday, June 14, 2008

Download Firefox 3 Tuesday, June 17th and Set a World Record

The best browser in the world gets even better. And if you download it on Tuesday, June 17th, you can help set a world record. Go here http://www.spreadfirefox.com/en-US/worldrecord/ for more details.

Thursday, June 12, 2008

PHP Date Limit for Unix Timestamps

So the other day I discovered another one of those weird limitations that I never would have thought existed. Apparently the PHP date function, which formats a Unix Timestamp to a user input format, only accepts a specific range of Unix Timestamps. It's funny because I never would have discovered this, but by accident. The following is actually quoted from php.net
The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT. (These are the dates that correspond to the minimum and maximum values for a 32-bit signed integer). However, before PHP 5.1.0 this range was limited from 01-01-1970 to 19-01-2038 on some systems (e.g. Windows)
Of course this isn't for another 30 years, but what about applications that use future dates? It doesn't make much since to me, because I always thought the Unix Timestamp was perfect to simplify the process of making dates readable across multiple regions and format preferences. But it seems that it may not be the best solution to store dates. But it does seem that much of the internet is already so dependent on it. Unless you create your own date/time conversion functions, you are probably using strtotime, time and date functions.

Another thing that confused me, when I discovered this issue by accident, I noticed that what the date function returns for values over the 2147483647 integer max, if you pass it as a string, it gives you a different date output. This doesn't occur when given a value within the acceptable range.

Anyway, I thought it was interesting, and I'm sure before the next 30 years pass, we will be running 256-bit systems and the limitations of our current 32-bit systems will be nill. In the meantime, it looks like applications that require higher date ranges will be better off using another date format like the MySQL Current Timestamp, with some conversion functions to make the dates more human readable.

Wednesday, May 21, 2008

Paper-thin Monitors

So today I thought I'd share some "new" technology. A few years ago I heard that they developed the technology to actually build paper-thin monitors that you could bend and fold and crinkle up, and they would still work fine. There are actually several companies that have "done" this, and also several terms for this.

The technology has actually been around for several years. It just hasn't been practically used yet, even though they keep saying production costs are finally low enough. From what I've read, one of the first companies was E-Ink Corp (possibly a division of Lucent?). See the Article on USA Today. Although, BusinessWeek featured an article about rigid, paper-thin screens before that, from both E-Ink and a company called Gyricon Media (possibly a division of Xerox?). The article possibly started one of the best known terms for this technology, ePaper. From there things just got better (maybe not as fast as you'd expect).

HP unveiled a flexible version that was built to retain an image even after power had been turned off, read more here. Other advances allowed video and live updates. Even Sony recently introduced their version of it see more here. You can also read about Electronic Paper on Wikipedia for more history and how it works.

So why haven't we seen this in stores and on newstands? And why are there still people that haven't heard of this? There are several theories to this that I have heard. One is that the newspaper companies purchased the rights to this technology so they wouldn't go out of business. Another I've heard is the obvious one, "it's just not practical yet". I surprised at how that can be an excuse. You'd think that if their technology is that good, then it wouldn't matter how "practical" it was, they just have to be smart enough to market it so people will pay for it (no matter how much it costs). Once they do that, then they can start to reduce their production costs.

Either way, I think it is a cool technology, even if it's been in production and promised to be out within the next couple years for, at least 8 years now.

Saturday, May 10, 2008

Auto Increment Values and mysql_insert_id()

The other day I was working on a project, inserting data into a MySQL database, nothing out of the usual. I had a function set up to do the inserts, and it returned the auto-increment value of the insert, if the insert failed I returned false. In the page I was calling the function I was running an if statement on the insert value to make sure that the insert was successful. And for some reason the if statement kept failing. So the first thing I did was check my query. It all looked good, so I checked my if statement and the function call. Everything was good. So I checked the database, and the insert was successful. But for some reason my if statement was failing. So I echoed the return value from the function, and it said (which can mean false too, which is why my if statement was failing). But why was the function returning ? So I started returning different things from the function to see where it was getting from. Turns out it was the mysql_insert_id() function I was using to get the auto-increment value of the insert.

So I looked that up on php.net and it basically says that if the previous MySQL query didn't perform an insert, then it returns zero. So I double checked and there was no other queries in between my insert and the call to mysql_insert_id(). So why it was returning zero, I'm still not sure. But the workaround I used at the time was essentially I ran an if statement in the function like:
$newid = mysql_insert_id();
if($newid === 0) {
return true;
}
else if($newid === false) {
return false;
}
else {
return $newid;
}

This just makes it so when I call the function, I don't get a failure notice when the insert was successful. However, this doesn't give me the actual id of the inserted item. In this case it wasn't that important to me, but it still bugged me that mysql_insert_id() wasn't doing what it was supposed to. So I was looking around, and found another workaround. I haven't tried it yet, but I am confident that it will work a lot more reliably. Here is a link to the original article where I found it. It goes into other ways of knowing the id of an insert (but the last one is really the only reliable one). To summarize it though, instead of using a function inside PHP to get the insert_id, you use a MySQL query.
INSERT INTO `comments` (`comment`, `username`) VALUES ('first comment', 'jojonaloha');
SELECT last_insert_id();


I would think that it would be best to do this in the same query, just for reliability. I also think this article brings up another interesting thing, which I've also been thinking about. That is with auto_increment fields or even INT fields in general; what is the maximum value of an int field? If you read MySQL's documentation, you can see the range of values that can be stored in MySQL numeric fields (tinyint, smallint, mediumint, int, big int), you can see what these values are and the corresponding ranges.

Typically I've used INT fields, and I've always seen them as size: 11, and have continued with the same thing. You might think that means the maximum is 99,999,999,999 but that isn't right. The maximum value (if you are leave the field as signed) is only 2,147,483,648. If you change the field to unsigned the max is 4,294,967,295 or 4^16. This is still not 11 digits long. So INT 11 is a little misleading on auto_increment fields. Of course, if you need a signed value (allowing negative numbers), then you need that 11th digit for the negative sign. But on auto_increment fields, you should make them unsigned and INT 10, because INT 11 just doesn't make sense.

So why does all that matter? Well, what happens when you get that 4 billionth item in the database? Basically the insert fails and returns an error that basically says "duplicate value on primary key". In normal circumstances you'll probably have a while before you have to worry about that, unless you are running a log of everything that happens on your site, then you could run into problems a lot sooner. In that case you may have to rethink how you set up your primary keys. One option (which is also in the previous article) is to not use auto_increment fields wherever possible. So, for example, instead of using an auto_increment id for a user, use their username as the primary key (which you should be making sure is unique anyway). That may not be the only or best solution. This is especially true if you have line item tables like the comments table, which contains the user who made the comment, because if the user changes their username, then you have to change all the line item tables as well.

I'm sure there are many solutions to this, but for now, it's something to think about. And if I come up with some really good ones, I'll let you know.

Friday, May 9, 2008

Firefox Overriding Window Resizable Parameter

THE FOLLOWING ORIGINAL POST HAS BEEN UPDATED AS OF 14 MAY 2008 -- SEE BOTTOM OF POST FOR UPDATES

As of today I've decided that my blog is going to be about random annoyances on the web. Such as today's topic: FireFox Overriding Window Resizable Parameter. Essentially, if you are a web developer and you use the following (or similar) code to open a new "popup" window.

function popup(winLink, winName, winWidth, winHeight, winResize, winScroll, winMenu) {
if(winWidth == undefined || winWidth == NaN) {
winWidth = 400;

}
if(winHeight == undefined || winHeight == NaN) {
winHeight = 400;

}
if(winResize == undefined || (winResize != "yes" && winResize != "no" && winResize != 1 && winResize != 0 && winResize.constructor != Boolean)) {
winResize = "yes";

}
if(winScroll == undefined || (winScroll != "yes" && winScroll != "no" && winScroll != 1 && winScroll != 0 && winScroll.constructor != Boolean)) {
winScroll = "yes";

}
if(winMenu == undefined || (winMenu != "yes" && winMenu != "no" && winMenu!= 1 && winMenu != 0 && winMenu.constructor != Boolean)) {
winMenu = "no";

}
if(!window.focus) {
return true;

}
var winURL;
if(typeof(winLink) == 'string') {
winURL=winLink;

}
else {
winURL=winLink.href;

}
var winStyle;
winStyle = 'width=' + winWidth + ',height=' + winHeight + ',scrollbars=' + winScroll + ',menubar=' + winMenu + ',location=' + winMenu + ',toolbar=' + winMenu + ',resizable=' + winResize;
window.open(winURL, winName, winStyle);
return false;

}
Even if you pass in values to allow resizing, FireFox overrides it with the default. And as far as I've seen, the default install setting is to disable window resizing. Of course, if you are a smart enough user you can change that in the "about:config" settings see this blog on how to do this, but the fact is, most users don't know about this. And this really puts a strain on website developers trying to make their sites as usable as possible.

Now, ranting about these issues is well and good, but the other part of my blog will be workarounds for these issues. I haven't found one yet, but I will be working on this as I feel it is important to not just complain about something if you don't try to do something to solve it. So look forward to that, and if you have ideas for a workaround, feel free to post a comment.

UPDATED: 14 May 2008 9:47 PM PST
I've updated the function above, part of the "bug" was a spelling error. And Firefox only overrides the default when the dom.disable_window_open_feature.resizable is set to true (which means windows will ALWAYS be resizable). So the above function should work properly.

Wednesday, May 7, 2008

Web 2.0

So I've been thinking about the relatively new phrase on the internet "Web 2.0". It really is an interesting concept, and it makes sense. But I find it a little humorous that this new phrase was coined with this simple concept. And it really is a simple concept, user interaction with your website. The degree of which can vary quite a bit, but still the idea remains the same. But wasn't that what the web was all about to begin with? Users interacting and the exchange of ideas. I think the only thing that has really changed in this "Web 2.0" world is how easily the exchange or transfer of ideas has become.

For instance, this blog; which you may be reading on Blogger or Facebook, or a variety of other social networks and blogging sites. The Web 2.0 part is that I can post this once and have it update all over the web on my various profiles. Heck, you (if you read this) could not even be logging into Blogger or Facebook, but reading this from your Outlook or import it to your own website via the RSS Feed.

I think the idea has always been there, it just hasn't been implemented to the extent that is now, and nobody really thought of it as it's own thing before this phrase came along. What I really find interesting is how this will continue to evolve into new applications of interaction on the web. Even in the last year, very new implementations of this have taken off and have already become accepted standards on the web. IE: Facebook's Apps. There are thousands of Apps now on Facebook and even Myspace (the monstrosity that it is) picked up the idea. And what I find even more interesting is there are now websites that all they do is rate and generate statistics on Facebook apps, such as: http://www.adonomics.com

Who knows where we will go next. The possibilities are endless.

Monday, May 5, 2008

My First Blog Post

I guess I should start by introducing myself. My name is Jonathan Jordan. I'm a web developer by profession, and work for a small, but rapidly growing company called Blue Sky Web Design in the Beaverton, Oregon area. I primarily work in PHP and MySQL, building client-editable websites.

This past year has been pretty eventful. I graduated from the University of Oregon in June. I got married in August to my high school sweetheart and girlfriend of five years, Jennifer. And we have a six month old daughter, Hannah.

That's it for now.