Hello! My name is Marc Wandschneider. I am one of the lead developers on SWiK and author of Core Web Application Programming with PHP and MySQL, published by Prentice-Hall.
My technical/personal blog is at www.chipmunkninja.com. My travel blogs are at http://travel.lanfear.com
I have been a programmer for well over 15 years, starting out accidentally after spending waaay too much time on a friend’s computer at University.
My key interests in computing have always been in development tools and user interface design and programming. A high quality rapid application development environment with a powerful language, easy to use environment, and a portable (read: small) and fast runtime is a rare thing indeed. Tools such as Visual Basic v1-3 and Borland’s Delphi tool are a dying breed.
In the user interface arena, I am constantly saddened by the amount of time and energy people put into making their applications gratuitously different and thus frustrating for the end user to figure out. Recent operating systems such as Apple’s OS X have made some improvement in enforcing user interface restrictions, but even these are likely susceptible to the inevitable decline seen in popular consumer operating systems such as Windows.
I have recently spent much of my time working with web applications and am interested in how they, especially using all the new rounded corners in Web 2.0, will affect the way in which traditional web applications are developed. I balance my efforts between design, performance, scalability, and maintainability.
Some of the major things I have done in my professional career include:
I now divide my time between learning spoken languages and spending far too much time in front of my Powerbook. I currenty live in Beijing, China.
This year, as part of my annual trip to Canada and the USA, I’ve been asked to give two talks at the annual PHP Québec conference in Montréal. I haven’t been back to that city since 1993 when I graduated from University, and it will be interesting to see how it goes. (Although I suspect that while Beijing basks in nearly 20C (nearly 70F) weather every day and even Seattle and New York were closer to 10C (50F), Montréal is still hanging below freezing most days and has over a metre of snow on the ground).
I will be giving talks on internationalisation (commonly just called i18n) and giving your database servers a break with memcached. If you’re anywhere in the neighbourhood, come on by for some good fun. I’ll be getting back to regular programming content this weekend.
The slides for my presentations are here:
[Sorry for the delay in fixing the memcached link – I have had a severe flu for the last few days. It should be okay now.]

This is a bit of a geeky post, but then this is a geeky computer blog, so … so be it. Regular readers of this blog will know that I’m a fan of Nethack, and have been playing it on and off for over fifteen years (mostly off, but recently I’ve rediscovered it again). Well, finally, after all this time, I can say the following:
Goodbye marcw the Demigod...
You went to your reward with 5941448 points,
The Book of the Dead (worth 10000 zorkmids and 25000 points)
Vorpal Blade (worth 4000 zorkmids and 10000 points)
The Heart of Ahriman (worth 2500 zorkmids and 6250 points)
The Bell of Opening (worth 5000 zorkmids and 12500 points)
The Candelabrum of Invocation (worth 5000 zorkmids and 12500 points)
8 emeralds (worth 20000 zorkmids),
2 diamonds (worth 8000 zorkmids),
2 rubies (worth 7000 zorkmids),
1 amulet of ESP (worth 150 zorkmids),
1 amulet of unchanging (worth 150 zorkmids),
and 2497 pieces of gold, after 93897 moves.
You were level 22 with a maximum of 95 hit points when you ascended.
No Points Name Hp [max]
1 5941448 marcw-Bar-Hum-Mal-Neu ascended to demigod-hood. 95 [95]
2 2623722 marcw-Val-Hum-Fem-Neu died on the Plane of Fire.
Dissolved in molten lava (with the Amulet). 129 [287]
3 1330849 marcw-Val-Hum-Fem-Neu choked on her food in Gehennom
on level 33. Choked on a disenchanter corpse. 248 [248]
Interestingly, I almost always play Valkyries, but decided to try Barbarians for a couple of games. The first game, I made it all the way down to level 24 without finding a single altar (except for a non-aligned one in the mines with a nasty priest next to it) before an Arch-Lich and Titan finished me off. The second game was this one. Most of the rest of the time, I do embarrassing things like choke on things or eat something I shouldn’t have. Need to be more careful, I suppose.
Well, that’s all there is to this post, but it’s exciting news for me. I’m still playing, and now trying other classes (read: dying a lot).
For the last 17 years or so, I’ve been a huge fan of the various BSD-inspired operating systems, starting with SunOS 4.1.x, and then moving on towards the various free flavours available for the PC, such as Bill Jolitz’s 386bsd, then FreeBSD, NetBSD, and OpenBSD. For a while, even I was a regular contributer to the NetBSD community, and enjoyed playing with them all.
When I started installing and running my own servers for mail and web application purposes about 8 years ago, there was an abortive few-month attempt to use Microsoft Windows Server, but since then it’s all been FreeBSD, with the latest lanfear.com server being FreeBSD 4.9-RELEASE (and with an uptime of 2 years, which would have been nearly 3 had my ISP not hacked and rebooted my machine one day).
To this date, various SYSV-inspired features, such as initd and their directory structure leave me with a bad taste in my mouth. I have repeatedly stuck with types of linux such as SuSE (before it sucked), and Ubuntu, that still gave me /etc/rc and familiar directory structures. Mac OS X still gives me warm tingly feelings to this day.
So, it is with some sadness that I recently decided to move from my own dedicated server to a virtual server hosting solution. I’m simply never in the USA any more, and I don’t want to have to worry about my computer going down. A virtual server comes with a guarantee that all hardware problems are the ISPs, and is a bit cheaper to boot. I usually hover around a 0.00 load average, so serious computing power isn’t a necessity for me.
However, the cheapest package with the best bandwidth means my server will, henceforth, be running Ubuntu Server. It is reasonably familiar to me … I can still add things to /etc/rc.local, and the rest of /etc isn’t too alien, and the apt-get scheme seems to work reasonably well. My needs are less these days, as I slowly admit defeat in the email world and let people like Google do it for me, so as long as I can run web apps and a few other fun things, I’m happy. All of my sites and addresses have already been moved to the new server.
The old FreeBSD 1U Dell server will be shut down by the old ISP on Thursday, and put in a box for a friend to go pick up sometime after that. I’ll miss it.

