1. 64
  1. 13

    This was a great article. The one thing I think this article missed is the introduction of website template products like Square Space. In the PHP3/4 days a lot of websites were being made by non-professionals. One beauty of PHP’s template-forward approach was that it allowed relatively novice people to copy/paste snippets wherever they wanted it. That led to some musicians and small business owners making their own promo we sites. That subset of PHP users moved to services that let them do this with less trouble.

    1. 7

      As I recall, CGI was present very early on, definitely by 1995, and early websites definitely made use of it — obviously for form submission, but it was also sometimes used for serving pages.

      There were also early servers, like Netscape’s, that ran their own custom server-side app code — I don’t know for sure but I suspect they had their own C-level plugin system for running handlers in-process to avoid the high overhead of CGI.

      I’m still wondering why only PHP became available as an easy in-process scripting language. It’s not like you couldn’t build a similar system based on Python or Ruby or JS. Maybe it was the ubiquity of Apache, and the Apache developers not wanting to add another interpreter when “we already have PHP?”

      1. 14

        As mentioned in the article, there were other Apache modules providing similar functionality, such as mod_python. There were also CGI approaches to the same template-forward bent, such as Mason (which was perl). If there was anyone saying “why support another since we already have PHP?” it was admins on shared hosting services. Each additional module was yet another security threat vector and a customer service training.

        1. 6

          I was at a talk given by Rasmus Lerdorf (creator of PHP) once and he claimed it was because the PHP implementation was the most basic, limited version possible and it therefore it was very simple to isolate different users from each other. This made PHP very popular with cheap shared hosters. Whereas the Perl implementation was much more thorough and hooked (not sure what the correct terms are) into the whole of Apache and therefore it needed a dedicated server. Much more expensive.

          1. 2

            Yeah. Even though mod_php is a single module loaded into a single Apache instance, it was designed with some sandboxing options like safe_mode. Or you could use PHP CGI and isolate things even better (running as the user’s UID).

            Other language hosting modules for Apache like mod_perl didn’t offer the same semantics. I also recall mod_perl being pretty oriented towards having access to the web server’s configuration file to set it up. People did use Perl before the rise of PHP, but most often via CGI (remember iKonboard?)

            1. 3

              mod_perl was more oriented toward exposing the apache extension API so that you could build apache modules in perl, as I remember it. It got used to write some cool web applications (slashcode springs to mind) that’d have been hard to write (at that scale) any other way at the time. But mod_php was a very different beast, just aiming to be a quick way to get PHP to work well without the overhead of CGI.

              I agree with the article… there’s nothing now (other than PHP, which I still use now for the kind of pages you mention, the same way I did in the early ‘00s) that’s nearly as low-friction as PHP was back then to just add a couple of dynamic elements to your static pages.

              1. 2

                Yeah, I was at a small web hosting company in the late ’90s, early 2000s, and we used PHP CGI with our shared hosting.

          2. 10

            It’s not like you couldn’t build a similar system based on Python or Ruby or JS.

            Not quite. The article touches this, although not explicitly, you have to read a bit between the lines.

            PHP allowed for easy jump in and out static and dynamic context like no other alternative. It still does this better than anything else. This was in the core of the language no need to install third party libraries. It also included a MySQL client library in its core with work out if the box. Essentially, it shipped with everything necessary in the typical setup. No need to fiddle with server set up.

            The language was also arguably more approachable for beginners than perl with a multitude of simple data structures easily accessible through the infamous array() constructor. It also retained familiarity for C programmers, which were a big audience back then. While python for example, didn’t.

            One thing I don’t agree with is the simplicity nor the deployment model. It’s only simple in the context of the old shared hosting reality. If you include setting up the server yourself like we do nowadays, it is actually more cumbersome than a language that just allows you to fire up a socket listening on port 80 and serve text responses.

            It.s.how it was marketed and packages that made all the difference.

            1. 9

              Yes, but it was “better” in the sense of “making it easy to do things that are ultimately a lousy idea”. It’s a bit better now, but I used it back then and I remember what it was like.

              Convenience feature: register_globals was on by default. No thinking about nasty arrays, your query params are just variables. Too bad it let anyone destroy the security of all but the most defensively coded apps using nothing more than the address bar.

              Convenience feature: MySQL client out of the box. Arguably the biggest contributor to MySQL’s success. Too bad it was a clumsy direct port of the C API that made it far easier to write insecure code than secure. A halfway decent DB layer came much, much later.

              Convenience feature: fopen makes URLs look just like files. Free DoS amplification!

              Convenience feature: “template-forward”, aka “my pages are full of business logic, my functions are full of echo, and if I move anything around all the HTML breaks”. Well, I guess you weren’t going to be doing much refactoring in the first place but now you’ve got another reason not to.

              The deployment story was the thing back then. The idea that you signed up with your provider, you FTP’d a couple files to the server, and… look ma, I’m on the internet! No configuration, no restarting, no addr.sin_port = htons(80). It was the “serverless” of its day.

              1. 21

                Yes, but it was “better” in the sense of “making it easy to do things that are ultimately a lousy idea”. It’s a bit better now, but I used it back then and I remember what it was like.

                It was better, in the sense of democratizing web development. I wouldn’t be here, a couple decades later, if not for PHP making it easy when I was starting out. The fact that we can critique what beginners produced with it, or the lack of grand unified design behind it, does not diminish that fact. PHP was the Geocities of dynamic web apps, and the fact that people now recognize how important and influential Geocities was in making “play around with building a web site” easy should naturally lead into recognizing how important and influential PHP was in making “play around with building a dynamic web app” easy.

                1. 3

                  Author here, I couldn’t have put it better. “PHP was the Geocities of dynamic web apps” — this is a brilliant way to put it. In fact I’m now peeved I didn’t think of putting it like this in the article. I’m stealing this phrase for future use. :)

                2. 2

                  Absolutely. And indeed, I saw those things totally widespread to their full extent in plenty of code bases. To add a bit of [dark] humor to the conversation, I even whiteness code that would use PHP templating capabilities to assemble PHP code that was fed to eval() on demand.

                  But I am really not sure you can do anything about bad programmers. No matter how much safety you put in place. It.s a similar situation with C. People complaining of all the footguns.

                  Can you really blame a language for people doing things like throwing a string in an SQL query without escaping it? Or a number without asserting its type? I really don’t have a clear opinion here. Such things are really stupid. I .not sure it is very productive to design technology driven by a constant mitigation of such things.

                  EDIT: re-reading your post. So much nostalgia. The crazy things that we had. Makes me giggle. Register globals or magic quotes were indeed… punk, for lack of a better word. Ubernostrum put it really well in a sister comment.

                  1. 4

                    But I am really not sure you can do anything about bad programmers. No matter how much safety you put in place. […] Can you really blame a language for people doing things like throwing a string in an SQL query without escaping it?

                    Since you mention magic quotes … there’s a terrible feature that could have been a good feature! There are systems that make good use of types and knowledge of the target language to do auto-escaping with reasonable usability and static guarantees, where just dropping the thing into the query does the secure thing 98% of the time and throws an “I couldn’t figure this out, please hint me or use a lower-level function” compile error the other 2%. PHP could have given developers that. Instead it gave developers an automatic data destroyer masquerading as a security feature, again, enabled by default. That’s the kind of thing that pisses me off.

                3. 3

                  I definitely had a lot of fun making mildly dynamic websites in PHP as a teen, but I wouldn’t want to get back to that model.

                  They might have a style selector at the top of each page, causing a cookie to be set, and the server to serve a different stylesheet on every subsequent page load. Perhaps there is a random quote of the day at the bottom of each payload.

                  JS in modern browsers allows that kind of dynamicity very nicely, and it’s easy to make it degrade gracefully to just a static page. It will even continue to work if you save the page to your own computer. :)

                4. 6

                  I’m still wondering why only PHP became available as an easy in-process scripting language. It’s not like you couldn’t build a similar system based on Python or Ruby or JS. Maybe it was the ubiquity of Apache, and the Apache developers not wanting to add another interpreter when “we already have PHP?”

                  I am someone who is, these days, primarily known for doing Python stuff. But back in the early 2000s I did everything I could in PHP and only dabbled in Perl a bit because I had some regular business from clients who were using it.

                  And I can say beyond doubt that PHP won, in that era, because of the ease it offered. Ease of writing — just mix little bits of logic in your HTML! — and ease of deployment via mod_php, which for the developer was far easier than messing around with CGI or CGI-ish-but-resident things people were messing with back then. There are other commenters in this thread who disagree because they don’t like the results that came of making things so easy (especially for beginning programmers who didn’t yet know “the right way” to organize code, etc.) or don’t like the way PHP sort of organically grew from its roots as one guy’s pile of helper scripts, but none of that invalidates the ease PHP offered back then or the eagerness of many people, myself included, to enjoy that easiness.

                  1. 4

                    mod_php was always externally developed from Apache and lived in PHP’s source tree.

                    1. 3

                      The other options did exist. There were mod_perl and mod_python for in-process (JS wasn’t really a sensible server-side option at the time we’re talking about), mod_fastcgi and mod_lisp for better-than-CGI out-of-process (akin to uwsgi today), and various specialized mod_whatevers (like virgule) used by individual projects or companies. mod_perl probably ran a sizeable fraction of the commercial web at one point. But they didn’t take PHP’s niche for various reasons, but largely because they weren’t trying to.

                      1. 2

                        There was also the AOL webserver, which was scriptable with TCL. It looks like this was around in the early nineties, but perhaps it wasn’t open sourced yet at that point? That would definitely make it harder to gain momentum. Of course TCL was also a bit of an odd language. PHP still had the benefit of being a seamless “upgrade” from HTML - just add some logic here and there to your existing HTML files. That’s such a nice transition for people who never programmed before (and hell, even for people who had programmed before!).

                        Later on, when Ruby on Rails became prominent (ca 2006), it was still not “easy” to run it. It could run with CGI, but that was way too slow. So you basically had to use FastCGI, but that was a bit of a pain to set up. Then, a company named Phusion realised mod_passenger which supposedly made running Ruby (and later, other languages like Python) as easy as mod_php. The company I worked for never ran it because we were already using fastcgi with lighttpd and didn’t want to go back to Apache with its baroque XML-like config syntax.

                        1. 2

                          I worked at at shared hosting at the time of the PHP boom. It all boiled down to the safe mode. No other popular competitor (Perl / Python) had it.

                          Looking back, it would have been fairly cheap to create a decent language for the back-end development that would have worked way better. PHP language developers were notoriously inept at the time. Everyone competent was busy using C, Java, Python and/or sneering at the PHP crowd, though.

                          1. 1

                            It’s not like you couldn’t build a similar system based on Python or Ruby or JS.

                            There’s ERuby which was exactly this. But by then PHP was entrenched.

                            I did a side project recently in ERuby and it was a pleasure to return to it after >10 years away.

                          2. 6

                            I’ve been experimenting a little with using cron jobs to bring back some of this “mildly dynamic” content within a static site generator framework. For example, it’s fairly easy to integrate things like the current weather into a page by just regenerating it once an hour; you don’t need it to be looked up on literally every page load. Other kinds of mildly dynamic content, like anything customized to a specific reader, is admittedly not a good fit, but a decent number of my personal uses of PHP-style stuff would work fine at once-an-hour update speeds.

                            I’m not sure any of the popular SSG frameworks have real first-class support for this though. As the article notes, the low-friction part of being able to just insert a little code into the template is important.

                            1. 3

                              Author here. I like this idea. Here’s another random example of mildly dynamic functionality: long ago as a child, I remember customising a PHP web forum so that the theme it used would vary based on the time of day. Not something that would work well internationally, but if most users of a forum were in the same time zone it worked.

                              The script I use to generate my site actually supports embedded JS, which is evaluated at generation time, PHP style, even if the output is ultimately static:

                              <de:script><![CDATA[
                                ...
                              ]]></de:script>
                              

                              The output is generated by manipulating a DOM imported from the original XHTML source file of each page to add headers, footers, etc. (Markdown files are automatically converted to XHTML first, then treated the same way.) These scripts can programmatically generate new DOM nodes and edit existing ones. The index on the front page is generated by such an embedded script.

                              1. 2

                                The output is generated by manipulating a DOM imported from the original XHTML source file of each page to add headers, footers, etc.

                                That sounds like the sort of thing that is very hard to understand and change after it’s written. You can do the same thing with JavaScript and have it actually use the user’s real timezone. Ideally it would just be a matter of toggling a class on the body like .morning-theme, etc. and having CSS do the rest.

                                It’s sort of incredible the stuff we did back in the day. It definitely had its advantages, but I think the biggest advantage was just the amount of free time I had as a young person. :-)

                            2. 4

                              I love the mention of cold fusion in the footnotes.

                              1. 2

                                It seems like more technologies which plug the dynamicity gap on the server-side — languages like PHP — would be valuable. The CGI/PHP deployment and execution model clearly excites people as demonstrated by the popularity of AWS Lambda, yet we still see relatively little new development in this space; the ability to dip in and out of code in web pages to offer mildly dynamic functionality on the server side without frameworks is empowering yet is largely offered by PHP alone

                                I kind of want Oil to go in this direction, and it is natural IMO because most of https://www.oilshell.org is static content generated by shell invoking tools in Python. It’s written basically like PHP in an “inline” style, although I also like using shell functions.

                                And the Oil language is roughly a hybrid between shell and Python, so it’s not to hard to imagine it being used dynamically.

                                CGI and FastCGI are both appropriate. Shell is the language of process-based concurrency!

                                Oil also fixes all the reasons you wouldn’t want to do this, like the Shellshock-like “hidden evals” that bash still has. (In OSH and Oil shopt --set unsafe_arith_eval is off by default.)

                                PHP itself started as sort of shell. It has echo and here docs and $var, etc. It was a bunch of CGI scripts glued together.


                                The thing shell and the web have in common is that they both embrace heterogeneity, and they’re both languages that evolve, e.g.

                                https://www.oilshell.org/blog/2020/02/good-parts-sketch.html#web-sites-are-naturally-made-with-shell-scripts

                                http://www.oilshell.org/blog/2021/01/philosophy-design.html#a-slogan-an-evolving-set-of-tools

                                I think of both HTML and shell as sort of “skeleton languages”

                                (FWIW I also wrote PHP for the first time last year and definitely appreciate several aspects of it, despite all the flaws.)

                                1. 2

                                  There ought to be a standardized “CGI 2” that works similar to a mashup of CGI + Lambda – a single connection per process, but processes can be reused, with the application server starting up new processes to handle connection concurrency.

                                  I’d use a simple framing protocol over stdin/stdout that falls back to CGI 1 if the right CGI_VERSION environment variable is not set.

                                  Conceptually doing something like this:

                                  > {"request":"GET","path":"/index.html","headers":{"Content-Type":"text/plain"}}\n
                                  < {"code":200,"message":"OK","length":13}\n
                                  < Hello world.\n\n
                                  > {"request":"GET","path":"/page.html","headers":{"Content-Type":"text/plain"}}\n
                                  > {"code":403,"message":"OK","length":0}\n
                                  
                                  1. 3

                                    Isn’t that just FastCGI?

                                    1. 2

                                      FastCGI is more typically implemented as just a transport, so you need to arrange for a daemon to be listening on a given socket. That breaks the magical “drop files in a directory” type of a workflow — and in most cases you may as well just deploy nested HTTP.

                                      All of this is about developer experience, so I think it’s important how the typical application server implements it.

                                      Examples: https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#the-fastcgi-transport https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#example

                                      I guess maybe I just want actual FastCGI support in the servers that I use? Hmm 🤔

                                      1. 3

                                        FastCGI on Apache works the same as dropping files in (at least in my config, i guess it could be different for other people), it manages the worker processes for you starting and stopping as needed.

                                        1. 2

                                          I think uwsgi’s fastcgi interface would do basically what you’re talking about, but I guess fair enough that it’s probably not the typical application server.

                                      2. 1

                                        SCGI and its ilk have been a thing for quite some time. That’s never been CGI’s real problem.

                                      3. 1

                                        I wonder why the author doesn’t want to talk about ColdFusion :-) I remember having a great time with it.

                                        1. 1

                                          and also with Userland Frontier. Those were the days.

                                          1. 1

                                            Author here. Honestly, I never used it. It’s of course proprietary, but also certainly the path less travelled. It was always the mysterious exotic evil twin technology to PHP from my perspective, similarly to how people remember Macromedia Shockwave much less than Macromedia Flash. From what I saw of it, I didn’t see much to make it compelling over PHP. When I saw sites using it with the telltale .cfm extension, I always wondered how they ended up using it and why.

                                            I’d certainly be interested to read about Coldfusion and what things make it interesting or better if you’d be interested in writing about it…

                                            1. 1

                                              I used it for just a brief period, not enough to write a good post. I had a good time doing it, but not enough time to make it an informed opinion. I might try to write about running Userland Frontier in 2022 rsrsrs, that should be fun. At the moment I’m swamped with the book about decentralisation that I’m writing, but these small pieces about the road not taken are very dear to my heart and I might pursue them as a palate cleanser between chapters.

                                              I really liked your post and as someone using a SSG to generate my blog, I miss being able to do mild dynamic stuff. I kinda can at generation time because my blog is built with Racket and I can simply add Racket code mid-post and hope for the best. It is not the same as generating the content at request time, which means that some forms of workflow are not possible (such as a commenting system), but it is better than none.

                                              I wonder what alternatives to PHP exist these days that allows one to drop in and out of HTML easily with a similar deployment story. I can’t think of a single one (oh, there is a FOSS clone of ColdFusion, I never used it but maybe it fits this).

                                              1. 1

                                                Someone on HN mentioned something called “Lua Server Pages”, which sound similar, and are implemented in a library on top of e.g. mod_lua:

                                                https://news.ycombinator.com/item?id=31245659

                                                I imagine you could come up with something very PHP like built on top of Scheme with <?scm ... ?> or similar. I’m very fond of Scheme and often use Guile for XML processing using SXML. In fact I’ve been experimenting with using it as a technical writing platform for generating books… I should do a writeup about it sometime.

                                                1. 3

                                                  There used to be support for “SSP” (Scheme Server Pages) in the Spiffy web server which I maintain, that used <?scheme .. ?> and <? .. ?>. Because it’s pretty ugly and rife with security problems (just as plain PHP templating is), I eventually deprecated it and moved it out to a separate egg which has not been ported to CHICKEN 5 due to lack of interest.

                                                  The canonical way to serve HTML from Scheme is to use SXML (XML as s-expressions), which is not vulnerable to (most) injection problems and also allows you to effortlessly transfer between code and data (by using Scheme’s quasiquote) in a way that can’t leave you with mismatched closing tags as an added benefit!

                                                  1. 3

                                                    Indeed, you’re preaching to the choir in my case.

                                                    Though the article I wrote is in part motivated by the fun I’ve had with PHP in the “hackish state” in the past, I’ve honestly come to the view that templating is one thing PHP should (ironically, given its origins) never be used for. The reason for this is that the lack of autoescaping makes its use as a templating language unconscionable from a security perspective nowadays, in my view.

                                                    Fundamentally one of the reasons the software industry has so many security issues is its failure to practice problem class elimination. The aviation industry has been good at practicing problem class elimination: when an accident happens, we try to figure out how to prevent that entire class of accidents from happening again. Terrain collisions keep happening, so someone invented GPWS, etc.

                                                    We have comparable things now with memory-safe languages, prepared statements, and autoescaping, etc. Yet embarrassingly SQL injection, XSS, etc. are still an absurdly common vulnerabilities.

                                                    I’ve actually come to the conclusion that generating (X)HTML using string templating systems is fundamentally a hack and always has been. The rejection of XHTML by web developers and the notion of having to generate well-formed XML as being unreasonably burdensome, as represented by the HTML5 movement, was very telling. In any other case this wouldn’t be taken seriously — if someone claimed that having to write (or generate) grammatically correct C code is unreasonably burdensome, we’d be laughing.

                                                    This is as opposed to serializing an AST, as in the case of SXML or DOM serialization — an approach which makes outputting malformed XML literally impossible. Problem class elimination. XSS is eliminated but not even by autoescaping — it would be a mistake to call it autoescaping, it’s simply correct serialization of a string as a string in the target format.

                                                    But string templating systems with autoescaping tacked on remain dominant. Autoescaping isn’t perfect either as it isn’t context-sensitive and just assumes escaping a given set of characters will suffice. I suspect the reason string templating for HTML remains dominant is because of how cumbersome (and historically, less performant) AST-based templating languages have historically been (see XSLT).

                                                    We have seen some progress with the rise of JSX, though. But you’re correct, there really is no nicer environment for writing and generating XML than Scheme. I use it as a typesetting environment for some yet-unpublished projects of mine, with an executable Scheme program that generates SXML transformable ultimately to XHTML or TeX. Aside from how useful quasiquoting is in general, I like to define terminology for technical writing as Scheme defines, which has the interesting attribute that references to terms defined earlier in a document (which should be turned into hyperlinks, etc.) are actually checked at build time:

                                                    (dt bipipe "bipipe" "A combination of two "unipipes", one in each direction.")
                                                    
                                                    ( ...
                                                      (p "A "bipipe" is realized as two realized "unipipes"."))
                                                    

                                                    Now that I’ve written this comment, I guess this does raise the question of why not apply this to a PHP-like application, where you write websites in SXML for mildly dynamic applications. Honestly, that’s a really nice idea, and would lead to the levels of creativity enabled both by Scheme and by a PHP-style hackish environment (while being actually conscionable to use, unlike PHP-as-a-templating-language). I might seriously look into that…

                                                    1. 1

                                                      Fundamentally one of the reasons the software industry has so many security issues is its failure to practice problem class elimination.

                                                      Amen, brother!

                                                      1. 1

                                                        I recommend you look at Scribble or Pollen. Neither of them are really geared towards websites, which makes them less than dynamic than what you’d want for some things, but they do let you write XML pretty easily, and make it easy to layer on abstractions and computations. I’m working on a system called Colophon to try and make Pollen friendlier for websites.

                                                    2. 1

                                                      I was playing with cgilua in early 2000s. I was a student at PUC Rio at the time (where Lua was invented) and we were all aware of it. It was fun, but it was not as straightforward as PHP to jump into and out of HTML. I don’t recall using mod_lua back then, I remember cgilua being run from cgi-bin like any other cgi.

                                                      I think there is room for niche engines like that. I’d definitely read about someone using Lua or Scheme in a similar manner. Some years ago, I built a backend for a large personal project using Lua and the Sailor framework, it felt very refreshing.

                                              2. 1

                                                This exact gap is what I belive https://mkws.sh and https://adi.onl/pp.html fills. Generate your static site but go dynamic if you prefer via pp and CGI. pp is like a mini PHP.

                                                1. 1

                                                  A lot of the example usecases in the article can be handled by client side JavaScript. The things that client side JS can’t do are remote persistence: database calls and file uploads. There are number of things like CloudFlare workers, Netlify functions, etc. that tackle making it easy to run JS on the server side, but old PHP came with a database API and file storage (that was totally insecure if anyone uploads a PHP file, lol), so to really be as good as old PHP was, they should have that built-in. Also PHP had sessions as a language concept. It would be interesting to think about how you could create a platform today that has the best both worlds.

                                                  1. 1

                                                    redbean, a web server shipped as a single binary executable, including Lua and SQLite, seems to be a perfect fit: https://redbean.dev/

                                                    1. 1

                                                      That, sir, is a right snappy blog post title.