Archive for the 'php' category

Return to blog homepage

Older Entries

Kill your index.php

29 Jan 2014

Although not all PHP frameworks are "Rails clones", the history of modern PHP frameworks really begins with the rise in popularity of Ruby on Rails.

Until Rails started to gain serious mindshare amongst developers and market share amongst consulting clients, no-one was really talking about using MVC patterns in PHP applications.

If you look at the list of frameworks on http://www.phpframeworks.com/ (which is pretty old) you find that PRADO was the only framework that was released before Rails became popular (it was released about the same time as Rails), and one of the only ones that doesn't cite Rails specifically as an inspiration[1].

Most importantly, Rails brought a term into the web vernacular in a big way and that was "MVC".

Even though Java's Spring MVC framework was released well before Rails You can see that the release of Rails had an impact on the popularity of people looking for "spring mvc":

CMS added image

The appeal of index.php

The core idea of a web developmernt framework is to improve developer productivity by:

  • Automating or simplifying repetitive tasks, making them both faster and less error prone
  • Making it easier for multiple developers to collaborate on the same project

Every PHP framework I've ever come across includes in it the main "entry point" for the application, usually called index.php or something similar.

The PHP framework zeitgeist was very heavily influenced by the Rails MVC and ORM models and implementing this without a central "application entry point" seems clunky.

You don't want to "spoil the illusion" that we're working in a purely object orientated environment by exposing the developer to some procedural application entry point (this could well be Java's influence, which took the C language's "main" function and hid it from the developer altogether).

I know this in part because I fell victim to the same thinking.

"Projects which were, according to the collective wisdom of the time, terribly written "spaghetti code" were in fact the easiest to understand, fix and modify."

I had been working on some framework-like ideas and when Rails was released a lot of these ideas were included in my own framework called RocketSled.

The first versions of RocketSled were a hideous, monolithic beast that never gained any traction with anyone except me.

Following the development of a few larger web applications I started to re-think how I wanted to structure my code.

One of the biggest realisations I had was that making PHP application structures more "standardised" was not succeeding in creating something on which it was easier for a range of different developers to collaborate.

The more I started working with other people's code, maintaining existing projects, fixing problems on websites that had been abandoned by other developers and working with frameworks I had not written myself, I realised that the more cleverly written an application was, the higher the barrier of entry for me to understand it so I could fix bugs or make changes.

Projects which were, according to the collective wisdom of the time, terribly written "spaghetti code" were in fact the easiest to understand, fix and modify.

Around the same time, I started lurking in #stackvm on freenode and looking at the way James Halliday (aka Substack) and Peteris Krumins were writing their software using "very small modules" and began thinking about how I could strip everything away from RocketSled and what a truly minimalist PHP "framework" would look like.

