Category: PHP

Pats Over Bills 20-10

Bills fall to New Englad 20-10

And that’s how you win a football game.

~A!

A Quick Note About header()

I've seen a great many questions come in lately on the use of the header() function in PHP. This is just a quick note to describe it's use and some of the issues surrounding it.

There are two basic types of headers when we talk about HTTP Header directives. Request Headers and Response Headers. Request headers are what your browser sends to a server, and response headers are what the server sends back. I've seen more complicated explanations, and they are accurate, but that's the essence of the thing.

When you specify a header with the header() function in PHP, you're sending a response header from the server 99.9% of the time. Most of the time, you're trying to use a header to:

  • Relocate to a different page
  • Download a file

Headers can be used for a number of different things, including setting cookies, but we're not going to look at that in this article. The cookie functions in PHP can handle everything you want to do with cookies.

Relocation:

When you're relocating to a different page from PHP, the function you use looks like this:

PHP:
  1. header("Location: http://somesite.com/page.php");

Which will send that header to the browser, causing it to head over to page.php on the somesite.com domain. So, you enter it into your code, and everything works, right?

Well, not always. See, when you don't specify any headers but send output to the browser (with print(), echo(), or just standard HTML), PHP is smart enough to send it's own. (PERL, not so much, but that's a different story). So when you've output anything, including whitespace to the browser, you get message telling you Headers have already been sent.
If you're not sure why, and you haven't output anything to the browser on purpose before the headers you're sending, the first thing to do is check for whitespace right before your opening PHP tag in your code. Resolve that there is none first.

If you want a little cleaner solution, you can use ob_start() and ob_flush() to send everything into an output buffer before it gets to the browser, and have it sent all at once.
One caveat here is that you want to call ob_flush() as soon as possible, because on scripts with large output, you can slow things down considerably.

Example without output buffers:

PHP:
  1. <?php
  2.   if ($redirect):
  3.     header("Location: http://somesite.com/redirect.php");
  4.     exit();
  5.   endif;
  6. ?>

exit() in the above example stops the script so nothing else will be executed. Without it, you can have the unintended consequence of doing something after you meant to redirect the user to a different page.

Example with output buffering:

PHP:
  1. <?php
  2.   ob_start();
  3.   if ($redirect):
  4.     header("Location: http://somesite.com/redirect.php");
  5.     exit();
  6.   endif;
  7. ?>
  8. <html>
  9. <body>
  10. Welcome to the site!</body>
  11. </body>
  12. </html>
  13. <?php
  14. ?>

~A!

Upgrading PHP from 4.3.9 to 5.2 on Cent OS4

I recently had to migrate a client from PHP 4.3.9 to 5.2 on a godaddy virtual dedicated server running Cent OS4, and I came across a fantastic guide for doing so:

http://www.jasonlitka.com/2006/11/30/upgrading-php-520-rhel-centos/

Unfortunately, I ran into a couple of problems with that guide, including broken links to a couple of RPMS and source packages that I corrected with forum searches and from the author's other posts, which are all fantastic. That is a great site, and Jason should be commended for his commitment to helping people do what they need to on Linux Servers.

I have assembled here the steps I took to upgrade, aggregating the information I got from Jason's site as well as the filling in I had to do.
So, without further ado, here are the steps I took:

1) Log into the server as root

2) mkdir /usr/src/redhat

3) chmod 777 -R /usr/src/redhat

4) yum install wget nano make autoconf automake rpm-build postfix fileutils file libtool gcc cpp gcc-c++ perl-DBI readline-devel libc-client-devel libstdc++-devel bzip2-devel curl-devel db4-devel expat-devel gmp-devel aspell-devel httpd-devel libjpeg-devel libpng-devel pam-devel openssl-devel sqlite-devel zlib-devel pcre-devel krb5-devel cyrus-sasl-devel openldap-devel postgresql-devel unixODBC-devel libxml2-devel net-snmp-devel libxslt-devel libxml2-devel expat-devel ncurses-devel gd-devel freetype-devel

5) wget http://www.jasonlitka.com/media/files/SRPMS/php-5.2.3-jason.2.src.rpm

