How to Build an iCal Calendar with PHP and MySQL

When I wanted to produce an iCalendar feed from my PHP application, I was somewhat surprised at the difficulty I had in finding any basic tutorials on the subject.

Sure, there is the official documentation which I would highly recommended studying if you are serious about building a detailed calendar, but a basic template will probably suffice for most.

The good news is that iCal calendars can be output on a basic LAMP server without any special configuration. It is essentially just plain text.

My iCal is a bunch of appointments retrieved from a database for my clinic management software, and the raw output looks like this:

PRODID:-//Thomas Multimedia//Clinic Time//EN
SUMMARY:Emily Henderson
LOCATION:Bundall Clinic Room 1

The PHP to generate the calendar looks like this (using the CodeIgniter framework):


// the iCal date format. Note the Z on the end indicates a UTC timestamp.
define('DATE_ICAL', 'Ymd\THis\Z');

// max line length is 75 chars. New line is \\n

PRODID:-//Thomas Multimedia//Clinic Time//EN\n";

// loop over events
foreach ($query_appointments->result() as $appointment):
 $output .=
SUMMARY:$appointment->firstname $appointment->surname
STATUS:" . strtoupper($appointment->status) . "
DTSTART:" . date(DATE_ICAL, strtotime($appointment->starts)) . "
DTEND:" . date(DATE_ICAL, strtotime($appointment->ends)) . "
LAST-MODIFIED:" . date(DATE_ICAL, strtotime($appointment->last_update)) . "
LOCATION:$appointment->location_name $appointment->name

// close calendar
$output .= "END:VCALENDAR";

echo $output;


Here is some info on some of the fields:

PRODID:-//Thomas Multimedia//Clinic Mate//EN\n”;
The PRODID are your app / company details in the format Business Name//Product Name//Language.

The start tag for an event. You can have as many vevents as you require.

This is the event title.

A unique ID for the event. This is important and required, and allows you to push changes to event details after they have been created. If you are retrieving database rows, the primary key is an ideal candidate for the UID value.

STATUS (optional. default value is CONFIRMED)
The event status is optional and can be one of CONFIRMED, TENTATIVE, CANCELLED. A cancelled event shows with a line through text decoration on iOS.

The event start and end timestamp. This should be formatted as demonstrated using the defined iCal format.If your dates are not already in the UTC timezone, you should convert them to UTC before outputting as this is the expected timezone when the timestamp ends with “Z”. There are options to specify a timezone which are beyond the scope of this tutorial.

LAST-MODIFIED (optional)
If your event has been modified you might like to set the last modified date in the same format as DTSTART above.

LOCATION (optional)
The title of the event location. There is also a GEO field where you can specify LAT/LONG coordinates, as well as a proposed VVENUE field to contain additional location data, however at least on the iPhone / iPad these tags are unused by the OS. Further reading here. I would expect better map integration in the near future for these fields.

It is worth noting that if you enter an address or phone number into the DESCRIPTION field (which I haven’t documented) in plain text, iOS will auto-link the text and will jump to the Maps app or Phone app with the details pre-filled.

The close tag for an event. Remember, you can have as many vevents as you require.

Once your web application is generating calendar data, its time to test!

I did my testing on my iPad. To add a calendar URL, go to: Settings > Mail, Contacts, Calendars, Add Account… Select Other > Add Subscribed Calendar, and then enter your URL in the Server field. Your calendar does not have to have the .ics extension. Then in the Calendar app, you can open the Calendars option and select only your new calendar for testing purposes. The refresh button is your friend! The Apple Calendar updates on its own schedule, which I think is somewhere around every 10-15 minutes.

The major thing to watch out for is formatting errors. iOS devices flatly refused all calendar data if there were any issues with new lines or anything else in contrast to the iCal specifications.

Questions? Comments? Ask below.

CodeIgniter – One Year On

