This post was originally written several years ago and included my own attempt at building a very simple ical creator in PHP. I’ve been meaning to update this blog with some better code and information for quite some time. I finally found what I consider to be a very good class for creating iCalendar files using PHP: iCalcreator. Along with creating iCalendar files, iCalcreator can also parse and merge iCalendar files, and spit out a modified version. There are lots of examples available for download on their site, as well as supporting utilities.
The iCalcreator documentation is excellent. The examples are easy to follow, and they clearly document how to use their class. Unfortunately, the iCalendar specification is complicated enough that you’ll still need to read rfc2445 to really take advantage of all of the options available to you. Some good iCalendar reference sites include:
Since I haven’t actually built anything using iCalcreator yet (other than some quick test scripts earlier today) I can’t really answer any questions about its usage. The good news is they have a forum on sourceforge for questions. I’d love to hear about your experiences using iCalcreator, so feel free to leave your comments below.
One benefit to working as part of a development team is that you can ask your fellow developers to look at your code and ask for help. Sending code back and forth over email can be frustrating and even counter-productive. IMO, the ideal way to share code is by simply sending a URL and letting the other developer view it in their browser with syntax highlighting. Here’s one way that does not require any external libraries and is very easy to setup:
Add this line to .htaccess
RewriteRule ^(.*)\.phps$ viewsource.php?file=$1.php [QSA,L]
This rule sends all requests for files ending in .phps to the viewsource.php file with the path as an argument.
Put the below script in the document root of your development server
<?php
// This script should *NEVER* be used on a production server!
// By default, it will only run on localhost. If you want to run this script on
// a staging server add the IP address of that server to $allowed_servers.
$allowed_servers = array('127.0.0.1');
// Set $base_dir to the root of your web directory.
$base_dir = $_SERVER['DOCUMENT_ROOT'];
// Make sure this script is running on an allowed server.
if(!in_array($_SERVER['SERVER_ADDR'], $allowed_servers)){
die('Do not run this script in production!');
}
// Exit if no file was specified
if(empty($_GET['file'])){
die('No file specified.');
}
// Ensure the requested file is inside $base_dir and that it exists
$real_path = realpath($base_dir.$_GET['file']);
if(strpos($real_path, $base_dir) === false || $real_path === false){
die('Access denied or file not found.');
}
?>
<html>
<head>
<title><?= $real_path ?></title>
<style type="text/css">
.num_margin {
text-align: right;
margin-right: 6pt;
padding-right: 6pt;
border-right: 1px solid gray;
}
.num_margin a {
color: gray;
font-size: 13px;
font-family: monospace;
}
body { margin: 0px; margin-left: 5px; }
div { float: left; }
</style>
</head>
<body>
<?php
$lines = '';
$lines_array = range(1, count(file($real_path)));
foreach($lines_array as $line){
$lines .="<a href=\"#$line\" name=\"$line\">$line</a><br />";
}
$content = highlight_file($real_path, true);
echo "<div><div class=\"num_margin\">$lines</div><div>$content</div></div>";
?>
</body>
</html>
Now when you visit a link like: http://localhost/info.phps it will show the source of the file.
Note: This script should never be used in production!
Note: If you’re using Leopard, the directions below are for you. If you’re using Snow Leopard, just follow the directions provided at xdebug.org.
Most of the blog posts I’ve seen about getting Xdebug working on OS X insist that you must use MAMP or XAMPP to do so. But since PHP and Apache come included with OS X, it seems like overkill to use one of those packages.
Contrary to popular belief, it is possible to get Xdebug working with the OS X standard Apache/PHP installation. Here’s how I did it:
- Download Xdebug source
- Extract the xdebug tarball
- cd into the xdebug directory
- Run: phpize
- Run: MACOSX_DEPLOYMENT_TARGET=10.5 CFLAGS=”-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp” CCFLAGS=”-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os -pipe” CXXFLAGS=”-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os -pipe” LDFLAGS=”-arch ppc -arch ppc64 -arch i386 -arch x86_64 -bind_at_load” ./configure –enable-xdebug
- Run: make
- cp modules/xdebug.so /usr/lib/php/extensions/no-debug-non-zts-20060613 (note: your path may be slightly different)
- add the following line to php.ini: zend_extension=”/usr/lib/php/extensions/no-debug-non-zts-20060613/xdebug.so”
- Run: sudo apachectl restart
- Write a PHP page that calls “phpinfo();” Load it in a browser and look for the info on the xdebug module. If you see it, you have been successful!
This is simply a modified version of the README file that comes with the xdebug source. The most important tweak is step 5, which sets the correct flags for the resulting module to work with Apache as it is compiled in OS X.
Note: If you’re currently using another debugger, you will need to disable it before Xdebug will work.
In my last post I mentioned geocoding using the Yahoo Geocoding API, so I thought I’d post some code to do exactly that.
For the purposes of this example, assume that we have a very simple MySQL database table with this structure:
CREATE TABLE `addresses` (
`id` int(11) NOT NULL auto_increment,
`street` varchar(255) default NULL,
`city` varchar(255) default NULL,
`state` varchar(2) default NULL,
`zip` varchar(9) default NULL,
`lat` float(10,6) default NULL,
`lon` float(10,6) default NULL,
PRIMARY KEY (`id`)
)
Here’s the script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| <?php
// Geocode addresses using the Yahoo Geocoding API
// Turning on track_errors stores the latest PHP error in $php_errormsg.
// This allows for more elegant display of the error messages without having to
// code a php error handler, which would be overkill for such a simple script.
ini_set('track_errors',TRUE);
mysql_connect('localhost', 'user', 'password');
mysql_select_db('database_name');
// Setup variables to be used later
$geocode_url = "http://local.yahooapis.com/MapsService/V1/geocode";
// Enter your custom appid here
$appid ='sample_appid';
// Get the addresses that have not been geocoded
$address_result = mysql_query("SELECT id, street, city, state, zip FROM addresses WHERE lat IS NULL OR lon IS NULL");
// If the MySQL query returned any rows, loop through them
if(mysql_num_rows($address_result) > 0){
while ($row = mysql_fetch_assoc($address_result)){
// appid is required for each call to the yahoo geocode API
$row['appid'] = $appid;
// output can be either xml or php
$row['output'] = 'php';
// Store the row id for updating the database and remove from $row array
$id = $row['id'];
unset($row['id']);
// Build the query string for sending the request to yahoo
$query_string = http_build_query($row, '', '&');
// Get latitute / longitude
$response = @file_get_contents($geocode_url.'?'.$query_string);
if($response == false){
// If the call to the yahoo api failed, display an error message
echo 'ERROR: '.$php_errormsg;
}
else{
// Get the results of the api call and update the database records
$response_array = unserialize($response);
$lat = mysql_real_escape_string($response_array['ResultSet']['Result']['Latitude']);
$lon = mysql_real_escape_string($response_array['ResultSet']['Result']['Longitude']);
$update_result = mysql_query("UPDATE addresses SET lat = '$lat', lon = $lon WHERE id = $id");
// For each record, let the user know if the update was successful. If not, display the mysql error message generated.
if($update_result == TRUE){
echo "Added lattitude and longitude for {$row['street']} {$row['city']}, {$row['state']} {$row['zip']}.";
}
else{
echo "Unable to add lattitude and longitude for {$row['street']} {$row['city']}, {$row['state']} {$row['zip']}
MySQL Error: ".mysql_error();
}
}
echo '<br />';
}
}
else{
echo "Nothing to do.";
}
?> |
The code is documented pretty well so I’ll just point out a couple things
- The Yahoo Geocoding API can return either XML or PHP serialized objects. I chose PHP serialized objects because it’s easier to deal with for what I’m trying to do.
- http_build_query does not exist in PHP4. To use this code with PHP4 you could use the PHP_COMPAT Pear Package or the http_build_query function from the CodeIgniter Compatibility Helper.
The other day I was using http_build_query to generate URLs to use with the Yahoo Geocoding API and kept getting back a “400 Bad Request” error back from Yahoo. The URLs were being echo’ed and looked fine, I could copy the URL and paste it in the browser address bar and get the results I was looking for.
When I finally viewed the source I saw that the urls were being created with & as the separator for the variables in the query string instead of &. That explained why the url looked fine when it wsa echo’ed but didn’t work when executed.
This is where arg_separator.output comes in. This setting in php.ini determines what character(s) are used when generating URLs using the built in PHP functions. So to get my code working, I just needed to change the value to just an &:
ini_set('arg_separator.output','&');
I just wish it hadn’t taken me so long to figure that out!