I recently spent two weeks converting JustLooking, my Mac OS X Image Viewing program, from NSImage to CoreImage and friends. This experience was overall much easier than I expected, and I have learned a bunch of things, some of which might have been handy to have known in advance.
The good news is that it mostly lives up to the hype. The bad news is that it’s not without tricks and traps of its own. Here are some notes and comments.
drawRect: when you first display it or tell it to change its contents. Thus, it seems as though a lot of people (myself included) have developed code that sets up a single-fire timer for some short period of time (0.1 seconds or so) to cause a redraw, which is respected.[[NSColor colorWithDeviceRed: 0 green: 0 blue: 0 alpha: 0.7] set]. In some situations, overriding - (BOOL)isOpaque might be required so you can return NO. Doing this in the NSOpenGLView, however, is a monumental effort, and something I have to figure out. I found a few examples on the Internet, but was never able to make one work. (If you have done this before, please please please send me mail or add a comment).imageWithContentsOfURL: is extremely useful for quickly loading in images, but won’t let you get at things like EXIF or other image meta-data. You’re far better off using CGImageSourceRef APIs to load in CGImages, and then pass these to the CIImage method imageWithCGImage:. These will even let you load in all the frames in an animated GIF (something which NSImage simply does not).In summary, I’m extremely glad I took the CoreImage leap. The system is extremely cool, and has pushed NSImage to a minor utility role in my programming world. If you do decide to go for it, take heart—it’s really not so bad, and internet searches will give you all the help you could want.

