Have an old or current Classic ASP project that needs converting to PHP?
This is recently just what we did for an internal system, it took a while but we finally got there and launched (Hurar!), I’ll be explaining our process and issues along the way in this blog post. Hopefully this will help someone wanting to achieve the same, what to watch out for and general tips and tricks.
A bit of history first, I started with classic ASP back in around the year 2000, creating some simple websites connecting to an access database. I’d already been programming in VB6 for a couple of years and being able to create dynamic websites in a very similar language was great. ASP did get a few upgrades but essentially it didn’t really change like PHP or ASP.NET has. Having programmed in PHP and C# (ASP.NET and desktop) I knew what was possible (for a sometimes programmer) and the now aging Classic ASP launched around 20 years ago just wasn’t enough. To be honest I’m still surprised that classic ASP lasted so long and has given us such a good run.
Here’s a few details about the website, it was a mostly internal website to manage parts of the business and contained about 200+ ASP files and around 50k lines of code including comments and some HTML.
WHY!?!!? Why would you even bother!
Many times, through the project I did think this but then I revisited the reasons for starting this project and stayed the course. In the beginning I thought this project would probably be done over 3+ months but after starting it we just wanted it completed. Making changes to the original classic asp version wasn’t an issue at the beginning as the conversion tools would just convert this new code. It wasn’t until we had to start editing the newly created PHP files that we had to pause any new development or changes to the old ASP version. It was nearly an even split between time using the tool and manually fixing bugs. It did actually take about 1-2 weeks longer but that was because of some additional major features we added in before launching.
It’s easy to get excited about a project and equally easy to lose interest, converting a project like this is a big drain so keep this in mind if you start a similar project. Keep in mind why and time frames.
So why did we want to convert this application?
Classic ASP has served us well and lasted a LOT longer than we even expected but it was starting to become harder to write well-structured code and we know all PHP is great code. : /
New technologies were coming out fast and Classic ASP wasn’t keeping up, we had to reinvent the wheel to support these technologies or just forgo some of them.
Arrays… enough said, well that is an old “feature” that we had to work around, but something newer like SOAP was a true pain to handle.
We also had to factor in future support in new operating systems.
Changing from proprietary software to open source and cross platform, we already converted from SQL server to PostgreSQL which we now love and moving to PHP gives us operating system flexibility.
Security; not security of the language but security features. We were able to upgrade passwords to Argon2 without much trouble.
Caching is also a challenge with classic ASP and with PHP there are a plethora of options for this.
I think that is enough and what really sparked it was working with JSON and SOAP, it feels like you are constantly having to fight the language too much. A few of the other points were hanging over our heads and it was enough to make the leap.
BUT PHP is old NodeJS/Ruby/Insert flavour of the month is the future
There are some aspects of PHP that could be improved, the groups behind PHP are promising and we believe we’ve chosen a language that will be around for a very long time.
PHP was selected because of existing experience with the language, friendliness to Windows/IIS and syntax compatibility.
When you dig down you realise that classic ASP is very similar to PHP with both having started as more a functional language before taking on some OO features. Converting to anything else and yes that includes .NET would have been a much bigger task; well it wouldn’t be worth converting, you would realistically just rewrite the damn thing and that would be a year+ project.
Work that tool
Without tools, this conversion wouldn’t have occurred or even been thought of. We did research for a tool to convert most of the code previously and found either very basic tools or extremely old tools and at this point we put the project on hold.
We looked at Regex but realised very quickly this wouldn’t work well, you need to understand context with scoping, classes and functions and unfortunately regex just doesn’t have this depth or we weren’t willing to invest the time making it do something it was never designed for.
When revisiting this project we found some old code that did some ASP to PHP conversion, but for anything but the simplest single page it just didn’t work. It was a good starting point and gave us a hint of hope.
We started making some changes to get some of our more simpler pages working and the deeper we went down the PHP rabbit hole the more we discovered and tweaked in our conversion tool, through this iterative process we realised we were starting to get close to something that would get full pages converted and eventually got us to a point where I’d guess around 95% of the code was converted successfully.
Now don’t get me wrong that 5% at the end still takes a LONG time to refactor or fix. Testing was the big factor here and if you’ve used classic ASP before you’ll know how tedious testing is.
We did use some regex for some post processing for fixing a few common oddities, at this point it wasn’t worth changing the tool to support an edge case for the couple of minutes spent doing a regex replace. This was a common theme, get most of the code working with the conversion tool but edge cases can be fixed post.
Changing the original ASP code was also a common theme, if there was a change we could make in the ASP that would fix a more complex issue in conversion than we would go that route. This kept edge case code out of the conversion tool that made it much easier to work with.
As another note, our tool processed files independently, while this was good for testing and made for a much simpler tool it did create scenarios where converting a file that would be included doesn’t itself know that it was part of something bigger. This means that some additional pre-parsing was required to help with converting these files.
If you want to go down this conversion path one thing to consider is that every project is different and some parts were coded specifically for how we manage our database layer or other classes. That brings us to the next section, challenges.
While ASP and PHP are similar there are a few things that are obviously different.
A simple one would be that code lines end with a semicolon compared to ASP’s new line character(s), well the concept is simple and any human would quickly understand when it’s needed and not but it can be a little trickier in code.
Any conversion tool would need to consider the following:
Variables are case sensitive in PHP
Object specifics or rewriting (EG. MSXML converting to CURL is quite different)
Classes don’t have set/get/let
variables within a class need to reference using $this
variable scoping is local, EG a function needs to make a global variable accessible (Globals Ewww)
Sub/function in ASP don’t always have parenthesis
Associative Arrays are case sensitive (Compared to dictionary objects)
GET/POST are case sensitive (think form names)
Some Variable types aren’t compatible (Looking at you Bool and Date*)
In lining IF statements are quite different
COM Objects (If not replacing, understanding properties vs functions)
Now not all of the above are big but added up it does create quite a lot of different scenario’s to consider. What objects you use is quite a big one, case sensitivity was in most part easy to change automatically except for $_POST where it was a bit more painful as it relied on HTML that we were (very badly) ignoring for the most part.
I’ll be covering a few specifics of the issues above in detail below, feel free to skip these if you don’t care for PHP ranting.
As mentioned above, there were a few pain points with Classes and object interaction.
First we needed to understand if a variable was an object or a standard variable as these are handled quite differently. This involved tracking these to make sure we understood if a variable was an object, and then depending on the type of object sometimes do something different.
This could be something simple like if a dictionary object is asked for count we would be changing this to an associative array count.
We had to also keep track of class properties so if these were called within a class function that $this could be prepended.
A fair amount of pre-parsing was used to track these as without it there would have been a lot more manual fixing to do.
COM object also added a bit of pain as there is no automatic way to determine what is a property or function, it means handling these is a little trickier. To get around this we added in a special class to the ASP files that the ASP file would never use but the conversion tool used as hints to process the COM correctly, these were also not pushed out in the final PHP making a fairly clean solution.
Database class, we had a common way to access the database through a database class. This made doing parameterised queries much easier. As this was a core part of the system we manually changed this file over to PHP and then excluded it from any further conversion runs. We did this to a few select classes that needed some human touch to make it convert or to make it run in a more PHP efficient way.
A few other classes/objects we had to touch on were the standard database ADODB objects, filesystem, email, MSXML, dictionary.
Dates (Ohh my gawd dates)
I must admit, I didn’t think I’d find many things in PHP that were substantially worse than ASP Classic… well welcome to the world of pain called dates.
Okay, the new(ish) datetime class does make this not too bad but these are now objects and not a data type, so standard comparisons do not work. I know most new languages base their dates on class object now and not a data type so it would be a major point of difference for any language conversion.
There was no way to fix all the date issues in the conversion but with a few function replacements of the ASP versions and some trial and error, a fair amount of the date pain subsided. Date comparisons were handled manually as each scenario was slightly different, this was just time consuming.
Datediff and date intervals were also a pain, I much prefer how asp classic handled this with a simple function getting the chosen datetime type (year, date, hour, minute). This isn’t just PHP either, dates are handled so differently in each language especially date intervals and date differences that it’s hard to convert these to any language.
Date format, this caught us out and must have missed it in documentation. Depending on how you delimiter the date time so using slashes vs hyphen vs full-stop will change how the datetime is interpreted. This can be a REAL pain with user input dates as it’s much harder to control their input. Things like these though are language quirks that while are annoying at first become understood and are thought about when developing new code.
Now don’t get me wrong, datetimes in ASP classic CAN be a pain but after knowing how to configure dates in ASP and IIS it should work as expected.
One last thing…. *deep breath*… There was a function that did annoy me greatly… checkdate takes the parameters month, day, year. We are not all American at least do it year, month, day which is much more international friendly. Anything that assumes American dates always annoys me, in Australia we do day, month year for most things but I feel year, month, day is better for programming. Phhheww okay got that out, take a breath. ☺
The almighty Boolean
Depending what language you come from will probably change how you see the bool handling in PHP. Coming from ASP this was strange and there were some annoying quirks.
Booleans from PostgreSQL don’t come through as a proper Boolean, I’m a bit surprised by this as it does add a small extra step for Booleans.
Converting… writing out a Boolean isn’t the best, in ASP I could force the bool to a string and it would convert to the “True”/”False” text which was a nice easy way to display it.
Bool is from good old C, A false bool is 0==“null” which will evaluate to false. This makes sense and for C I feel it’s more fitting, for PHP it seems out of place and for an easy language it an interesting quirk and annoyance. It’s funny because the conversion tool is written mostly in C and as a side note, damn FAST.
When using bools in PHP you do need to understand and utilise the datatype and value matching operator (=== or !==). If comparing just with a standard comparison the datatype may get converted. This can also be an issue when comparing a string of “0” and getting unexpected results. So while this is more around bools it can impact other data if you’re not aware of the type casting.
We did write some functions to assist with bools but this does depend on the context so it was hard to provide a blanket rule.
Some other things to consider
Global variables were one that we run in to quickly, we minimised this for most code and changing this in the ASP was good way to do compatibility testing. If you are using strict ASP then you should be much better off but if your code is substandard like ours you’ll find some variables referenced outside their correct scope. We even found a few spelling mistakes that embarrassingly worked because of another available variable including the data that was correct.
To work around globals we had a few globals listed in our conversion tool, if the pre-parser found these in the function it would include those specific to it. Initially we had all the globals in every function but this was messy and unnecessary as only a small amount of functions required these globals. Also because of how globals worked, we tried to minimise the amount of these in general.
COM Objects might not be as reliable. While most do work we did run in to a few issues as COM uses a variant data type that PHP would not allow me to see properly. We are in the process of removing these COM as time goes on but for now we have removed the problematic ones and kept those that worked.
Some COM were to supplement ASP with missing features and in most of these cases we were able to use built-in functions or load a module that filled the gap, an example of this would be Uploading.
Let/Get/Set are not a standard feature in the OO model of PHP. This did make a few classes fail and in these cases we manually adjusted them. Our conversion tool changed these so as to avoid function name conflicts and we finished off the rest.
We could change these to magic functions in PHP but I’m not sold on it and it make for very messy or confusing code. This is one thing I’d love to see added to PHP but understand the complexities in doing so.
Performance, this was something we were hoping to gain and in the end we did get a small improvement in performance. Considering how old classic ASP is I was more impressed how close it was to PHP still even after all these years. PHP has certainly been given a massive increase in speed since the launch of PHP7 which we were using so not sure we’d have seen much increase at all using the older 5.x releases.
A big benefit of using PHP is the community and support for new technologies. While Argon2 is coming to PHP as a standard module, it’s not there yet and it was still easy to change over to. We had to use libsodium as an extension and it works great! There are plenty of other algorithms you can use as well depending on your use case without having to create a COM or use a slow native VBScript/Jscript implementation.
We are excited that now we can keep up with security improvements much quicker now thanks to the great community.
We all love Email
And PHP does make this quite easy, we used an existing library to simplify this and as we used a templating system we only had to change the email from a COM to the PHP class in one spot.
We didn’t program in changes to the CDOSYS object as we didn’t have to change many of these and knew syntax would change with a few differences between the PHP library and CDOSYS.
ASP and the PAIN of uploads
Anyone who has used ASP knows that ASP and uploads are not the most fun thing in the world. After years of using some COM’s we are familiar with these and work arounds but with PHP having a built-in way of handling these, it is certainly much nicer. In the case of uploads the code to handle it was quite different from the COM we were using so forgo COM specific conversion and did this manually. Since we only handle uploads in a few spots this was the best use of time vs programming it in.
PHP does do things funny sometimes and the way it handles uploads is a bit quirky, first there is the way it handles a single upload vs multiple but it’s an easy work around and then there is the way the array works for the uploads. We can only guess why it was originally implemented this way but it works and it’s unlikely to change.
It’s all about the money
Sounds much better than Sigil (https://en.wikipedia.org/wiki/Sigil_(computer_programming)) or variable prefix… But I do now feel much more wealthy having all these dollar signs around my code. I’m like the PHP monopoly man, PHP will make me rich *Swings walking stick around*
On a more serious note, the concept that variables are prefixed by a $ sign is easy to understand and you know instantly if something is a variable or not. Where this fits in the conversion process is knowing what is and isn’t a variable as ASP doesn’t have a variable sigil. You could pre-parse and figure out all the variables, or you could just assume that if it isn’t anything else like a function or known symbol/keyword then it’s most likely a variable which is the route we took. There is a bit of context in this as well but it works 99+% of the time. If anything, you end up assuming some functions are variables as the function is unknown.
“Can we make this faster?”
Every programmer has had those moments, you finish a project and then the user focuses on the one thing that is possibly bad… performance. Well you suck and you should have made it faster in the first place!
Well I must admit that users do care about speed and while we shouldn’t jump the optimisation gun we need to keep it in mind. Without trying to over optimise everything in the code, we focus on those slower parts and keep drilling down to the biggest issue. PHP is great for profiling with the help of XDebug and other tools available.
Having used caching in .NET not having something similar available in ASP was a pain while also not having connectors to in-memory database stores like Redis or Memcache. We didn’t quite need a full blown in-memory store like Redis but a simple shared memory cache for things that don’t change very often like configs that are stored in a database. Being on Windows and IIS still we used Wincache, this nice extension provided us with shared-memory caching as well as it’s built in support for session management and file caches. Anyone running PHP on Windows I’d recommend using it. We don’t currently use it for much as most of our data needs to be live from the database but for those spots we do it’s great to have available.
Speaking of performance, we did make a funny mistake when converting the database layer, we used pg_connect instead of pg_pconnect. Now as it’s SOO damn close we missed the spelling mistake and were very concerned about the database performance. PostgreSQL is very heavy when opening a new connection using around ~100ms to do this task. We realised quickly that it was a pooling issue and found the solution. We also quickly had a look at alternatives to make sure we had the best solution. We run PGBouncer a PostgreSQL pooling service which ends up being around the same speed as pg_pconnect. ODBC supports a pooled connection as well and was VERY close in speed to pg_pconnect being a touch slower on average. Using ADO through COM is also possible but it didn’t appear to pool the connection so it was very slow.
In the end we forgo PGBouncer only because it can have issues with some queries and it’s an additional failure point we would have to maintain. I’ll be keen to revisit this decision later as it certainly has its use case.
Features aren’t always what they seem.
Even though we have experience with PHP, it was limited and on older projects. We were excited to use a few new features in PHP and while we certainly did change some to use these like the null coalesce operator, a few didn’t really work the way we wanted when we understood them better.
Having more experience in c# or the .NET framework more so, we were disappointed with the implementation of “namespaces”, hence the quotes as I feel these aren’t really what I’d call them. We were hoping to change some code over to use these but having to add slashes throughout our code… well let’s just say we didn’t like that idea. We’ll certainly revisit this at a later stage but I feel it’s going to be hard for PHP to change namespaces to what I and others envisioned without breaking backwards compatibility quite severely.
Error handling was also something we expected to work slightly different and while PHP7 did improve on this we felt other language implementations were better.
Always keep improving
Like any project, we want to keep this one moving forward in terms of features but also fixing bugs, maintainability and security. We would like to refactor some of our code over the next year to conform to the PSR standards. Some of these won’t be too hard to reach but others would require massive re-writes so we need to focus on the quick wins first.
Autoloading is AWESOME, while we aren’t utilising this as much as we could be right now as we extend our software this will be used more and a few new features we added in took advantage of the dynamic way PHP works.
Dynamic everything, ASP can do more dynamic calls than most think but it’s convoluted and slow. Being able to run functions using a string in a variable is standard in PHP but not in ASP. Of course, a conversion tool isn’t smart enough to refactor our code to be more dynamic but as we continue writing new features it’s certainly nice having it available. We did have to change a few dynamic calls in ASP which was VERY easy and we didn’t use it often so was a quick find/replace job.
This post wouldn’t be complete without some PHP bashing (if there wasn’t already enough)…. we could discuss the fun of PHP’s inconsistencies but there’s plenty of other sites that do that.
The more we work with PHP the more we find and really, we just have to laugh… or there would be expensive hardware bills to pay.
Really every language has its quirks, PHP just has more than usual and we don’t care. It does the job we need it to do with the performance and features required to get the job done.
Farewell Classic ASP
With the main conversion project completed we really do say goodbye to Classic ASP.
You did us well for MANY years and it’s amazing how much this language taught me about programming and the web in general. There is always a language you go back to and ASP was that language until now. It was easy to create something basic without too much “magic” happening.
I’ll miss your structured and consistent nature while glad to leave awful error catching and shitty arrays behind.
I feel the PHP code is slightly messier looking than ASP but I’ll put that down to having looked at ASP code for so long it’s just part of my synaptic network and looks natural.
Help where possible
If you made it this far we hope you have found this post valuable and now understand that while it’s a big task it’s certainly possible and can be completed quite quickly.
We would love to hear about your conversion projects or attempts, leave a comment below or reach out to me matt (that at symbol) aspireweb.com.au and we’ll try to help out where we can.