6) rpm -ivh php-5.2.0-8.src.rpm

7) rpm -ivh php-pear-1.4.9-4.src.rpm

8) wget http://mirrors.kernel.org/fedora/core/6/source/SRPMS/pcre-6.6-1.1.src.rpm

9) rpm -ivh pcre-6.6-1.1.src.rpm

10) cd /usr/src/redhat/SPEC

11) rpmbuild pcre.spec

12)  cd /usr/src/redhat/RPMS/i386

13) rpm -Uvh pcre*

14) cd /usr/src/redhat/SPECS

15) pmbuild -bb php.spec

16) cd /usr/src/redhat/RPMS

17) rpm -Uvh php*rpm

At this point, php installed for me fine and dandy. Next, I updated the php.ini file to reflect the new modules directory, which was /usr/lib/php4 to the 5 version, which is /usr/lib/php/modules

Then I restarted apache to reflect the changes

18) /usr/sbin/apachectl -k graceful

After that, I just checked to make sure it had installed appropriately, and that mysql was working

19) php -v (Should display 5.2.3 for the version of PHP)

You can create a php file to check for the existence of mysql_connect() as I did, or you can create a file that actually connects to your database. Either one proves out the ability to connect to MySQL from your PHP installation.

Great big hat-tip to Jason Litka of http://jasonlitka.com for his doing the heavy lifting on most of this. The only things I had a problem with when following his guide were the PCRE version (it kept asking for >= 6.6) and a broken link to the php 5.2.2 source rpm on mirrors.kernel.org, which he fixed in this post on upgrading to 5.2.3 by providing his own source package.

Hope this helps anyone stuck with an outdated PHP version who wants to use actual object-oriented code and json functions from PHP.

~A!

Welcome University of Wisonsin Students!

Just a quick note to say hello to any students coming over from the University of Wisconsin - Eau Claire Database Management Courses.

We're happy to be able to help out in any way with your studies. If you have any questions about anything you see here, or need some help with programming or database tips and advice, please feel free to contact us at info@mypetprogrammer.com, and we'll be glad to give you a hand.

Good luck, and all the best,
~A!

Inline Values: The Ternary Operator

So, you have a text field, and you're using an associative array to fill the values based on table data from your RDBMS, right?

You're filling the values inline, something like:

value="<?=$row['name']?>"

Which is great, so long as you're sure the row will exist. If the row doesn't, you get an E_NOTICE telling you there's an undefined index. You could turn off error reporting, and poof, it works, or you could do it right.

Turning off error reporting to mask sloppy coding practices is like stopping doctor visits once you've been diagnosed with cancer. You're still sick, you're just not hearing about it any more. And just like a disease will continue to have adverse affects whether you admit it or not, your server's error log will fill up with undefined index and undeclared variable warnings and notices. Will this cause a long term server problem? Maybe, but I doubt it.

More important, filling up the server log with garbage makes you less able to diagnose real problems and fix them, because you have to sift through millions of lines of stupid notices and warnings to get to find a "real" error.

Let's say you're pulling the row like this, and assigning the array:

$result = mysql_query("select * from users where id=1");
$user = mysql_fetch_assoc($result);

If there is no record, you have a problem. Granted, most programmers I've seen are too sloppy and lazy to care about such a problem, but the problem still exists regardless of how you feel about it.

In a situation like the above (which is not the best way to access a DB, IMHO, use classes and good architecture), you can short circuit the issue by declaring the empty array first and then using a decision block to init the data if there is something to init.

$row = array();
$result = mysql_query("select * from users where id = 1");
if (mysql_num_rows($result)):
  $row = mysql_fetch_assoc($result);
endif;

This step alone does not solve your problem, it lays the foundation by which your problem can be solved. Now, when you do an inline value (interpolated into the HTML), do it with the ternary operator:

value="<?=$row ? $row['name'] : ""?>"

The ternary operator is a standard construct in most programming languages (VB6 had IIF, but it's the same thing). The Syntax of the ternary operator is:

condition ? truepart : falsepart

Where condition is any valid statement that translates into a boolean, truepart is what to do when it's true, and falsepart is what to do when it's false.

Let's take something a teensy bit more complicated. If you have a date in the database (let's say a DateTime type), and you want to initialize to the current date if the date in the table is empty, otherwise print the date as m/d/Y, the ternary operators can be nested like so:

value="<?=$row ? $row['date'] ? date('m/d/Y', strtotime($row['date'])) : date('m/d/Y') : date('m/d/Y')?>"

Confusing? Not so much. If we use some parentheses to section it off, it looks like:

value="<?="$row ? ($row['date'] ? date('m/d/Y', strtotime($row['date'])) : date('m/d/Y') ) : date('m/d/Y')?>"

Which is the same thing as doing this (in blocks):

if ($row):
   if ($row['date']):
     print date('m/d/Y', strtotime($row['date']));
   else:
     print date('m/d/Y');
   endif;
else:
   print date('m/d/Y');
endif;

Interpolation of PHP into HTML gets a bad rap either way, because it makes code harder to maintain, and a little ugly. But if you're going to do it, and many of us still do (I don't want to have the overhead and code size of something like Smarty all the time), then do it well and correctly.

And correctly includes the use ofthe htmlspecialchars() function, but that's a topic for another day.

~A!

Mama Says $_REQUEST is the Devil

And unlike her embarrassing mistake with the alligators, this time Mama is right.

To those unfamiliar, PHP makes available something called super global arrays that allow you to access various things, server properties ($_SERVER), cookies ($_COOKIE), post and get values ($_POST, $_GET), etc.

When you submit a page that looks like this:

http://localhost/foo.php?key=value

Your variable is available in the $_GET superglobal array. You can access it with $_GET['key']

Likewise, when you submit a form via post, you can get the form values from $_POST the same way.

$_REQUEST, however, is the grandaddy of them all, allowing you to access all of the POST, GET, and COOKIE values from one array. How convenient, right? How very courteous, to make an array I can grab anything from at any time I want. Lovely, right?

Wrong. Really wrong. Really, really, really wrong. $_REQUEST is the same thing as using register_globals to handle GET and POST. It allows anything and everything you have to be hacked given very little knowledge of your actual application internals.

For example: Let's say you have a web application with a front-end and an administration panel. You only want people with admin rights to be able to access the admin panel, most likely, so you build yourself a little login panel, and a form, which posts to a script the variables username and password. Those are then checked against the database, and boom, you're in business, knowing who to authorize and who not to.

That code would look something like this:

$username = $_POST['username'];
$password = $_POST['password'];

$result = mysql_query("select id from admin where username='$username' and password='$password'");

if (mysql_num_rows($result))
{
$_SESSION['admin_access'] = TRUE;
$_SESSION['admin_id'] = mysql_result($result, 0, "id");

}