There are lots of ways in which a website can be annoying. Favourite methods include: rotating and blinking animated GIFs (or worse, Flash), popup advertising windows, unexpected background music files, or just plain all around atrociously ugly page design. (I’ve been quite guilty of this in the past!)
But until you’ve lived in China, or at least spent some time browsing around websites here on the mainland, there’s probably one way to annoy the living bejeezus out of people that you’ve never thought of.
To demonstrate, simply visit any Chinese website, such as the Bank of China or something else such as Chinaren. Don’t worry if you can’t see the characters, they’re not important for this experiment. (Windows XP users can add them by going to Control Panel /International and installing the Asian Font Pack, while Vista and Mac users will have all these fonts installed already).
Once you have one of these pages up in your browser window, click on a link or two. Click on some more links on those pages. Try to get back to where you came from. Within minutes, you’ll have at least a dozen browser windows littering your desktop, or at best, for those Firefox users with the correct settings, dozens of tabs.
You could be forgiven for thinking that this was specific to a few sites with particularly bad design. And you’d be totally wrong. This is completely endemic here in local website design, and is how the locals think that the “Internets” should work. Indeed, there is almost no concept of forward or back button usage any more, and it is not uncommon to see users with well over twenty browser windows littering their desktop at any given time. While Windows users can at least expect the Task Bar to group similar windows, Mac users just end up using the mouse to move the windows out of the way until needed later, or until they just close the browser application completely.
Ultimately, the problem becomes such that, if you want to fix the site design to not do things this way, you will confuse your user. When they click to go to a new page, and they then subsequently finish visiting it, they will close the browser window and proceed to go looking through their other browser windows until they find the one from whence (they hope) they came.
The only thing I can say? At least blatent ripoffs of other sites on the internet don’t seem to have felt compelled to introduce this behaviour into their clones. For everybody else, it’s going to take a while to change this design.
Imagine, if you will, the following scenario:
What you might not have noticed, especially if you – like me – have a few thousands rows of data, is that MySQL might have screwed you along the way and not really told you all that clearly.
After a few days of operation on my live server, I started to notice a few weird things—foreign keys weren’t being enforced properly, and there were some values in the database that probably shouldn’t have been possible. I furrowed my brows and put it on my list of stuff to investigate.
Well, yesterday, I added a new table to the database, and it went something like this:
mysql> CREATE TABLE Fudgecicles
>(
> id INTEGER AUTO_INCREMENT PRIMARY KEY,
> value VARCHAR(255) NOT NULL,
>)
>ENGINE = InnoDB;
Query OK, 0 rows affected, 1 warning (0.10 sec)
Where did that warning come from?
mysql> SHOW WARNINGS;
It is here that MySQL tells me:
+---------+------+-----------------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------------+
| Warning | 1266 | Using storage engine MyISAM for table 'Fudgecicles' |
+---------+------+-----------------------------------------------------+
Augh! No! Bad! Bad, MySQL, Bad! Why on earth would it do that? I didn’t misspell InnoDB or even use “incorrect” casing in the name. There’s nothing wrong with the schema I specified and I’ve done this hundreds of times before.
Well, after some research, I then tried the following:
mysql> SHOW ENGINES;
And MySQL helpfully gave me the following:
+------------+---------+----------------------------------------------------------------+
| Engine | Support | Comment |
+------------+---------+----------------------------------------------------------------+
| MyISAM | DEFAULT | Default engine as of MySQL 3.23 with great performance |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables |
| InnoDB | DISABLED| Supports transactions, row-level locking, and foreign keys |
| BerkeleyDB | NO | Supports transactions and page-level locking |
| BLACKHOLE | NO | /dev/null storage engine (anything you write to it disappears) |
| EXAMPLE | NO | Example storage engine |
| ARCHIVE | YES | Archive storage engine |
| CSV | NO | CSV storage engine |
| ndbcluster | NO | Clustered, fault-tolerant, memory-based tables |
| FEDERATED | NO | Federated MySQL storage engine |
| MRG_MYISAM | YES | Collection of identical MyISAM tables |
| ISAM | NO | Obsolete storage engine |
+------------+---------+----------------------------------------------------------------+
12 rows in set (0.00 sec)
The InnoDB database engine had been disabled somewhere along the way and I hadn’t even noticed. It is enabled, by default, on the standard Linux and Mac OS X MySQL binaries that I’ve been downloading. So something changed along the way that made this all stop.
Far worse, I then began to worry about my existing tables, all of which we supposed to be InnoDB. Upon executing the following for each of them:
mysql> SHOW CREATE TABLE Fudgecicles;
I would see something like:
| Table | Create Table
| Fudgecicles | CREATE TABLE `Fudgecicles` (
`id` int(11) NOT NULL auto_increment,
`value` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
Sure enough, every single one of my tables was MyISAM, and not the InnoDB it was supposed to be.
The problem turns out to be that InnoDB is somewhat finicky about my.cnf settings, and will frequently refuse to operate if settings change in this file in such a way that makes any existing data incompatible with the way it would write new data.
In my case, it turns out that changing the settings for the InnoDB binary data and log file sizes were somehow incompatible with the existing binary data and log files (ibdata1 and ib_logfileX). Upon starting the server, InnoDB finds this inconsistent state and simply refuses to start up. This is the first problem.
The second, and far more serious problem, is that MySQL just switches the database persistence engine on you and only provides a little warning. If you’re loading in thousands – if not tens of thousands – of rows, those warnings are easily lost in the scroll-a-thon that ensues. This is bad behaviour. MySQL should simply refuse to create your table if your selected persistence engine is not available.
Fixing this problem involved three parts. The first, and easiest, is to get the InnoDB engine back. You have two choices:
I chose the second method.
| WARNING: Using this second method can result in data-loss if you’re not careful. Some database engines store things in the binary data and log files before writing them to the actual table files, and if they’re deleted, you might lose those changes. I only selected this path because all my tables were MyISAM, I had a full backup, and spent a good 10 minutes after deleting the files verifying that all data were correctly restored. |
You then stop and restart the MySQL database engine, and InnoDB will be back. You can verify that all is well in InnoDB land by executing:
mysql> SHOW InnoDB STATUS;
And you will receive a gloriously long and detailed set of information on how things are going.
Part two of the process involved converting my tables back to InnoDB.
mysql> ALTER TABLE FishSticks ENGINE = InnoDB;
This proves to be tedious and time consuming, as you cannot simply go from table A to table Z doing the conversion – because of the foreign keys and references, they have to be done in the right order. When one of the ALTER TABLE statements fails, you can find out what happened by re-executing the SHOW InnoDB STATUS command – it will tell you why it wouldn’t convert the table to InnoDB.
But, eventually, I got them all done. It was then that I noticed that none of the foreign keys were set up properly any more.
So, the last step of the process is to re-establish the FOREIGN KEYs. I did this by, for each foreign key in each table I had, executing the following commands:
mysql> ALTER TABLE FishSticks DROP KEY [dead foreign key name];
mysql> ALTER TABLE FishSticks ADD FOREIGN KEY (keyname) REFERENCES Table (fieldname);
The good news is that saving my database and getting back to all sorts of InnoDB goodness only took about an hour in total, for about 30 tables or so.
However, if MySQL had simply reported an error a few days earlier instead of blithely just switching tables types behind the scenes, I might have avoided this whole mess in the first place. Oh well, at least I learned a few neat little commands I can play around with now! Lesson learned!
Here’s to hoping that this article helps some other folks solve the same problem a bit quicker!
I recently had the oppportunity (necessity) to set up a web application that interacted with many of the users through SMS messages in addition to the more traditional HTML interface. While there are a number of possible software solutions for GSM modems on Windows, on Unix-like platforms the most commonly used one is Kannel. It also has the advantage of being open source and thus very, very free.
However, setting, configuring, and using Kannel tends to be a bit tricky. I’m writing this article (almost a HOWTO) in an attempt to help out anybody who’s undertaking the process themselves and might be able to get some tips and tricks from this. I expect this to not be a terribly popular article, but if I ever need to set this stuff up myself in the future, then I’ll have it written down somewhere at least!
Most of the instructions here will work on any Unix platform, such as Linux, FreeBSD, or Mac OS X. It’s worth noting that I got nearly everything working with OS X, only to be thwarted at the very end because Mac’s no longer have serial ports to use GSM modems. You could, however, easily use some of the more advaned HTTP based SMS services on a Mac server of some sort.
The web site written was a discounting service oriented around cell phones. Users would send the site a message, and then receive a discount at local venues. They could go to the site on the intarwebs to see their total savings and learn about more venues.
The usage pattern was always:
Kannel is trivially easy to download and compile. You visit the Kannel.org website and download the latest and greatest gateway-1.X.Y.tar.gz file.
From there:
# mkdir src
# cd src
# tar xfz ../downloads/gateway-1.4.1.tar.gz
# cd gateway-1.4.1
# configure --prefix=/usr/local/kannel
I chose to install to /usr/local/kannel just because I’m that kind of guy who likes to keep everything reasonably separated and organised. You’re free to put it anywhere.
Compile and install.
# make
# sudo make install
password: **************
You can be forgiven for thinking Kannel is trivially easy thus far—it really is that easy to download, compile, and install. Unfortunately, here is where things get tricky.
You now need to set up a configuration file. This file has a zillion options to support all of the possible and powerful ways in which Kannel can be used. I will be showing strictly how I set it up for the GSM modem we had in the office (It’s a Siemens GSM modem connected to a serial port, and works quite well).
The basic smskannel.conf (in the gw/ directory) has much of the information we want, but we’ll need to add a few things for our GSM modems and to interact with our web server correctly.
Configuration is divided into a few key groups, each representing the key parts of the kannel system, including the server that handles sending and receving the actual SMSes (bearerbox) and the system that handles the final dispatching to your scripts (smsbox).
The first part of the file is the “core” group, and the default is pretty close to what we want:
group = core
admin-port = 13000
smsbox-port = 13001
admin-password = bar
#log-file = "/tmp/kannel.log"
#log-level = 0
box-deny-ip = "*.*.*.*"
box-allow-ip = "127.0.0.1"
You’ll want to change the password of course, but everything else is nearly standard. We are assuming that all communication to the kannel server will come from the same physical computer (127.0.0.1). You can set a log file if you are going to be running kannel as a service on your server, or you can just redirect stdout to some file.
Be aware that kannel has various log levels, ranging from 0, which displays information that is only of interest when you’re in the development and debugging phases, to 4, which only displays critical errors and problems. I tend to develop at level 0 and run live servers at level 1. Disk space is cheap.
Kannel supports a pretty insane number of ways of sending and receiving SMSes, ranging from SMS services over HTTP, to a fake SMS centre for testing/development purposes, to GSM modems, which is what I have used and is the smsc module. These modems use AT-style modem commands and typically hook up over the serial port. To get this going, I set up the smsc group in the smskannel.conf file:
group = smsc
smsc = at
modemtype = auto
device=/dev/ttyS0
my-number = 123123123123
connect-allow-ip = 127.0.0.1
log-level = 0
The my-number field contains the number of your GSM modem’s SIM chip. Again, I only allow connections from my local server, and the Ubuntu Linux serial port is on /dev/ttyS0.
The smsbox group helps configure the part of the system that dispatches SMSes received by the core SMS or receives SMSes before they’re sent out. I honestly don’t fully understand what this group really does, but it’s necessary, and pretty trivial to set up.
group = smsbox
bearerbox-host = 127.0.0.1
sendsms-port = 13013
global-sender = 123123123123
log-level = 0
The global-sender field is the outgoing-number of your GSM modem, which for me is the same as the my-number field above.
This group is what allows your web applications to send SMS messages using Kannel. They do this via simple HTTP requests, and configuration here basically requires a user name and password:
group = sendsms-user
username = kanneluser
password = df89asj89I23hvcxSDasdf3298jvkjc839
concatenation= true
max-messages = 10
Since the password is semi-plain and unprotected here, I tend to use one that is complicated and nearly impossible to remember, but quite different from any other passwords that I actualy use for login accounts and the like.
The sms-service group configures how Kannel gets messages to your web application. You are allowed to specify a number of these groups, each of which can “catch” incoming messages based on various criteria. My application had all messages go to one processing script, so I just set up one group that caught all incoming messages.
group = sms-service
keyword =
keyword-regex = .*
catch-all = yes
max-messages = 0
get-url = "http://localhost/sms?phone=%p&text=%a"
This particular configuration has Kannel set up to use an HTTP GET request to send the message to my application. The param phone contains the phone number of the sender and the text parameter contains their entire message.
NOTE: The max-messages value was particularly tricky and critical for me: When I first set up Kannel and tested sending messages, I would always get back '<Empty reply from service provider>'. Setting max-messages to 0 tells Kannel to never send a reply directly from the incoming message (you can, of course, initiate your own response later, of course).
Kannel and smsc tends to be pretty good at figuring out everything about your modem by yourself, but you can help them out by including modems.conf in your smskannel.conf file as I did:
include = "/usr/local/kannel/modems.conf"
The hard part is done; all we have to do now is copy over the config files and start the service up:
# cd /usr/local/kannel
# cp ~/src/gateway-1.4.1/smskannel.conf .
# cp ~/src/gateway-1.4.1/gw/modems.conf .
# sbin/bearerbox -v 0 smskannel.conf &
# sbin/smsbox -v 0 smskannel.conf &
I tend to run the last two commands in two separate shell windows when developing/debugging so that I can see the output from the two programs clearly and use the information to help me figure out what’s going on (level 0 really tells you a lot).
Kannel will simply call the URL you told it to in the sms-service group and you can process this with whatever HTTP server environment you want. We’re using LAMP right now, but, again, any will do. The incoming phone number and message are in GET parameters. You can, if you want, configure the sms-service to send them as POST messages as well.
The final part our puzzle is to send outgoing SMS messages through Kannel, and has only one little twist. It is also done via an HTTP interface. It requires you to be a little careful about the character set you use. I found I had the most success by using the UCS-2 character set. In PHP5, you can easily use the iconv function to do this for you.
Since I send both English and Chinese messages, my PHP scripts and langugage string files are all UTF-8. Here is the code I use to send messages:
function sendSmsMessage($in_phoneNumber, $in_msg)
{
$url = '/cgi-bin/sendsms?username=' . CONFIG_KANNEL_USER_NAME
. '&password=' . CONFIG_KANNEL_PASSWORD
. '&charset=UCS-2&coding=2'
. "&to={$in_phoneNumber}"
. '&text=' . urlencode(iconv('utf-8', 'ucs-2', $in_msg));
$results = file('http://'
. CONFIG_KANNEL_HOST . ':'
. CONFIG_KANNEL_PORT . $url);
}
To make this work, of course, you need to have allow_url_fopen set to On.
That’s pretty much it. This has been a pretty dry article, but it does contain everything you need to get Kannel up and running and operational. The manual actually does contain everything you could possibly want to know, so keep digging in there if you’re stuck. Finally, there are mailing lists at kannel.org which tend to be quite helpful as well.
Good luck!
I’m just beginning a short little contract here in China for a month or so to pay some bills and help a friend out. Part of doing work in the computer industry in China is that 1000$ USD for a computer is an outrageous sum of money. Those of us with Apples are either reckless spendthrifts or simply lucky beyond belief (Mac OS X itself is simply not understood … is that a program that runs on top of Windows?). So, this morning, when one of my coworker’s computer broke, I groaned. Not only was it likely to be a huge hassle, but the likelihood of having spare parts or another working computer were quite remote.
In short, he had no network. The cable lights simply did not come on. We switched cables. We unplugged and re-plugged in everything in the office. We uninstalled and re-installed drivers. We fiddled and diddled with Windows ad nauseum. We stole other departments’ switches. No joy.
Finally, in an act of desperation, I opened the computer up, tapped the network port aluminum housing a few times, looked sagely at at the current state of affordable PC hardware (it is pretty cool looking inside those things), and then tried again …. it works fine now.
What the—???
The other day, I was working on JustLooking, changing the appearance and the like of a couple of dialogs. As I ran the programs and went to show the dialogs (“panels” in the local terminology), nothing would show and I’d get an error in my XCode results window:
"Unknown class 'CustomCombo' in nib file. using 'NSObject' instead."
I spent the next half hour trying to figure out why Interface Builder didn’t know about this class: it was there in the class inspector, and the ui widgets were correctly set up to use that new class, and all the hookups in the UI also seemed correct.
Well, I finally figured out: when I first developed the class, I had some errors in it, but wanted to test a few things out in my program elsewhere. So I had unchecked it in XCode, telling the IDE not to compile and link it.
Thus, when Cocoa tried to load the NIB file, it couldn’t find the definition for the class, and just put in NSObject instead.
Here’s to hoping that this blog entry saves somebody that 30 minutes I spent on that one.
Thanks to Keith from the UK for pointing out something odd in my book that doesn’t seem to work as it did in earlier versions of PHP:
If you have a regular expression (I use the POSIX ones almost exclusively since they’re UTF-8 aware whereas the Perl ones were not when last I inquired), and you want to set a range for the number of matches on a particular expression you can use the syntax:
$expr = '[a-zA-Z]{5,50}'; // matches between 5 (incl) and 50 (incl) letters
Now, the problem is: what if you want to have the number of characters in the range be PHP variables that you can set in a configuration file or some such thing? Your first attempt, and what I used in my book, might be:
$expr = "[a-zA-Z]\{$min,$max}"; // double quotes for var expansion
And you would get a wonderfully annoying error message from the PHP engine:
Parse error: syntax error, unexpected ',', expecting '}' in Filename on line 5
No amount of backslashes will fix this problem. It turns out that the PHP parser consumes { and } characters when performing complex variable expansion, so …. all you have to do is add an extra set around each of the variables you wish to expand. PHP leaves the other two alone:
$expr = "[a-zA-Z]{{$min},{$max}}"; // extra { }s are consumed.
And what you are left with is a wonderfully working regular expression.
In normal times, buying a new computer is a rather fun experience. In addition to the endorphin rush caused by plonking down a huge wad of cash for such a small – but often 好看 (that’s Chinese for good looking) – piece of hardware, you get to take it home and play with it and discover all the new and fun things that are different about the new toy. Even if it’s still running Microsoft Windows, your new vendor has probably come up with some new set of stuff to include with the machine. Or, far more common with me, my last attempt to finally come up with the “ultimate organisation of my hard disk™” was a miserable failure, and I’m excited about trying out something new.
So, imagine my disappointment when I recently upgraded my 15” Powerbook G4 to a shiny new 15” Macbook Pro.
In short, my upgrade experience was:Yes, about an hour later (35GB of iTunes and image data takes a while to transfer from one 5400RPM laptop drive to another), my new machine, appeared exactly the same as the old one. My desktop, my language settings, all my files, SSH keys, svn and source enlistments, and more.
What’s worse, all of the software on the old machine was Universal Binaries, so I didn’t even need to install any new software. It if weren’t for the fact that JustLooking compiled in less than 30 seconds instead of well over 2 minutes, I might not have even noticed right away.
I did also notice after a bit of experimenting that I can finally watch HDTV movies (720p and 1080i) at more than 2 frames a second, and that instead of being completley unable to play Civ IV, I can now play it at 1900×1200 with all features and whizzy 3D goodies turned on. I’ve also noticed that the fabled heat problems of the MBP have not materialised on my machine. After a copule of hours of gaming, the machine is no warmer than the G4.
However, the upgrade itself was a complete and utter disappointment. No temporary disruption in productivity, nor any hours spent trying to figure out how to migrate all of my data over. In short, no excuse to do anything other than just go back to work.
Thanks Apple. Jerks.
I recently started working on an image viewing program for Mac OS X using Cocoa, and one of the features I decided to add was the ability to rotate images in 90° increments. I did some searching on the internet, and found a few things:
Neither of the first two was exactly what I wanted—the first didn’t quite work, while the second was too complicated and, in order to support arbitrary rotation, created an NSImage object that was way too large.
So, after reading the Apple Cocoa Drawing Guide on Using Transforms, I came up with the following code to rotate an NSImage object 90°. Please note that this function ONLY supports a clockwise or counterclockwise 90° rotation. It also returns an object that must be released by the callER. I’m not 100% sure if this is standard Objective-C/Cocoa methodology, as I’m still a bit new to all this stuff.
But, without any further ado, here is the rotation function:
- (NSImage *)rotateIndividualImage: (NSImage *)image clockwise: (BOOL)clockwise
{
NSImage *existingImage = image;
NSSize existingSize;
/**
* Get the size of the original image in its raw bitmap format.
* The bestRepresentationForDevice: nil tells the NSImage to just
* give us the raw image instead of it's wacky DPI-translated version.
*/
existingSize.width = [[existingImage bestRepresentationForDevice: nil] pixelsWide];
existingSize.height = [[existingImage bestRepresentationForDevice: nil] pixelsHigh];
NSSize newSize = NSMakeSize(existingSize.height, existingSize.width);
NSImage *rotatedImage = [[NSImage alloc] initWithSize:newSize];
[rotatedImage lockFocus];
/**
* Apply the following transformations:
*
* - bring the rotation point to the centre of the image instead of
* the default lower, left corner (0,0).
* - rotate it by 90 degrees, either clock or counter clockwise.
* - re-translate the rotated image back down to the lower left corner
* so that it appears in the right place.
*/
NSAffineTransform *rotateTF = [NSAffineTransform transform];
NSPoint centerPoint = NSMakePoint(newSize.width / 2, newSize.height / 2);
[rotateTF translateXBy: centerPoint.x yBy: centerPoint.y];
[rotateTF rotateByDegrees: (clockwise) ? - 90 : 90];
[rotateTF translateXBy: -centerPoint.y yBy: -centerPoint.x];
[rotateTF concat];
/**
* We have to get the image representation to do its drawing directly,
* because otherwise the stupid NSImage DPI thingie bites us in the butt
* again.
*/
NSRect r1 = NSMakeRect(0, 0, newSize.height, newSize.width);
[[existingImage bestRepresentationForDevice: nil] drawInRect: r1];
[rotatedImage unlockFocus];
return rotatedImage;
}
The function works great and correctly rotates the incoming NSImage 90 degrees. However, People working with JPEGs should probably not save these data to disk in place of existing images, as the function works on the decompressed bits and resaving them can lose more image data. To truly solve this problem, you should use a “Lossless JPEG Image Rotation” algorithm.
User:marc: Chipmunk Ninja Technical Articles
cocoa
user:marc
rotation
transform
NSImage
rotate
NSAffineTransform
I am happy to announce the immediate availability of Payjacks, currently at version 0.2.0. Payjacks is a PHP/Ajax web application framework I’ve written using the object-oriented features in PHP5+.
Payjacks can be downloaded here:
http://chipmunkninja.com/download/payjacks-0.2.0.tar.gz
Payjacks is an object oriented PHP-Ajax web application framework I’ve written to help write robust and organised web applications. It was designed to require a minimal amount of effort to get your own web application up and running, while helping with such tasks as accessing a (MySQL, currently) database or providing a framework for sending asynchronous Ajax requests back to the server.
Payjacks uses many of the new object-oriented features in PHP 5 to do its work, and handles most of the details required to run a robust web application.
Some of the main features are:I use Payjacks for almost all of my “web page writing” these days. You can use the SimplePage class to just throw out a single PHP/HTML page, or you can use the WebApplication class and template off one of the samples to create more robust web applications.
Since Payjacks takes care of so many of the details that many web application authors simply neglect (error handling, URL processing, security details), using it gives you nearly everything you want except for the actual HTML of your pages.
If you want to use asynchronous Ajax requests, Payjacks helps to make this very easy too.
Payjacks does not help with generation of well-styled HTML or page content. It merely gives you the framework in which to place your content. There is nothing preventing you from designing and developing perfectly secure and robust yet hideously ugly web applications using Payjacks. It is for this reason that graphic designers earn their paycheques. Even my blog site, http://chipmunkninja.com is very rudimentary and clearly shows the limits of my design skills.
In order to run Payjacks, you need:
On Apache, this is mod_rewrite, and can easily be compiled into your Apache build. I have tested and run this on Apache 1.3.xx and 2.0.yy.
For IIS, there are a number of mod_rewrite clones, ranging from very free and open source to very expensive and not open source.
Payjacks is currently version 0.2.0 and should be considered development quality only right now. I plan on making some minor changes to function signatures in the next few versions (mostly to get rid of some parameters that I thought I would need for localisation, but have since realised were the wrong way to go about the problem).
If you have any bugs, comments, or questions, please do not hesitate to contact me at marcwan@chipmunkninja.com.
Last night, after a nice weekend of varying activities (yoga, studying, yoga, sleeping), the plan was to lie in bed and read some Apple Developer Connection documentation for a while and generally learn more about some stuff I’ve been working on lately.
So, it was with some dismay that I sat down with Samantha’s PowerBook G4, clicked on Safari and got … nothing. The icon bounced twice and then stopped. System logs showed nothing, and the application appeared to be all fine in its folder in /Applications.
Uuuuuh. Now what? Oh yes, the System Console, in /Application/Utilities. It gave me the following very helpful message:
2006-08-20 22:32:29.825 Safari[249] Unable to load nib file: MainMenu, exiting
So what was wrong with mine? Some searching around the Intarwebs suggested that it might be a permissions thing. So I fired up the Disk Utility and that said that a bunch of permissions were all wrong (how did that happen?) and fixed them.

Still no dice with Safari. There was another button on the Disk Utility to “Verify Disk”. That did not go well. Lots of red text and scary messages, and finally “exiting”.
So, in the end, I had to actually boot the PowerBook into single user mode (when the machine is booting up, hold down CMD/Apple + S), and run
/sbin/fsck -fy
to get everything working again. It took running this program twice to get everything fixed, and even then, one of the sub-nib files in the MainMenu.nib/ directory for Safari.app was still corrupted, and had to be copied from somewhere else. Luckily, we’ve got another PowerBook G4 here in Beijing, or I would have had to harrass somebody overseas for the file.

None of this is particularly new, shocking, or difficult to figure out, especially for those who have run various flavours of Unix for some time. I’ve just grown so used to my PowerBook being extremely reliable (in three years of owning a Dell Laptop running XP, I never once had half the average uptime I get on my PowerBoook) that it’s so shocking when there is a problem, I’m taken off guard.
There is an interesting point to be made here though, and that is that there no way Samantha would have been able to figure out and do this by herself. She still would have had to go to an Apple Geek Lab (or whatever they’re called) – of which there are none here in Beijing – and get somebody to look at it for her, or beg a friend (who might not have known what s/he was doing and suggested “wiping and reinstalling everything”). As nice as it can be to use, personal computing software still has a long way to go.