Here's a rough bullet list of what I wanted to achieve:

  • I wanted a framework that did almost nothing: no templating, no URL structure, minimal routing, no package management; all this stuff should be the job of "userland" packages so you can pick and choose what you wish to use easily at any time in a project's lifecycle
  • Whatever it did do, should be completely optional. You should be able to override whatever default behaviour is provided "out of the box" as and when it becomes convenient to do so; if some feature of the framework stops being a help and starts being a hindrance, you should just be able to leave it behind and do something else
  • You should be able to hack things together however you want in order to get the job done (arguably one of the core philosophies of PHP as a language)
  • I wanted to have a system for installing little modules (similar to Node's npm)
  • I wanted to make use of the __autoload feature (PHPs strongest feature IMHO[2])
  • I wanted to make it simple to use the huge body of PHP code already written, without having to modify it to fit in with the framework
  • I didn't want the code that I wrote to be tied to any framework

I had already banished the concept of using a templating engine in favour of Template Animation so any benefits that existing frameworks had to offer around structuring views or providing a "separation of concerns" between business and display logic were a non-issue.

As I toyed around with this concept I started writing the first versions of the newly stripped back RocketSled, and a package manager called RocketPack which would install code from git repositories and manage dependencies.

Source code management: the thorn in my side

I wanted to be able to install the framework code and leave it completely unmodified, then have all my code, configuration etc. happen in "userland" packages which would have their own source control repositories (I use git but whichever SCM tool you use the principle is the same).

This inevitably lead to all sorts of directory scanning to find configuration files and various code location configuration options so that I didn't have to alter the RocketSled code once it was installed.

In order to resolve this issue, I started coming up with a directory schema and configuration "injection" system, but I couldn't seem to come up with anything that just didn't feel like a rehashing of the same old "directory structure dictatorships" common to every other PHP framework in existence.

Nothing I came up with truly addressed my goals of having something that was essentially invisible, essentially meaningless and essentially not a framework.

"Once you take away package management, templating and routing (all of which I wanted to be the responsibility of interchangeable userland packages) what does a framework really do anyway?"

Before Rails, there really wasn't a way to write web applications in Ruby. Before Django, there really wasn't a way to write web applications in Python.

That's when I realised that what others had been saying for so long is true: that PHP is a framework.

Why was I so hell bent on having an "index.php" as part of a PHP framework rather than as part of the application code for the project I was building? Why did my "framework" have to provide the main application entry point?

Rails and Django only did for Ruby and Python what PHP had already done for itself, but the structure of those frameworks made the PHP community jealous. Everything looked so well organised and clearly documented. PHP raced to catch up; raced to be as MVC as Rails.

When I examined the applications we'd built with this new version of RocketSled the config files that were contained within each package were just arbitrarily formatted PHP files, and I'm a huge fan of PHP as a configuration language in PHP projects (rather than having configs written in JSON or YAML or XML or whatever). The configuration files were all pretty minimal, and were just chunks of "procedural" code to define constants or include required files (such as autoloaders for other packages).

Whilst I was trying to come up with some clever way to prevent the developer from ever having to touch the YUCKY and PROCEDURAL index.php file with a weird concoction of directory schemas and configuration injection points and directory scanning, I could much more easily just create tools that did some stuff that I found repetitive and define a simple way to use them from any application (ie. from an "index.php" not under the control of the "framework" itself).

Once you take away package management, templating and routing (all of which I wanted to be the responsibility of interchangeable userland packages) what does a framework really do anyway[3]?

So I refactored RocketSled into what it is today: a non-framework tool for managing your autoloading class paths in a way that's completely override-able by anyone who chooses to use it and that doesn't interfere with the way that any other class loaders you might wish to use already work.

Unfettered by the shackles of my "index.php" rewriting RocketPack to be a standalone package management tool for git repositories became a triviality.

I then came up with a basic "boilerplate" for using RocketSled with RocketPack and put that into it's own repository at https://github.com/iaindooley/RocketSledBoilerPlate.

The key here is that RocketSled and RocketPack themselves are completely independent of the methodology used in RocketSledBoilerPlate. You could come up with your own RocketSledBoilerPlate. I can change my mind later on pretty easily: none of the code I've written has any dependency on this way of doing things.

If I want to use a PHP package that already exists, I just have to include the autoloader/bootstrap file provided by that package. I don't have to worry about changing that package to fit in with my framework at all.

The workflow we follow now is:

  1. Clone the boilerplate
  2. Create a new repository using these files
  3. Modify the code as required

One could argue that I have broken my original rule of "not modifying the framework" code, but the code contained within the RocketSledBoilerPlate project is so minimal that I don't really see this as an issue.

[1] Symfony is another but you can find empirically that there was a big influence on Symfony from the rise in popularity of Rails [take me back to where I was]

[2] I'm obviously not the only one who thinks so, either. Check out this fantastic article from the team behind Propel: http://propelorm.org/blog/2011/03/21/the-end-of-autoloading.html [take me back to where I was]

[3] I think there is a common move towards this non-monolithic style of framework. For examples, check out this post by Fabien Potencier on what Symfony2 is: http://fabien.potencier.org/article/49/what-is-symfony2 and this fantastic talk by Kenneth Reitz "Flasky Goodness (or why Django Sucks)" http://www.youtube.com/watch?v=U2frH932U1g [take me back to where I was]

Filed under: php | View Comments

I've been working with the Basecamp API to plugin our IRC bot that we use for time tracking and I'm astounded to learn that escaping single and/or double quotes for XPath queries in PHP does not have a well documented, best practices solution.

In fact, it seems as though this is not peculiar to PHP. I took a look around and found this excellent article by "Kushal" (s/he doesn't have his full name on his blog):

http://kushalm.com/the-perils-of-xpath-expressions-specifically-escaping-quotes

I've produced a PHP solution for the general escaping/concatenation problem:

https://gist.github.com/1155973

Anyone got a better/more elegant solution? I'm always a big fan of code golf :)

Filed under: php | View Comments

I recently deployed a job on which the timeline was so tight that my ability to type quickly was what made the difference between delivering on time or not.

Everything was rushed, the budget was tight, it was one of those real seat of the pants deals and there was far too little testing done.

Just before I cut the site live (ie. minutes before I had to put this into production and have hundreds of people using it) I thought "You know, I bet something will go wrong somewhere here and I'll need complete logs of all the SERVER, POST and GET requests to piece it back together".

Boy am I glad I had that thought! Because it did. Nothing catastrophic, mind you, but there was a tricky integration with an external system that, as it turned out, periodically died without any errors or exceptions and just started returning bogus values.

The only problem is that, in the 5 minutes before the site was supposed to go live, I didn't really have much time to thoughtfully prepare a logging system to record all this stuff and, in my haste, I settled for:

catchall_logging.txt

When I came to actually process this information into something usable I realised what a dumb format for a log file this was and began kicking myself.

I had a look around for a snippet to parse the output of PHPs print_r but couldn't find anything so I thought I'd post my solution here just in case anyone else ever makes the same mistake - might save them a couple of hours (I've added comments in the code below):

logfile_antipattern.txt

Filed under: php | View Comments

This article assumes you have already experimented with using the vim debugger as described in this fine article. Big respect to Sueng Woo Shin for writing the debugger script itself. If you've already experimented with it and think you want to use it more then read on...

Using a debugger wins hands down over the good old echo/die approach but if you want to use the aforementioned debugger (which you do because you want to use vim right? right!) then you may need to get used to a few little quirks and maybe even tweak it a little bit so feels a bit....righter.

So with that in mind here's a few little things that I've done and realised that have made it start to feel that little bit righter to me ...

How to debug a script which is invoked via the PHP CLI

To debug a page being served up via apache you point your browser to:

http://example.com/index.php?XDEBUG_SESSION_START=1

in order to set a cookie your browser and then the presence of this cookie indicates that you're keen to debug some stuff.

If the code you want to debug is being run via the PHP CLI then the above won't work so instead you need to set an environment variable before you run invoke the PHP CLI to run your script. This works for tcsh:

setenv "XDEBUG_CONFIG=vim=1"

Then run your script via the CLI (eg, php -f my_script.php) and as long as you have Vim open and have pressed F5 (like you normally do when you want to debug in vim) then you should be able to debug your script.

What's going on with regards to the files that get opened when a debugging session starts up?

Well regardless of if you like it or not the debugger always opens the "first" file in the call stack, if you like. So if you are debugging a handler that gets invoked from say http://workingsoftware.com.au/myapp/index.php?h=FindProduct then it always opens index.php. This is quite annoying cos most of the time you don't really want to debug index.php...in the above example you probably want to debug find_product.class.php, which is the file where the application code actually resides (whereas index.php is part of a framework and you're usually interested in debugging the application, not the framework). (ed: this is referring to usage of the RocketSled PHP framework but will be similar for any framework where you have one script which dispatches control to a Controller or Handler class or script)

There's no real way around this that I can see. So this is what I do if say I want to debug a file called find_product.class.php which is a Handler class (or Controller in MVC terminology) invoked by hitting the URL http://workingsoftware.com.au/myapp/index.php?h=FindProduct:

  1. cd myapp/packages/myapp/handlers/
  2. open find_product.class.php at the command line by doing: vim find_product.class.php
  3. hit F5
  4. quickly (within the 5 second limit....see below - I have made this limit longer) go to the URL in your browser (ie, http://workingsoftware.com.au/myapp/index.php?h=FindProduct)
  5. the debugger will open up index.php and in the process close your find_product.class.php leaving you with index.php and the other debugger specific windows. Bit annoying. Since you dont want to debug index.php close this file by doing :q
  6. now you're left with just the debugger specific windows (actually viewports) in vim. Now open up the file you want to actually debug, find_product.class.php, by doing :sp find_product.class.php
  7. Now go ahead and do your debugging. Once the debugger session finishes you'll be left with the find_product.class.php file open in vim.

The good news is that if you set any breakpoints in your find_product.class.php file while you were debugging, as long as you don't close vim itself (doesn't matter if you close the find_product.class.php file and reopen it, as long as vim stays running) then you can hit F5 again to start another debugging session and repeat the above steps and once you've gotten back past the final step you will see that the breakpoints you set in the last debugging session have been remembered in the new one.

What are all these bloody windows?

I have found that using the debugger was made quite hokey due to the fact that the screen space got crowded out with a lot of superflous stuff. I can't see, at this stage, why you really need the TRACE_WINDOW or HELP_WINDOW to be there in every debugging session. The WATCH_WINDOW and the STACK_WINDOW are the only real useful ones.

So I have edited the debugger.py file to do away with these windows. You can download my edited version here

http://www.workingsoftware.com.au/downloads/debugger.py
NB: All the original code has been more or less left in there, I've just commented the various bits out. You can run a diff on the original to see exactly what the changes are.

Now when you start up a debugging session you'll see the file(s) you're debugging, the WATCH_WINDOW and the STACK_WINDOW all stacked on top each other like you'd get if you did :sp normally.

The fine peeps who developed this debugger have mapped the F1 key so that it resizes the debugger windows. This is cool but I found you had to press F1 three times to get the window to resize the way I wanted it. So I edited debugger.py again to make it just toggle between the equivalent of Ctrl+W= and Ctrl+W_ . So when you're debugging if you are in the find_product.class.php file's window and you press F1 it will blow that window up to take up the majority of the screen. Press it again and it will go back to an even split between find_product.class.php, WATCH_WINDOW and STACK_WINDOW. Go into the WATCH_WINDOW window and press F1 and it will make the WATCH_WINDOW blow up, hit again and back to even split. Same goes for STACK_WINDOW obviously.

Controlling the debugger - keyboard commands

I realise this is probably stating the obvious for a lot of people and that it is also covered in the debugger script documentation but I'll include it anyway for completeness... Also if you're using the aforementioned edited version of debugger.py you will no longer have the HELP_WINDOW in your debugging session so you'll have to remember the commands with your brain... this might help:

  • Command

    What it does

  • F1

    toggle resize

  • F2

    step into

  • F3

    step over

  • F4

    step out

  • F5

    run (to next breakpoint or end of script)

  • F6

    end debugging session

  • F11

    dump context to WATCH_WINDOW. this will dump the values of all the variables in scope into the WATCH_WINDOW

  • F12

    if you have the cursor positioned over a variable in the file that you are debugging then hitting F12 will dump the value of that variable out into the watch window. NB: I have listed 2 caveats associated with F12 below

  • ,e

    If you do ,e and then type in the name of some variable then you will see that variable's value. If its an array you'll see the array contents, if its an object you'll see the object's contents. if it's empty or out of scope you'll see (null).

  • :B

    sets a breakpoint

  • :Bp

    unsets a breakpoint

F12 caveats

  • If you hit F12 and the cursor is positioned on an empty line then it will cause vim to go into INSERT mode. Quite annoying but good to know.
  • If you hit F12 and the cursor is in the WATCH_WINDOW or the STACK_WINDOW then it will print "no commands none" and end yourde bugging session. Again annoying but good to know.

A couple of handy commands for managing breakpoints

So you can use :B and :Bp to toggle a break point on/off. But some other useful stuff that's not immediately obvious is:

  • Command

    What it does

  • :sign place

    lists all breakpoints, showing which line each one is on

  • :sign unplace *

    clears all breakpoints

  • ???

    go to next breakpoint (haven't figured this one out yet ... anyone?)

Five seconds doesn't seem like long enough..

The debugger.py script is hard coded to wait 5 seconds for a connection after you've pressed F5. This didn't seem like long enough to me so I changed it to wait 15 seconds.

This change is in the edited debugger.py which you can download here:

http://www.workingsoftware.com.au/downloads/debugger.py

Just search for 'serv.settimeout(15);' and change 15 to however many seconds you think is reasonable if you want to change it.

So they're a few little things I found handy. If you find more let us know ;-)

Comment Archive

This post originally appeared on an older version of the Working Software website which implemented it's own comment mechanism. This new version of our website/blog uses Disqus for comments (see below) however I wanted to preserve the comments made on the previous post here:

hi just to add another usefull thing :Bp would set up a breakpoint with an expression according to context you might wanna check, http://slackdna.blogspot.com/search/label/vim-dbgpclient

Zeft (2009-03-11)

Filed under: php | View Comments

Usually, you can keep the design of your interface pretty well separated from the logic behind it. One example where this falls down, however, is in the case where the interface uses image inputs.

The image input type allows the use of an image for a button. They're not really all that popular due to accessibility issues, but this is not an ideal world and sometimes you have to work to a design brief that includes image buttons.

So the catch with these is that Browsers send the name attribute of the button to the server differently. In Firefox, for example, you get three $_POST variables in PHP:

  • button_name
  • button_name_x
  • button_name_y

The x and y are the positions of the button on the page and are (were) used in server side image maps. However Internet Explorer will send only the latter two values:

  • button_name_x
  • button_name_y

So the safest way to check if a particular button was clicked in your PHP script (or other server side scripting language) is to look for the parameter with the _x or _y appended (in .NET this becomes .x or .y):

post_argument_check.txt

The use of both _x and _y here is redundant but I thought I'd just include it for the sake of completeness.

The problem I have with this is that it ties the way the form is being processed to the way the form appears. So my workaround for this is to always use a function to check for the presence of button parameters:

button_clicked_function.txt

That way you can check which button the user clicked and it won't matter if a later design change adds in image buttons.

Filed under: php | View Comments

I recently had to take a string with some delimited values as placeholders and replace them with variables from an array. This is useful for things like sending out email newsletters or SMS messages with people's names or other unique information in each message.

An example of a string with placeholders in it, using the string '%%' as a delimeter is:

Hello %%NAME%%, your %%CAR%% is ready to pickup from %%STORE%%

Now take an array with some key/value pairs:

key_value_pairs.txt

The following code will perform the replacement based on the filter:

replace_code.txt

The interesting parts of the above code are, in the pattern:

pattern_1.txt

the "?" makes this a "non-greedy" match, meaning that it won't match everything in between the first and last occurence of $delim. Using "?" in this way allows you to make individual matches non-greedy. You can also use the /U modifier at the end of the expression to make all matches non greedy:

pattern_2.txt

The next interesting bit is the fact that the second argument to preg_replace_callback can be an array with an object and a method. This allows you to use preg_replace_callback in your OO based PHP application without breaking out into procedural code (cos where would you put it!?).

Filed under: php | View Comments

I'm working on a job for a client where legacy database data are being used to generate an XML document for processing with an XSLT stylesheet.

The data are encoded HTML entities in the database. So when I created my DOMDocument, I got the following warnings:

Warning: DOMDocument::loadXML() [function.DOMDocument-loadXML]: Entity 'middot' not defined in Entity, line: 963 in /usr/local/www/data-dist/sheds/includes/SDEHSFunctions.php on line 414

Instead of passing in '·' in the XML string to the constructor of the DOMDocument object, I needed to either declare all entities in the XML doctype (bothersome) or I needed to convert these text entities into numeric ones (eg. '·' becomes '·').

I took a look around and found this handy function:

http://php.net/get_html_translation_table

I did a print_r on the translation table returned and found that it returns an array where the key is the actual character represented and the element is the textual HTML entity. So here's a quick function to get the character coded equivalent:

html_entity_convert.txt

Filed under: php | View Comments

Here is my little recipe for getting PHP4 and PHP5 to run concurrently on FreeBSD using Apache 2.2 and mod_proxy.

First, check this out:

http://wiki.coggeshall.org/37.html (ed: this link is currently broken - I've emailed John to see if he has an updated link)

That's the basic recipe, however there are a couple of problems with it. The first is what appears to be a syntax error (perhaps an incompatibility between 1.3 and 2.2 versions of Apache) that I fix later, the second is that this ProxyPass only does half the job! I found that, using this method, if i went to the PHP4 host, it displayed the correct index page, however if i put something like http://php4.myhost.com/subdir then it changed the URL of the browser to be http://127.0.0.1:8081/subdir! This wasn't to do at all. Thanks to megapaz in the apache chatroom on freenode for pointing me to this next article (and thankyou to niq also for writing it!):

http://www.apachetutor.org/admin/reverseproxies

So I also need a reverse proxy. That article is really long and deals with the issue in great detail, more detail than I required. I do not need to deal with multimedia content, javascript links or problems with links in general because I am only using PHP4 to run one very specific application.

There is one specific upshot of this: the discussion below deals with compiling the proxy modules from the FreeBSD ports system, however when I tested it out, this did not include all the modules mentioned in the above article, so in order to get all those modules compiled in, you will need to look into which 'KNOBS' you can turn on (see below) or manually change the Makefile.

Compiling with mod_proxy from FreeBSD ports

Because when you install from a port in FreeBSD you don't actually type ./configure, you have these things called 'knobs' that allow you to set/remove common options. In order to configure with mod_proxy when installing Apache 2.2 from the ports, I did this:

compiling_with_mod_proxy.txt

I then include the following lines up the top of httpd.conf (where I
have added line breaks for readability, they are marked with "\"):

proxy_httpd_conf.txt

Installing another Apache instance

For this I just downloaded the latest version of Apache 2.2 from the apache.org website and cut and pasted the instructions in John Coggeshall's tute above to install it without the ports.

PHP Installation

Now that I had Apache 2.2 installed from the ports, I went through my usual PHP5 installation for which I don't actually use the ports. So I just get the latest from php.net and have a script called runconf.sh which has my configure line in it that I've been using forever:

php5_runconf.txt

Once that was all up and running, I needed to install PHP4. So I downloaded the latest version of PHP4 from php.net, and used the following line in configure:

php4_runconf.txt

So when I installed, my PHP4 installation was located in my second Apache installation folder. I then copied php.ini-recommended into:

php_ini_recommended_dir.txt

And downloaded the latest APC (Alternative PHP Cache). I wanted to install it for the PHP4 installation, so I followed the instructions in the INSTALL file, but used the phpize and php-config included in the php4 folder I had installed under my apache_php4 installation:

apc_php4.txt

If you haven't used phpize on this machine before it will complain it can't find autoconf etc. FreeBSD comes with autotools 253 and 259. I've found that 259 works for phpize, so I do:

autoconf_freebsd.txt

Configuring and running Apache

I won't bother going through Apache configuration here, but there is a mistake on the Coggeshall wiki (well, for v2.2 it doesn't work, perhaps it worked for 1.3 or something). I put a file called php4.conf in /usr/local/etc/apache22/Includes/ with the following in it:

php4_config.txt

And use the 'Listen' directive explained in the Coggeshall wiki in the instance of Apache that is running PHP4.

I then run my Apache PHP5 instance as normal, and use the good old apachectl for the PHP4 instance:

php4_php5_start.txt

Bug 37770

After running for a few hours, I noticed that the Proxy would mysteriously stop working, and would return a 502 error page with the error:

proxy: error reading status line from remote server 127.0.0.1

This was a real doozy, as it was a very intermittent bug and didn't seem to be related to load or anything in the code that was being executed. I tried running httperf on it to load test and could not reproduce the error reliably. After some reading, I found this blog post mentioning a similar error using mod_proxy to run Mongrel for Ruby on Rails hosting:

http://blog.pomozov.info/posts/apache-mongrel-and-proxy-error.html (ed: This link is broken. I've attempted to contact Anatol Pomazov to see if there is an archive of the content somewhere)

I also found this mentioned here:

http://httpd.apache.org/docs/2.0/mod/mod_proxy.html#envsettings

So from this I concluded that my VirtualHost include should now look like this:

php4_config_2.txt

I then restarted both the PHP5 and PHP4 Apache instances:

php4_php5_restart.txt

and I am yet to see the error recur having run the system for nearly 48 hours since doing it. If the error does recur I'll update my post but it appears to be fixed.

That's it! Now I can point my browser to http://php4.myhost.com/ and it serves up pages using PHP4, but maintains the correct domain in the browsers URL bar. Because I've used a VirtualHost directive to pass to the PHP4 instance, I can easily make any domain I'd like get served up with PHP4.

Filed under: php | View Comments

Subscribe

Subscribe via RSS

Building software in the real world - the Working Software blog

We write about our experiences, ideas and interests in business, software and the business of software. We also sometimes write about our own products (in order to promote them).