It’s been about a year since I selected CodeIgniter as my framework of choice, and 3 or 4 projects later I can say with sincerity that I have never looked back.

With the benefit of hindsight I want to go over some of the reasons why moving to a framework (or perhaps a better framework) is a great choice for any programmer once they have a grasp on the fundamentals of PHP.

Proper MVC gives method to mayhem

I used to think I got MVC architecture, but it wasn’t until I delved into CI that I started to really understand how its supposed to look and feel, and why the correct structure is so beneficial to an application.

The way that CI is predictably structured means that debugging is almost fun. There is such a distinct pattern of organisation that finding a gremlin is as easy as looking at the URL and going directly to the responsible controller and method. This carries across projects and across developers.

Gone are the days of following breadcrumbs off to some other obscure part of the application (they don’t really exist anymore) – at worst you would find yourself in a library or helper file which is easily tracked down to a predictable location from the controller level (and generally speaking very well documented).

[On the topic of debugging I will give a curteous nod to CI’s error handling capabilities when in development mode as well.]

Reasonable code organisation is imposed on you to an extent that you probably are in the wrong industry if you manage to make a mess of things.

Not to mention models. Sure, I still have some confusion about how to deal with multiple table joins in an elegant way given the supposed “one model per table” rule, but at least now my models are truly simple and truly re-usable without a hint of business logic in sight.

DIY frameworks are for rookies and exceptional cases – probably not so much for you

I can’t think of many instances where you should create your own framework. Would you try and write a jQuery killer? Sure, its great as an educational exercise, but the online world really doesn’t need another javascript (or PHP) framework, especially when it is a problem that has been elegantly solved many times over in numerous offerings that are used today by thousands of developers.

Don’t kid yourself into thinking that your method is more understandable to an outside person compared to any well established and widely used framework – and save a thought for the next developer who inherits your code!

Ignorance is bliss

Back then, I said:

I was surprised to discover the folder full of procedural helper functions. This is nice in terms of using functions anywhere inside the application, although it does seem a little out of place in an OOP framework. It would be nice to see these converted into classes.

Actually, the helpers are really awesome, extremely useful and they should not be object orientated.

I find particular use in writing helper functions like now_utc(), convert_date(), user_id(), logged_in() and minutes_dropdown(). They are the kinds of things that you use all over your controllers and worrying about object context is an unnecessary barrier to accessing basic information really quickly.

Q: What does this thing do?

A: Find it in the user guide. Also known as Read The Fucking Manual! It is your new BFF.

The documentation is where the answer to your question is at. If it’s part of CI, it’s in the manual, and in something of a rarity this user guide will answer 99% of your questions efficiently.

The libraries and helpers are really really (really) useful

The best thing I can say about the out-of-the-box helpers, config files and libraries in CI is that they do just enough. Over the course of building a handful of applications you will almost certainly call on the majority of the tools in your formidable tool belt, but by the same token you don’t feel overwhelmed at all by the size of the framework’s footprint, or that it does a lot of stuff that you don’t need.

CI elegantly wraps around some of those everyday pain-in-the-ass PHP functions like database, file uploading, image resizing, email, session, cart and caching.

If only to satisfy all the experienced programmers who have pulled out hair over these native PHP features for many-a-year, you should learn how to do these things natively in PHP – and then you can truly admire what this framework has done with them.

3rd party libraries and helpers + community support FTW

Extending CI is so easy, and it won’t break your code. If it does you just peel off the new layer. It’s like building with Lego. That is why alot of people do contribute, and why alot of excellent libraries and helpers exist to save you time and headaches.

I have really barely scratched the surface in this regard, but I have found a couple of libraries to be instant hits in my applications:

Message is a nice little library that I used as the basis of a global user notification system. In my case this means showing messages to the user spanning success, warning, error and notice notifications. It makes use of session flashdata to output the message once to the user before trashing it.

With Carabiner you decide at the controller level which javascript and CSS assets you would like to load in your view and carabiner does the rest.

