Using the Windows Azure Web Role and Windows Azure Table Service with PHP
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.
Note
If you wish to contribute to this page, use the Edit tab at the top (sign-in required). If you wish to provide feedback for this documentation please either send e-mail to azuredocs@microsoft.com or use the Comment field at the bottom of this page (requires sign in).
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.
Objectives
In this tutorial, you will learn...
- The process of developing a PHP application for Windows Azure
- How to use the Windows Azure Web Role with PHP
- How to interact with the Windows Azure Table service with PHP
- How to deploy a PHP application to Windows Azure
Prerequisites
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.
Understanding the Architecture
The following diagram illustrates the development and runtime components involved in this tutorial:
- You develop the PHP application in your IDE of choice and use the Windows Azure Command Line Tools for PHP Developers and the Windows Azure SDK for PHP (which both are dependent on the Windows Azure SDK).
- You deploy the application to Windows Azure Emulator for testing and to Windows Azure for production.
- A Windows Azure project includes two configuration files: ServiceDefinition.csdef and ServiceConfiguration.cscfg. These files are packaged with your Windows Azure application and deployed to Windows Azure.
- A service hosted in Windows Azure consists of one or more Web roles and worker roles. A Web role hosts a PHP application and is accessible via an HTTP or HTTPS endpoint. A Web role is commonly the front-end for an application. A worker role is a role that is useful for generalized development, and may perform background processing for a Web role. In this tutorial you will create a Web role project. For more information, see Overview of a Windows Azure Application.
- The Windows Azure storage services provide persistent, redundant storage in the cloud. The storage services include these fundamental services: Blob service, Queue service, and Table service. The Table service is used in this tutorial. For more information, see Understanding Data Storage Offerings on the Windows Azure Platform.
- You can use the portal to administrate Windows Azure platform resources.
Understanding the Table Service Object Model
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.
Part 1: Create Your Windows Azure PHP Project
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.
- Create a directory for the application. In the root directory for IIS (C:\inetpub\wwwroot), create a directory called GolferMessageBoard.
- Create the files for the application. This application will consist of 5 files: index.php, MessageBoardEntry.php, getMessages.php, storageConfig.php, and main.css.
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:
Configure Storage Information (storageConfig.php)
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:
- The Microsoft_WindowsAzure_Storage_Table class is defined in the Windows Azure SDK for PHP in the Table.php file.
- When you create a Windows Azure subscription (steps for doing so are later in this tutorial), you will get a storage account name and a storage account key. You will need to replace Your Storage Account Name and Your Storage Account Key in the code above with your account name and key at that time. In the meantime, you will run the application in “development mode” (note that PROD_SITE is defined as false). When no parameters are passed to the Microsoft_WindowsAzure_Storage_Table class, it will use the Windows Azure Compute Emulator and Compute Storage.
- The last line in the code above creates a table called MessageBoardEntry if the table does not already exist.
Create the MessageBoardEntry Class (MessageBoardEntry.php)
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);
}
}
?>
Note the following about the code above:
- The MessageBoardEntry class inherits from the Microsoft_WindowsAzure_Storage_TableEntity, which is referenced in the included Table.php file.
- The annotations (in the form of comments) on the $golferName and $golferMessage properties allow the Windows Azure SDK for PHP to map these properties to a table with the same name as the class (MessageBoardEntry).
- The properties that are set in the constructor (_partitionKey and _rowKey) are inherited properties. The partition key is set to a string in a **mmddYYYY **format and the row key is set to a GUID.
- The send method sends a MessageBoardEntry object to the Table service.
Create Functionality for Retrieving Messages (getMessages.php)
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>";
}
?>
Note the following about the code above:
- To ensure that a cached version of the page is not retrieved, the “no-cache” headers are sent via the PHP header function.
- Table entities are not necessarily returned ordered by timestamp. The objSort and getIndex functions are used together to accomplish this after the entities have been retrieved.
- The getIndex function requires two function calls (both called getTimestamp) to retrieve the timestamp of a table entity. The first getTimestamp call is a method on the MessageBoardEntry object (inherited from its parent class) that returns a datetime object. The second call to getTimestamp is a method on the datetime object that returns a timestamp.
- The objSort function relies on the array_multisort function to sort table entities by timestamp.
- The retrieveEntities method is used to retrieve entities from a table as objects.
- The first parameter is the name of the table from which entities are retrieved.
- The second parameter is a filter for entities. Passing null for this parameter returns all entities.
- The third parameter specifies the class that entities are retrieved as.
- After messages are retrieved, each one is formatted as an HTML table row. Because this script will be invoked by an AJAX call when the main page is loaded and every 60 seconds thereafter, the formatting is necessary to update the table in the main page.
Build the Main page (index.php)
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.
HTML
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>
Note the following about the code above:
- Each time the page is loaded, the Javascript function getMessages is called. This function makes an AJAX call to get the output of the getMessages.php script.
- Messages cannot be sent without specifying a name and a message. The Javascript function validateInput is called to make sure these have been entered before the form is submitted.
- The HTML markup will be formatted by a Cascading Style Sheet (CSS).
Javascript
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>
Note the following about the code above:
- The getMessages function makes an AJAX call that retrieves the output of the getMessages.php script.
- The setTimeout function is used to trigger the getMessages function every 60 seconds. The effectively refreshes the messages on the page every minute (allowing the user to view messages from other users in a timely manner).
- The validateInput will not allow the form to be submitted unless both the user and message input are not empty.
PHP
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();
}
?>
Note the following about the code above:
- The page submission contains POST data (which is checked for by looking at the $_POST[‘txtName’] variable, then the body of the if statement is executed.
- In the body of the statement, a new MessageBoardEntry object is created, its golferName and golferMessage properties are set, and its send method is called.
CSS
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.
Part 2: Test Your Windows Azure PHP Project
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.
Testing in the Windows Azure Compute and Storage Emulators
To run your application locally, follow these steps:
In a command prompt, navigate to your Windows Azure Command-Line Tools for PHP Developers directory and execute the following command without line breaks:
>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.
When you are prompted to provide administrator privilege, click Yes (you may be prompted more than once):
You should now see an icon for the Compute Emulator in your system tray. Right click the icon and select Start Storage Emulator:
You should now be able to access your running application at this URL: http://127.0.0.1:81/.
Test Using Live Windows Azure Storage
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…
- Open a Web browser and browse to http://windows.azure.com/.
- Sign in using the Windows Live ID associated with your Windows Azure account.
- Click Use the New Portal if you are prompted; otherwise, skip this step.
- In the left pane, click Hosted Services, Storage Accounts & CDN.
- In the left pane, click Storage Accounts. Your storage accounts and the associated subscriptions are listed in the middle pane.
- From the top menu, click New Storage Account.
- In Create a New Storage Account, type or select the following values, and then click Create.
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.)
- Click Create. Wait until the status for the storage account changes to Created. In the next procedure To create a host service, you will choose the same affinity group for the hosted service.
- In the Properties pane on the right, write down the Primary access key and Account Name. You will need the information to configure the application to use this storage account.
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.
Part 3: Deploy Your Application to Windows Azure
In this section you will deploy the GolferMesageBoard application to the Windows Azure staging environment, then move it to the production environment.
Deploying to the Staging Environment
To deploy your application to the Windows Azure staging environment, follow these steps:
Assuming that you made some changes to your application in testing, you will need to rebuild the deployment package created by the Windows Azure Command-Line Tools. To do this, in a command prompt navigate to the Windows Azure Command-Line Tools directory and execute the following command (without line breaks):
>php package.php --project=GolferMessageBoard --source="c:\inetpub\wwwroot\GolferMessageBoard" --phpRuntime="C:\Program Files (x86)\PHP\v5.3.4" --target="c:\workspace" --cleanRebuild
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.
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.
Return to the Windows Azure portal and click on New Hosted Service. In the resulting dialog, do the following:
- Choose a subscription.
- Enter a name for your service.
- Enter a unique URL for your service.
- Select Create or Choose an affinity group. Select the affinity group that you created when creating a storage account.
- Select Deploy to stage environment.
- Make sure that Start after successful deployment is checked.
- Enter a deployment name.
- For Package location, browse to the GolferMessageBoard.cspgk file that you created in the previous step.
- For Configuration file, browse to the ServiceConfiguration.cscfg file that you created in the previous step.
- Click OK.
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).
Deploying to the Windows Azure Production Environment
To promote the application to production…
- From the portal, in the left pane, click Hosted Services.
- In the middle pane, expand GolferMessageBoard, and then click v1.0.0.0.
- From the top menu, click Swap VIP.
- In Swap VIPs, click OK. Wait until the Status for the deployment changes to Ready.
- In the Properties pane, click the URL in the DNS name box. The application is opened in a new browser tab or a new browser window depending on your browser configuration.
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.