PHP: Convert US State names to their abbreviations and vice-versa

Sometimes you need to convert state names to their abbreviations. Sometimes you need to convert state abbreviations to their names.

Well, this function does both.

Give it a valid state abbreviation, and it’ll give you the name. Give it a name, and it’ll give you the abbreviation. (e.g. OR -> Oregon, Oregon -> OR). Enjoy! Hope it helps.

/* -----------------------------------
 * CONVERT STATE NAMES!
 * Goes both ways. e.g.
 * $name = 'Orgegon' -> returns "OR"
 * $name = 'OR' -> returns "Oregon"
 * ----------------------------------- */
function convertState($name) {
   $states = array(
      array('name'=>'Alabama', 'abbr'=>'AL'),
      array('name'=>'Alaska', 'abbr'=>'AK'),
      array('name'=>'Arizona', 'abbr'=>'AZ'),
      array('name'=>'Arkansas', 'abbr'=>'AR'),
      array('name'=>'California', 'abbr'=>'CA'),
      array('name'=>'Colorado', 'abbr'=>'CO'),
      array('name'=>'Connecticut', 'abbr'=>'CT'),
      array('name'=>'Delaware', 'abbr'=>'DE'),
      array('name'=>'Florida', 'abbr'=>'FL'),
      array('name'=>'Georgia', 'abbr'=>'GA'),
      array('name'=>'Hawaii', 'abbr'=>'HI'),
      array('name'=>'Idaho', 'abbr'=>'ID'),
      array('name'=>'Illinois', 'abbr'=>'IL'),
      array('name'=>'Indiana', 'abbr'=>'IN'),
      array('name'=>'Iowa', 'abbr'=>'IA'),
      array('name'=>'Kansas', 'abbr'=>'KS'),
      array('name'=>'Kentucky', 'abbr'=>'KY'),
      array('name'=>'Louisiana', 'abbr'=>'LA'),
      array('name'=>'Maine', 'abbr'=>'ME'),
      array('name'=>'Maryland', 'abbr'=>'MD'),
      array('name'=>'Massachusetts', 'abbr'=>'MA'),
      array('name'=>'Michigan', 'abbr'=>'MI'),
      array('name'=>'Minnesota', 'abbr'=>'MN'),
      array('name'=>'Mississippi', 'abbr'=>'MS'),
      array('name'=>'Missouri', 'abbr'=>'MO'),
      array('name'=>'Montana', 'abbr'=>'MT'),
      array('name'=>'Nebraska', 'abbr'=>'NE'),
      array('name'=>'Nevada', 'abbr'=>'NV'),
      array('name'=>'New Hampshire', 'abbr'=>'NH'),
      array('name'=>'New Jersey', 'abbr'=>'NJ'),
      array('name'=>'New Mexico', 'abbr'=>'NM'),
      array('name'=>'New York', 'abbr'=>'NY'),
      array('name'=>'North Carolina', 'abbr'=>'NC'),
      array('name'=>'North Dakota', 'abbr'=>'ND'),
      array('name'=>'Ohio', 'abbr'=>'OH'),
      array('name'=>'Oklahoma', 'abbr'=>'OK'),
      array('name'=>'Oregon', 'abbr'=>'OR'),
      array('name'=>'Pennsylvania', 'abbr'=>'PA'),
      array('name'=>'Rhode Island', 'abbr'=>'RI'),
      array('name'=>'South Carolina', 'abbr'=>'SC'),
      array('name'=>'South Dakota', 'abbr'=>'SD'),
      array('name'=>'Tennessee', 'abbr'=>'TN'),
      array('name'=>'Texas', 'abbr'=>'TX'),
      array('name'=>'Utah', 'abbr'=>'UT'),
      array('name'=>'Vermont', 'abbr'=>'VT'),
      array('name'=>'Virginia', 'abbr'=>'VA'),
      array('name'=>'Washington', 'abbr'=>'WA'),
      array('name'=>'West Virginia', 'abbr'=>'WV'),
      array('name'=>'Wisconsin', 'abbr'=>'WI'),
      array('name'=>'Wyoming', 'abbr'=>'WY'),
      array('name'=>'Virgin Islands', 'abbr'=>'V.I.'),
      array('name'=>'Guam', 'abbr'=>'GU'),
      array('name'=>'Puerto Rico', 'abbr'=>'PR')
   );

   $return = false;   
   $strlen = strlen($name);

   foreach ($states as $state) :
      if ($strlen < 2) {
         return false;
      } else if ($strlen == 2) {
         if (strtolower($state['abbr']) == strtolower($name)) {
            $return = $state['name'];
            break;
         }   
      } else {
         if (strtolower($state['name']) == strtolower($name)) {
            $return = strtoupper($state['abbr']);
            break;
         }         
      }
   endforeach;
   
   return $return;
} // end function convertState()

Getting CORS to work with Apache

Ok, if you’re reading this, I’m assuming you know what CORS means, so I won’t tell you that it stands for Cross Origin Resource Sharing. Or maybe I just told you.

Anyway, you want to enable it on your Apache server. Maybe, like me, you’re building an API-based web app. So you need some JavaScript to pull data from a remote server. (Or even, like in my case, a different subdomain on the same physical server.) It’s easy in Node.js, so it shouldn’t be hard in Apache.

So you google “apache enable cors”. The first result is from enable-cors.org. Wow, how relevant! Sounds so legit! And it says all you have to do is throw this somewhere:

Header set Access-Control-Allow-Origin "*"

So you put it in your httpd.conf file or .htaccess and boom done.

GOIN HOME EARLY TONIGHT!

Except then you try it. And Firebug is all like: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://buckle.up.because.thisll.suck.org. This can be fixed by moving the resource to the same domain or enabling CORS.

And Chrome says: XMLHttpRequest cannot load https://howdare.youthink.thiswouldbe.easy. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://pain.and.suffering.org’ is therefore not allowed access.

But… you did it right. You did just what it said you should do. You even googled it a few more times and everyone says the same thing—Just that one line of code and you’re supposed to be done! You’re supposed to be kicking back with some nachos now! Chrome even says that the header is there, for crying out loud!

Screenshot from Chrome

 

Yeah, no.

Disappointed face
Turns out there’s a friggin metric crapton more to it than that. I won’t go into all the details here, but there’s a lot. What I will do is give you a list of quick and dirty things to try.

Enable mod_headers

There’s a module that allows Apache to add things to the request/response headers. You’ll need that. Near the top-ish of your httpd.conf file, look for…

#LoadModule headers_module modules/mod_headers.so

(Mine was on line 115 in my Apache 2.4 setup.)

If yours has that hash/number/octothorpe/# sign at the beginning, remove it. As with any change to httpd.conf, you’ll need to restart Apache for this to take effect.

“Always”

First, just putting that line in my .htaccess wasn’t working. I could see the header in Chrome (image above) but it was apparently being ignored for who-the-crap-knows-why. Adding “always” before “set” seemed to correct this. I also moved it from my .htaccess to httpd.conf, just above my virtual hosts section.

Header always set Access-Control-Allow-Origin "*"

You’ll also see different versions of this out there in the wild, some where it says to use header set, and others where it says header add. Both will work, but set is safer in this case because add can add multiple headers, which according to the CORS documentation is not allowed.

Depending on your situation, that might do it for you. But probably not. So…

Other headers

Try adding these three:

Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"

The 2nd one determines what headers your requesting server (the one trying to make the remote call) is allowed to send. You likely don’t need all of those, but I left in a bunch for the sake of example.

The 3rd one is super important. It determines what kind of RESTful calls your app is allowed to make. Again, you probably don’t need all of them. In fact, if you’re only doing GET requests, that’s the only one you need. But if you want to POST, then you need OPTIONS, too. More on that below.

GET works, but POST doesn’t: welcome to “preflights”

I won’t go into details here, but I will say that POSTs are different than GETs beyond the obvious ways. One way being that with POST, browsers do what’s called a “preflight” check. Basically, it sends a request before your actual POST, checking to see if it’s allowed to do what it’s trying to.

This was very confusing at first, because my GET calls were working fine, but when I tried to do a POST call, Chrome: 1. showed it as an OPTIONS request; and 2. returned 404 (or 400 or 403 or 500). The heck?

What’s happening is likely that your server is trying to respond to that OPTIONS preflight as a normal page fetch. It’s literally trying to serve up a page. In my case, the URL in question was a PHP script that was expected POST data and wasn’t getting it, so it barfed up a 403 error.

Making PHP return 200 OK for preflights

I had to make a script called blank.php, which contained nothing but some ranty comments. Then, over in .htaccess, I added this:

RewriteEngine On                  
RewriteCond %{REQUEST_METHOD} OPTIONS 
RewriteRule ^(.*)$ blank.php [QSA,L]

What’s happening here is, whenever an OPTIONS request comes in (line 2), it redirects any-and-all of them to blank.php (line 3). Blank.php is, well, blank, so it returns an HTTP status of 200 OK. The browser then takes this as a successful attempt to discover what its OPTIONS are, learns from the other headers (Allow-Origin, Allow-Headers, Allow-Methods) that it’s allowed to do a POST, and then finally sends the real, actual, honest POST that you’ve been trying to get it to do this entire freakin’ time.

Hope that helped

Depending on your environment/needs, that might not be the end for you. If so, you have my sympathy. Either way, I hope this at least gets you a few steps closer.

Access a mapped network drive from PHP (Windows)

So you want to do something on a mapped network drive using PHP, but it simply tells you it couldn’t find the drive? Worry not.

What’s going wrong?

The problem is that PHP runs under the SYSTEM account, and the SYSTEM account can’t access mapped drives.

The solution:

So, we need to map the drive from the SYSTEM account. You can do this from your PHP script, but if you need persistent access, we can do this:

1. Download the Windows Sysinternals Suite and unzip it somewhere you can easily get to from a command prompt.

2. Open an elevated command prompt. (Search for “cmd.exe” from the Start ball, and then right-click on it, and choose Run as Administrator.)

3. Using the command prompt, navigate to wherever you put Sysinternals.

4. Elevate yourself once again to supreme power by using:

psexec -i -s cmd.exe

A new command prompt window will open that is running as the SYSTEM account.

5. Map the network drive:

net use z: \\[IP ADDRESS HERE]\[FOLDER NAME HERE] /persistent:yes

And you’re done!

Warnings:

You can only remove this mapping the same way you created it, from the SYSTEM account. If you need to remove it, follow steps 1 -4 but change the command on step 5 to: net use z: /delete.

The newly created mapped drive will now appear for ALL users of this system but they will see it displayed as “Disconnected Network Drive (W:)”. Don’t worry though! It displays as disconnected, but will work for any user.