Wednesday, March 3, 2010

iPhone development with PHP and XML

Building the command list



Building the application starts with defining some commands that the iPhone remote
control will present for you to select. You use an XML file to define the
commands. Listing 1 shows this file.



Listing 1. commands.xml
                
<commands>
<command title="Next Song">
tell application "iTunes" to next track
</command>

<command title="Previous Song">
tell application "iTunes" to back track
</command>
</commands>


The file is a list of <command> tags. Each tag has
a title attribute that defines a human readable title for the command. And the
content of the <command> tag is the AppleScript
code to execute when the command is requested. Because of XML encodings, if you
want to put in any AppleScript code that has angle bracket (< or >) or
ampersand (&) characters, you must encode those as <,
>, and &, respectively.



To wrap this XML file, I wrote a PHP V5 Command class
that reads the file, returns the command names, and runs the commands using the
Mac OS X osascript command. The code for this class is
shown in Listing 2.



Listing 2. commands.php
                
<?php
class Commands
{
private $_commands;

function __construct()
{
$this->_commands = array();

$doc = new DOMDocument();
$doc->load('commands.xml');
$cmds = $doc->getElementsByTagName( 'command' );
foreach( $cmds as $cmd )
{
$this->_commands []= array(
'title' => $cmd->getAttribute('title'),
'command' => $cmd->firstChild->nodeValue
);
}
}

function getCommands()
{
$cmds = array();
foreach( $this->_commands as $cmd )
{
$cmds []= $cmd['title'];
}
return $cmds;
}

function runCommand( $id )
{
$ph = popen( "osascript", "w" );
fwrite( $ph, $this->_commands[$id]['command'] );
fclose( $ph );
}
}
?>




The class starts by loading up the commands.xml file. It reads in the file using the
DomDocument PHP class. Then, it finds all the command
arguments using getElementsByTagName. When it has the
<command> tags as an array, the class loads the
_commands member variable with the titles and
AppleScript commands.


Two additional methods are defined:




  • The getCommands() method, which simply returns a list of the names

  • The runCommand() method, which given an index runs that command using the osascript command-line AppleScript executor.




Building the interface


With the commands XML file and Commands PHP class written,
it's time to add an interface. Just to make sure everything is
working properly, I'll put a fairly rudimentary interface on it. This interface is
shown in Listing 3.



Listing 3. Simple interface script
                

<html><body>
<?php
require_once('commands.php');
$cmds = new Commands();
?>
<?php
$id = 0;
foreach( $cmds->getCommands() as $cmd ) {
?>
<a href="do.php?id=<?php echo($id);?>"><?php echo( $cmd ); ?></a><br/>
<?php $id++; } ?>

</body></html>


The script first gets the Command class, and then asks it
for the lists of commands using the getCommands() method.
Then, the script builds a set of links to the do.php page using the command index
number and the name of the command that the Commands
class returned.



When I navigate to the page in the Safari browser, I see something like
Figure 1.




Figure 1. The rudimentary interface

The rudimentary interface



I could use this as my iPhone interface and it would work. But it wouldn't feel like
the iPhone. So, the next thing to do is use the iUI toolkit to extend the interface.
Listing 4 shows the code for doing so.




Listing 4. index.php
                
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<title>Mac Controller</title>
<meta name="viewport"
content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
<style type="text/css" media="screen">@import "iui/iui.css";</style>

<script type="application/x-javascript" src="iui/iui.js"></script>
</head>
<body>
<div class="toolbar">
<h1 id="pageTitle"></h1>
<a id="backButton" class="button" href="#"></a>

</div>
<?php
require_once('commands.php');
$cmds = new Commands();
?>
<ul title="Commands" selected="true">
<?php
$id = 0;
foreach( $cmds->getCommands() as $cmd ) {
?>
<li>
<a href="do.php?id=<?php echo($id);?>"><?php echo( $cmd ); ?></a>

</li>
<?php $id++; } ?>
</ul>
</body></html>



At the top of the file, you include the iUI CSS file that has all the styles that
give the page its iPhone look. Then, you include the iUI JavaScript file that
handles all the interactivity. After that, you use the Commands
class to get the list of commands. With that list, you build an unordered list
(<ul>) with list item elements for each item
(<li>). No, it's not as ugly as it sounds. In
fact, you can look at it in Safari, and you'll get exactly the same look as you
would on the iPhone, as shown in Figure 2.





Figure 2. The index.php page as rendered in Safari

The index.php page as rendered in Safari



If you use Windows, don't worry: Safari now runs on both Windows and Mac. Of
course, the PHP that runs this code must be on a Mac to run the
osascript command and the AppleScript code. But you
could use system commands if you want to run this on DOS or UNIX® systems.



The final step is to create the do.php file that index.php references to run the
actual commands. This class is shown in Listing 5.




Listing 5. do.php
                
<?php
require_once('commands.php');

$cmds = new Commands();
$cmds->runCommand( $_GET['id'] );
?>


Now, you can use Safari to browse to the page locally and just click the links to
check whether the application works. If everything is in order, iTunes
will go to the next or previous song depending on what you select.


One thing I did have to change on my installation was to edit the /etc/httpd/httpd.conf
file, change the User setting to my user name, and change the Group

setting to staff. I then rebooted my Apache server by running this command
line:


% apachectl graceful


With that done, my iTunes interface flipped back and forth between tracks when I
clicked the links. I can then turn on my iPhone and use the Safari browser to go
to my local machine by IP address and access the application, as long as my laptop
and my iPhone are on the same Wi-Fi network.



Telekenesis



As I did the research for this article, I found that someone had already taken
this whole concept of a Mac-driven remote for the iPhone to a new level. The project
is called telekinesis, and it's
hosted on the Google Code site. The application is called iPhone Remote, and
it runs as a graphical user interface (GUI) application in Mac OS X.



When I launch iPhone Remote, it opens Safari to a page that shows what it will look
like on the iPhone. This is shown in Figure 3.




Figure 3. The iPhone Remote interface

The iPhone Remote interface


From here, I can navigate to my applications and open them, browse my documents, use
an iTunes remote, even navigate around the screen and run command lines—all
from my iPhone.


The iPhone Remote does prompt you for a user name and password so that not just
anyone can use your Mac after you've installed and run it. So, it's possible to
use the iPhone as a secure virtual network computing (VNC) device for your Mac
remotely.




Conclusion






Developing for the iPhone is a breeze. The ads say that the iPhone gives you access
to the Internet as is rather than some mobile version of it, and the ads are right:
You can browse to your normal pages just as you would on your Mac or PC. But
toolkits like the iUI interface builder help give the application a more genuine
iPhone look and feel—handy with applications like this XML
and PHP-driven iPhone remote.