It has some nice pre-bundled “groups” which you can load from the Google CDN such as jQuery UI, and you can of course write your own groups if necessary. For some reason you can’t cleanly set a group to load at the controller level (as opposed to loading individual files which is trivial), but no doubt simply passing an array or some such thing to the view would solve this problem.

Now for the best bit – Carabiner can switch between development mode and production mode. In development mode files are included as is using the original file path; in production mode all CSS and javascript files are minified and concatenated into single files which are then cached to a web accessible directory on the server.

Not only is this great in terms of page download times and saved bandwidth, but is very useful for overcoming pesky browser caching, in that the concatenated file will have a new random file name every time it is changed.

In regards to community support, Stack Overflow and CodeIgniters very own forum are both good avenues to chase up the hard to solve problems.

What are the drawbacks?

To be fair to the CodeIgniter community I don’t think I am expert enough in the complete framework that I can make criticisms on aspects of it.

Instead I will list some things that are problems on my horizon that, to my knowledge, do not have easy fixes based on my research so far.

Multiple websites with a shared application on a single server

As far as I can tell, it’s quite possible to run multiple websites using a shared application folder with each site having its own front controller, but having unique configurations and different databases for each site might be another matter. The standard database config is designed to specify a single database to be used at a time. What then happens when each site uses its own database and has some distinct configuration options?

Not to mention how might you extend one site controller without affecting others?

It sounds to me like I need to specify which config(s) to load in the front controller, and or use some kind of hook system, without making a mess of things.

Easy user authentication and permissions

In its defence it has been argued that CI lets you determine user permissions and authentication based on your own individual requirements, however it would be nice if at least a 3rd party library really shone in this area, such that user authentication and user permissions could be configured in a consistent way.

There you have it – my one year summary of my CodeIgniter journey. For a relative framework n00b I think my training wheels are definitely off, and my efficiency continues to grow by the month as my experience and back catalogue of  code grows.

So if your not using a framework yet, in the immortal words of the Colonel in Stanley Kubrick’s Full Metal Jacket:

Well how about getting with the program? Why don't you jump on the team and c'mon in for the big win?

How to Run Cron Jobs in CodeIgniter

After a fair bit of trawling Google, I got the impression that setting up a cron job in codeigniter was going to be a bit tricky.

The two potential solutions I discovered were:

  1. execute the “wget” command to replicate a normal browser request, which then exposes my cron functionality to the big wide world and creates some additional unnecessary overheads, or
  2. Create a bespoke front controller a bit like the existing index.php but specifically for cron jobs

Neither of these options sound particularly ideal to me, and thankfully, there is a better way (and it turned out to be a case of RTFM).

Firstly, just create a normal controller. I creatively called mine Cron.

class Cron extends CI_Controller {

    function __construct()

        // this controller can only be called from the command line
        if (!$this->input->is_cli_request()) show_error('Direct access is not allowed');

    function foo($bar = 'bar')
        echo "foo = $bar";

Note the call to $this->input->is_cli_request() which ensures that this controller cannot be accessed directly from a url.

Next we setup our cron job as you would any other, except that you provide the path in a special format.

php /path/to/index.php controller_name method_name [“params”]

The first part is either the command “php” or your server path to php.

The second part is the absolute path to your CI front controller, eg. /path/to/index.php.

The third part is the controller name, followed by the method name, followed by optional parameters.

The CI manual gives the example:

php /path/to/index.php cron foo

If that doesn’t work, it probably means you don’t have the package php5-cli installed. On debian / ubuntu you can install this package as follows:

sudo apt-get install php5-cli

If you are not able to install packages, you can specify your path to php. I set mine as follows:

/usr/local/bin/php -f /home/clinic/public_html/index.php cron foo

You can even pass parameters in quotes, which have the same effect as parameters in regular url requests!

/usr/local/bin/php -f /home/clinic/public_html/index.php cron foo “beer”