(The code above is far over-simplified and only used as an example. I recommend error checking, good connection management, and database framework classes. Don't code like this.)

Now then, the problem with using $_REQUEST['username'] instead of $_POST['username'] in the example above is this: I can hack you in 2/10s of a heartbeat. Literally. Let's call this script "login_check.php" for giggles. Hitting the following URL would compromise access to your administration panel:

logincheck.php?username=foo'%20or%20true%20--

Because you're using $_REQUEST instead of the right array, username is going to be populated with the value I entered in the url, not what you thought it was going to be populated with. So, the query you thought you were executing now looks like this:

mysql_query("select id from admin where username= 'foo' or true --' ...);

And since MySQL sees everything after the two dashes as a comment, guess what? That query will ALWAYS return records, meaning that hitting it with that url will always log someone in as an administrator.

It's called SQL injection, and if you're using $_REQUEST or register_globals=On, you're asking for it to happen. Begging for it, even.

I could go on and on, but if you read this far into the piece, you care enough about your code and your professional reputation not to use $_REQUEST or register globals. And if you don't, you may never regret it, and someone will come hire me to clean up your mess later. Fine by me.

~A!

How long should that field be?

I recently had the opportunity to refactor and normalize an extremely large database in MySQL. I love refactoring as a matter of course, and normalization just makes me all a-tingle, so it's good work if I can get it.

I was noticing as I pored through the fields in the various tables, that many, many fields had the same varchar length. 90% of them were varchar(100), including zip codes. Well, that's when the zip codes weren't integers, but I digress.

The point is this: I did some checking online, and I couldn't find a good guide to field sizes, so I thought I'd just share a little of an approach I feel is common-sensical and worthy of at least writing down.

Let's take a look at a record, and go from there:

John Smith, 123 Somewhere Street Northwest, AmityMothertonvilePokeno, New Hampshire, 03101, 603-555-1212, jsmith2007@funky-nowhere.com

That's pretty basically a bit of contact information. It's normal, it's average, and we all use it constantly in just about every app we develop using MySQL and PHP. So how long should we make the fields?

The name. "John Smith" is only ten characters, but what if someone has a longer name? What about "Luxaranimous Fitzgerald Luntensteinivilosky"? I think we could all agree that's an egregiously long name, and it still comes in at 43 characters. So maybe we bump it up for a bit of a buffer, and call it 55 characters. Now I feel better, and I have my new "Fullname" field size for all time.

Next we have the street address. I once knew a person who lived on Martin Luther King Boulevard, and that's decent size, so I'll use that as a baseline. Assuming 6 characters for the number, and 5 for "north" or "south", we come in at 39 characters. Add an apartment (Apartment 123) and we're at 41 characters. Again, we pad it, and I come up with 55 as a decent field size for an address.

City and state are both easy. The longest city name in English is 60 characters long (look it up if you don't believe me), and it is also the longest 'place' name in the English language. So that means that the city field is 60, and the state field, if you're not just using abbreviations, is 60 as well. (Longest "place", remember?)

Zip codes/postal codes in the US are easy, that's nine without the dash, ten with the dash (12345-1234 or 123412345). Foreign postal codes are a bit longer in some cases, and according to This site, the longest postal code in the world is currently 10 characters long.

Next, let's take a look at the dreaded phone number. I know this one always gave me fits when I started out, never could decide what to do with it. Do I store it into separate fields, do I exclude foreign phone numbers? If I don't exclude foreign phone numbers, how do I make it familiar for all?

Relax, first of all. The longest phone number in existence, country code and all, is 14 characters by all accounts. The format varies wildly from country to country, so if you're dealing with international phone numbers, you can still split them up the way you would with US numbers, just make sure you add a country code field. As long as the split fields end up adding to 14, you're ok. If you want to add in the ability for separators, dashes, dots, morse code, whatever, jack it up to twenty. No need to go overboard. This methodology of course applies to mobile phone numbers and fax numbers, as well.

And then the email address field. My email address is "anthony.levensalor@opentravelsoftware.com". That's 41 characters right off the bat, and I know I don't have the longest. In fact, the longest email address can be 64 + 1 + 255 characters long (user name, @ sign, domain name) according to the IETF.

Does that mean your email field needs to be 320 characters long? Well, technically, it is best practice to make sure you cover the extremes, so yeah. Just make sure you crunch that by allowing the field to be null. If that sounds silly to you, rest assured it sounds silly to me too. But somebody's gonna have a 320 character long email address, and you know it.

So here's the basic field specs, as much for my edification as yours:

full name: varchar(55)
address:  varchar(55)
city:         varchar(60)
state:       varchar(60) [or varchar(2) if you're using the US abbreviations only]
zip:          varchar(10)
phone:     varchar(14) [that's just digits, no symbols. Of course, in the US, you only need 11]
email:       varchar(320)

Until next time, I hope this helps you out.

~A!

Windows XP Desktop Search

AS much as Steve Jobs and the Apple community want to complain about Microsoft "stealing" features (wasn't there already a lawsuit about that in the 80's, and MS won?), one of the things I liked best about Vista in the tests I did on it was the destop search.

I know, it seems unnecessary somehow, what with google desktop and all the other possibilities out there for indexing and searching the computer, but I love the fact that it is integrated directly with the operating system and just makes searching a hell of a lot easier. And faster. And more comprehensive.

I was trolling around the Microsoft site's XP downloads and came across a version of the search that runs on XP. Since I'm not really ready to upgrade to Vista (bought a copy of the upgrade, waiting until I get better video), I went ahead an installed it. (You can download it here).

Once installed, it creates a little search bar in the upper-right hand corner of the screen. Start typing and it brings up a list of the most relevent matches right away. Click "more", and you get a full interface that allows you to choose different types of files, specify search criteria, just a whole bunch of cool stuff.

The way I look at it is this: I'm tempted to try out an iBook, because the hype is hard to resist, and my wife loves hers. But let's be pragmatic for a moment. I am a professional software developer, and I do a lot of heavy lifting on my machine. I write PHP, Perl, MySQL, HTML, CSS, Javascript, Java, VB.NET, VB6, C++, and once in a while a little Delphi to pass the time. Do I really want to risk all those working on Mac (with or without the emulation of windows they provide now)?

Not really, no. Besides, I like using windows. I also like using Linux. I'd probably like using OS X, too. But the value of understanding the software that's running your machine cannot be overstated. Perhaps I'm just with Windows because I've been on Microsoft products for all 22 years of my programming life, and I've never had something happen I couldn't fix. Even when I was eight, DOS was easier to work with than The Apple IIe we had at the grammar school.

This isn't a "Windows is good, everything else is bad" article, just saying that's a nifty search they put in, and I'm glad they ported it down to XP.

~A!

 

PHP over IIS (Q&A)

Q: When I modify php.ini file in "c:\php\--IIS 6.0 is not picking it up.  Any changes made in php.ini do not reflect when I run phpinfo().  However, I can run *.php files.  I setup IIS 6.0 with the appropriate web extensions--on the web service extension and the web site directory mapping.  Both pointing to c:\php\php5isapi.dll  Just can't get php.ini to pick up???
Thank you for your help and God Bless!
- Tony

A:

You are slightly outside my strongest area when engaging in IIS interop with PHP, but I will say that the little experience I have with it inclines me to think that your php.ini file should be in a system path for windows, e.g. c:\windows. I've had a lot of luck getting things to pick up appropriately when I copied the php.ini file into system directories and restarted the IIS process.

~A!

Windows Vista

Like everyone else who runs a windows box for development, I've been psyched about the new Windows Operating System, Vista. Normally I am not an early adopter of Windows OS's, but it's been so long since I got to play with something new from MS that wasn't an office installation that I decided to give it a whirl.

 This article is the catalog of the results of that experiment.

I purchased my Vista license from the digital locker, so I can download it again if I need to and reinstall without all those cumbersome DVD's. I'd never done it before, so I gave that a shot. The download for Vista was in three parts, the core of which was a 2.3GB download that took a couple of hours over my DSL connection, nothing too severe.

 I ran the windows Vista upgrade advisor, and it advised me I needed to uninstall Nero before I could upgrade to Vista, that my video card would not handle the new Aero GUI, and that a few of my programs might possibly maybe have configuration issues after the upgrade. The language used to describe the possible incompatibilities was gentle enough that it seemed ok to just go ahead with the upgrade.

Operating System upgrades take a long time, longer than a clean install, and for good reason. When the OS is attempting to upgrade, it's simultaneously trying to put in the new while preserving the old configs and settings, so it makes sense that it will take forever. I defragged, cleaned up the disk space, and ran the upgrade.

The upgrade ran for about two and a half hours, and everything seemed to be progressing nicely, until the last reboot. When the screen said "preparing to run windows for the first time", I was really excited to try out the new OS. What I saw a minute later, though was "Upgrade was not installed successfully, your old Operating System is being restored".

It restored my XP Pro installation precisely to where it had been before I started the Vista install, so I couldn't complain too much. I was disappointed, but figured I had missed something simple. After all, the advisor had told me I was ok to upgrade, so I figured I was ok to upgrade. I ought to know better.

So I did some research on the MS website, in some discussion forums, and read over what little information there was to be had so far on the internet regarding Vista upgrades and installs. I saw other people with the same issues, and got the general impression that I needed to uninstall all of the possibly conflicting programs on my machine before I could upgrade. So that's what I did and, long story short, had the same result another 3 hours later.

Still determined to use my new $200 operating system, I backed up all of my development folders, files, and settings, and did a clean install. This one took 2 hours, and I had a shiny new Vista installation on my computer.

Something, however, seems to have changed in the definition of "clean" install, because Vista backed up my old operating system in a folder named windows.old that took up half my hard drive, and left all of my custom folders under root (C:\) completely untouched. I dunno if it's just me, but I had expected, you know, maybe a format, something to justify being a "clean" install. But nope, it only touched things past the root of the C drive, which it was easy enough to fix, just a minor annoyance.

Since I am a web programmer and 90% of my work is in PHP/MySQL, the first step to getting productive again was an installation of Apache 2. While I was installing it, I was asked for permission to continue from the OS about thirty times, and granted it each and every time. Then, at the end of the Apache installation, it told me it couldn't start, there was no installed Apache service.

 "Hmm", I thought. "I know I just installed that." Back to google I went, and sure enough, it was the new "Secure" feature in Vista, a bodyguard with a bad attitude named "User Access Control". The function of the UAC is to make sure that nothing can happen to your machine without your permission. Which is great, and reminds me of Unix and Linux and Mac, except that when you grant permission on those OS's, it actually gives whatever you're doing permission. The UAC seems, at least in the case of my Apache install, to have decided I didn't know what the hell I was talking about when I said yes, and denied the installation of the service because it knew better than I did.

 Luckily, the UAC was easy to turn off. So now I can actually run my machine, I just get a popup every five minutes or so telling me how unsafe my machine is because I don't have UAC turned on. That's irritating, I think, but I still pressed on.

I run a Gateway 7330GZ Notebook as my general use machine, to write PHP code and build and deploy my .NET applications. Gateway does not have the correct Video or Sound drivers for Vista yet, and support has told me they don't know when they'll be coming out. So that's two mistakes: One by Gateway for not getting on the ball with their driver development, and one on MS for not even setting up the pretense of backward compatibility. I have to use an external monitor with a generic driver to have a proper display, since the 1280x768 laptop screen will not display anything but 1024x768 on Vista at the moment, which just makes my head hurt.

I also have no sound for the time being, and oddly discomfiting feeling after being so used to pops and clicks and youtube videos and listening to my collection of music.

Having worked out the WAMP environment for development, I move on to VS.NET, which I run the 2003 version of. Immediately, Vista tells me there is a known compatibility issue with 2003, so back to google I go. Turns out it's just a couple of quirky things, nothing serious, so I install it anyway. Been using it since with no issue, so the compatibility issues are something I can't speak to yet.

Having done the .NET install, I went back to VS6 to install my old dev environments that I use to support some software I wrote back in the day and still support. While I got compatibility issues with the 2003 version of the development environment, it didn't make a single peep about the old, archaic VS installation from 1998. What kind of logic is this, I wonder? 2003 has compatibility issues, 1998 is ok.

So far, I've been using Vista day in and day out, and once I turned off the UAC, it's been good. I can't shake the feeling, however, that I spent 200 dollars for no other reason except to fumble around learning a new OS for a few weeks. I can't use the Aero interface, I have no sound, and I can't stand to look at my laptop screen.

There are prettier icons and things are smoother, and the networking is a thing of beauty. Downloads and uploads are a lot faster, and finally I have a system on which IE7 does not crash every time I open it. But now, regardless of the default browser set on the system, when you type an address in the address bar, it opens IE7, not your default browser.Ditto for double-clicked htm,html files. It opens IE7, even though FireFox is specified as the default browser. WTF?

Vista is going to be great, and it's going to be adopted across the board, no doubt. It has some definite room to grow, though. Vendors need to get their drivers working, MS needs to patch some of the dumber features (like overriding the default browser), and UAC needs to work better than it does now, or the benefits derived from it will be lost because the biggest support answer on planet earth will become "Turn off UAC" by a host of companies and vendors that need access to any system resources to do their job.

I'm not going back to XP, but I'm not enjoying the experience a whole bunch yet. I need my drivers, and without Aero, how do I know I'm not looking at XP? Prettier icons? A deskbar on the right I replaced with google desktop immediately, because google desktop works and is easier to configure? It looks just like XP with a different wait cursor, unless you have a top of the line video card and can run Aero. And I don't know what that looks like, because I don't.

~A!

WordPress Themes