This tutorial demonstrates how to use the Windows Azure SDK for PHP and the Windows Azure Command-Line Tools for PHP Developers to create, test, and deploy a PHP application to Windows Azure. While the Windows Azure SDK for PHP allows you to leverage the Table, Blob, and Queue storage services in Windows Azure, this tutorial will focus on using the Windows Azure Table service. This tutorial parallels this tutorial that builds a similar application using Visual Studio and ASP.NET.
In this tutorial, you will create a simple golfer message board application. In the application, a Web role provides the front-end that allows golfers to view the contents of the message board and add new entries. Each entry contains a name and a message. When a golfer posts a new message, the Web role creates an entry using the Table service that contains the information entered by the golfers. The Web role also renders this information to the browser so golfers can view the content of the message board.
Note: The tutorial code has been tested on Windows Azure SDK versions 1.3 and 1.4.20227.1419, the Windows Azure SDK for PHP version 3.0.0, and the Windows Azure Command-Line Tools for PHP Developers March 2011 Update.
In this tutorial, you will learn...
In order to complete the deployment portion in this tutorial, you must sign up for a Windows Azure account and purchase a subscription. For more information, see Get Started with the Windows Azure Platform and Provisioning Windows Azure.
Download and install the Windows Azure Command-Line Tools for PHP Developers. Instructions for doing so can be found in the Setup Guide.
Note: The command line tools can also be installed with the Web Platform Installer. If you choose to use the Web PI, you can install PHP and configure IIS to handle PHP requests at the same time.
Download and unzip the Windows Azure SDK for PHP.
Note: The code in this application is based on the Windows Azure SDK for PHP v3.0.0 BETA.
Configure IIS to run PHP applications.
Note: It is possible to use Apache to handle PHP requests in Windows Azure, but this tutorial will use IIS.
The following diagram illustrates the development and runtime components involved in this tutorial:
The Windows Azure Table service is structured storage in the cloud. The following diagram illustrates the Table service data model:
An application must use a valid account to access Windows Azure Storage service. (Later in this tutorial, you will create a new account using Windows Azure Platform Management Portal.) In the Table service, a table contains a set of entities. Each entity contains a set of properties. An entity can have at most 255 properties including the mandatory system properties - PartitionKey, RowKey, and Timestamp. "PartitionKey" and "RowKey" form the unique key for the entity.
The only difference between developing a Windows Azure PHP project and any other PHP project is that a Windows Azure PHP project will leverage the classes that are in the Windows Azure SDK for PHP.
Note: You can download the source code for this application from the MSDN Code Gallery at Windows Azure Platform Tutorials - PHP. You directory should now look as follows:
The Windows Azure SDK for PHP contains classes for interacting with the Table service. The primary class for doing so is the Microsoft_WindowsAzure_Storage_Table class. Since this class will be used to create a Table client in multiple places in our application, put configuration and client creation logic in the storageConfig.php file:
<?php require_once 'Microsoft\WindowsAzure\Storage\Table.php'; define("STORAGE_ACCOUNT_NAME", "Your Storage Account Name"); define("STORAGE_ACCOUNT_KEY", "Your Storage Account Key"); define("PROD_SITE", false); if(PROD_SITE) { $tableStorageClient = new Microsoft_WindowsAzure_Storage_Table( 'table.core.windows.net', STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY); } else { $tableStorageClient = new Microsoft_WindowsAzure_Storage_Table(); } $tableStorageClient->createTableIfNotExists("MessageBoardEntry"); ?>
Note the following about the code above:
When a class name matches a table name and properties are annotated with comments correctly, the Windows Azure SDK for PHP will automatically map the class properties to the table. To do this for your application, add the following code to the MessageBoardEntry.php file:
<?php require_once 'Microsoft\WindowsAzure\Storage\Table.php'; class MessageBoardEntry extends Microsoft_WindowsAzure_Storage_TableEntity { /** * @azure golferName */ public $golferName; /** * @azure golferMessage */ public $golferMessage; function __construct() { $this->_partitionKey = date("mdY"); $this->_rowKey = trim(com_create_guid(), "{}"); } function send() { include_once 'storageConfig.php'; $tableStorageClient->insertEntity('MessageBoardEntry', $this); } } ?>
To add functionality for retrieving messages from the MessageBoardEntry table, add the following code to the getMessages.php file:
<?php header('Cache-Control: no-cache'); header('Pragma: no-cache'); require_once 'MessageBoardEntry.php'; function objSort(&$objArray,$indexFunction,$sort_flags=0) { $indices = array(); foreach($objArray as $obj) { $indeces[] = $indexFunction($obj); } return array_multisort($indeces,$objArray,$sort_flags); } function getIndex($obj) { return $obj->getTimestamp()->getTimestamp(); } include 'storageConfig.php'; $messages = $tableStorageClient->retrieveEntities("MessageBoardEntry", null, "MessageBoardEntry"); if(count($messages) > 0) { objSort($messages,'getIndex'); $messages = array_reverse($messages, true); } foreach($messages as $message) { echo "<tr> <td> <div class='signature'> <div class='signatureDescription'> <div class='signatureName'>" .$message->golferName. "</div> <div class='signatureSays'> says </div> <div class='signatureDate'>" .date_format($message->getTimestamp(), "n/j/Y"). "</div> <div class='signatureMessage'>" .$message->golferMessage. "</div> </div> </div> </td> </tr>"; } ?>
The index.php file is the main page for the GolferMessageBoard application. It essentially has 3 sections, divided by language: PHP, Javascript, and HTML. Breaking these sections down one-by-one will help in understanding how the page works.
The body of the HTML markup is a form (for entering a new message) and a table (for displaying sent messages). After creating the basic layout of an HTML page in the index.php file (i.e. <html>, <head>, and <body> elements), replace the body element with the following:
<body onload='getMessages()'> <form method="post" action="./index.php" id="frmMessageBoard" onsubmit='return validateInput()'> <div class="general"> <div class="title"> <h1>Golfer Message Board</h1> </div> <div class="inputSection"> <dl> <dt> <label for="NameLabel">Name:</label> </dt> <dd> <input name="txtName" type="text" id="txtName" class="field" /> </dd> <dt> <label for="MessageLabel">Message:</label> </dt> <dd> <textarea name="txtMessage" rows="2" cols="20" id="txtMessage" class="field"></textarea> </dd> </dl> <div class="submitSection"> <input type="submit" name="btnSend" value="Send" id="btnSend" /> </div> </div> <div id="upMessageBoard"> <table id='dlMessages' cellspacing='0' style='border-collapse:collapse;'> <div id="tablerows"> </div> </table> </div> </div> </form> </body>
Add the following <script> element to the <head> element of the index.php file to facilitate retrieval of Table messages and validation of form input:
<script type="text/javascript"> function getMessages() { xmlhttp=new XMLHttpRequest(); var url="getMessages.php"; xmlhttp.onreadystatechange=getMessagesInfoStateChanged; xmlhttp.open("GET", url, true); xmlhttp.send(null); } function getMessagesInfoStateChanged() { if (xmlhttp.readyState==4) { document.getElementById("tablerows").innerHTML = '<div id="tablerows">' + xmlhttp.responseText + '</div>'; setTimeout('getMessages()', 60000); } } function validateInput() { var name = document.getElementById('txtName'); var message = document.getElementById('txtMessage'); if(name.value == '' || message.value == '') { alert("Both Name and Message are required."); return false; } return true; } </script>
PHP is used on the main page to handle form submission (sending of a message). To process a submitted form, add the following code at the top of the index.php file:
<?php // Send new message if one has been POSTed. if(isset($_POST['txtName'])) { require_once 'MessageBoardEntry.php'; $mbEntry = new MessageBoardEntry(); $mbEntry->golferName = $_POST['txtName']; $mbEntry->golferMessage = $_POST['txtMessage']; $mbEntry->send(); } ?>
How the HTML in this application is formatted is arbitrary and up to you. Add CCS code the main.css file as you see fit. If you choose, you can use the CSS code that is available in source code download for this application, available here: MSDN Code Gallery at Windows Azure Platform Tutorials - PHP.
Your application is now ready for testing.
In this section you will test your application in two phases. In the first phase the application will run locally in the Windows Azure Compute Emulator and messages will be stored in the Windows Azure Storage Emulator. In the second phase, the application will run in the Compute Emulator, but messages will be stored to a live Windows Azure Storage account. When testing is complete, you will deploy the application to a Windows Azure hosted service.
To run your application locally, follow these steps:
>php package.php --project=GolferMessageBoard --source="c:\inetpub\wwwroot\GolferMessageBoard" --phpRuntime="C:\Program Files (x86)\PHP\v5.3.4" --target="c:\workspace" --runDevFabric
Note: You may need to change values for the source and phpRuntime parameters if your application source code and PHP installation are in different directories.
Note: All paths in your php.ini file need to be relative before deploying an application to Windows Azure. (e.g. extension_dir='.\ext' instead of extension_dir='c:\PHP\ext'). Now is a good time to make sure that all paths are relative.
To test your application using a live Windows Storage account, you need to first create a storage account. To create a storage account for the golfer message board application…
Name
Value
Choose a subscription
(select a subscription that is associated with the storage account)
Enter a URL
(e.g.<yourname>gmb)
Choose a region or affinity group
(select Create a new affinity group. In Affinity Group Name, type (for example) ag<yourname>GMB; in Location, choose the region where you wish your service to run, most likely, the one that is closest to your current location, and then click OK.)
Now you can configure your application to use your live storage account. Open the storageConfig.php file, enter your storage account name, your storage account key, and define PROD_SITE to be true:
define("STORAGE_ACCOUNT_NAME", "Your_Account_Name_Here"); define("STORAGE_ACCOUNT_KEY", "Your_Account_Key_Here"); define("PROD_SITE", true);
Be sure to save the file after you make changes.
Now you can follow the instructions in the Testing in the Windows Azure Compute and Storage Emulators section above, but you can skip step 3 since you will not be using the Storage Emulator.
In this section you will deploy the GolferMesageBoard application to the Windows Azure staging environment, then move it to the production environment.
To deploy your application to the Windows Azure staging environment, follow these steps:
>php package.php --project=GolferMessageBoard --source="c:\inetpub\wwwroot\GolferMessageBoard" --phpRuntime="C:\Program Files (x86)\PHP\v5.3.4" --target="c:\workspace" --cleanRebuild
The command above will create the following directory: c:\workspace\GolferMessageBoard_Build. That directory will contain two directories: GolferMessageBoard and GolferMessageBoard_WebRole. You will need the GolferMessageBoard.cspkg file and ServiceConfiguration.cscfg file in the GolferMessageBoard directory for deployment.
It will take several minutes for your application to upload, deploy, and start. When the portal indicates that your application is in the ready state, you can access it at the URL provided in the DNS name property for the deployment (on the right hand column of the portal).
To promote the application to production…
Note: Some DNS services take longer to replicate the records. If you get a page not found error, you might need to try browsing to the URL again in a few minutes.
Congratulations! You have completed the Using the Windows Azure Web Role and Windows Azure Table Service with PHP tutorial.
Jonathan Gao edited Revision 23. Comment: update the architecture diagram
Jonathan Gao edited Revision 21. Comment: formating; updating tags
Brian Swan - MSFT edited Revision 5. Comment: Bad formatting.