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 Blob serviceThis tutorial parallels another tutorial (here) 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 of the page (sign-in required). If you wish to provide feedback on this page, use the comments field at the bottom of the page or send mail to azure@microsoft.com.

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 (stored in a Windows Azure Table) and an optional picture (stored in a Windows Azure Blob). When a golfer posts a new message, the Web role creates an entry using the Table service (containing message information. 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 Blob service with PHP
  • How to deploy a PHP application to Windows Azure

Prerequisites

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 Blob and Table services are 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 Blob Service

The Windows Azure Blob service enables applications to store large objects, up to 1TB each. It supports a massively scalable blob system via the Windows Azure CDN, where hot blobs will be served from many servers to scale out and meet the traffic needs of your application. Furthermore, the system is highly available and durable. You can always access your data from anywhere at any time, and the data is replicated at least 3 times for durability. In addition, strong consistency is provided to ensure that the object is immediately accessible once it is added or updated; a subsequent read will immediately see the changes made from a previously committed write.

An application must use a valid account to access Windows Azure Storage. An account can have many Blob containers. Each container provides a grouping of a set of blobs.  

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.

1.       Create a directory for the application. In the root directory for IIS (C:\inetpub\wwwroot), create a directory called GolferMessageBoard.

2.       Create the files for the application. This application will consist of 6 files: index.php, MessageBoardEntry.php, MessagePicture.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 Blob and Table services. The primary classes for doing so are the Microsoft_WindowsAzure_Storage_Blob and the Microsoft_WindowsAzure_Storage_Table classes. To instantiate these classes so that they can use the Blob and Table services, your account name and key are necessary. Since these classes will be used to create Blob and Table clients in multiple places in our application, put configuration information in the storageConfig.php file:

<?php
define("STORAGE_ACCOUNT_NAME", "Your Storage Account Name");
define("STORAGE_ACCOUNT_KEY", "Your Storage Account Key");

define("PROD_SITE", false);
define("BLOB_CONTAINER_NAME", "picturecontainer");
define("TABLE_NAME", "MessageBoardEntry");

?>

 

Note the following about the code above: 

  • 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” (i.e. using the Windows Azure Compute Emulator and Compute Storage). When you supply your account name and key, and change the value of PROD_SITE to true the application will use the live Blob and Table services.
  • You can choose what ever name you want for the blob container name, but the name must adhere to the meet the requirements outlined here: Naming Blobs, Containers, and Metadata.
  • The name for the Azure table (TABLE_NAME) must match the name of the class (defined later in the tutorial) for containing table messages.

Create the MessagePicture Class (MessagePicture.php)

The MessagePicture class is a class that contains information for saving a picture (associated with a particular message) to a Windows Azure Blob. To create this class, add the following code to the MessagePicture.php file:

<?php
require_once 'Microsoft\WindowsAzure\Storage\Blob.php';

include_once 'storageConfig.php';

class MessagePicture
{
   public $name;
   public $picture;

   function __construct($name)
   {
      $this->name = $name;
   }

   function send()
   {
      if(PROD_SITE)
      {
         $blobStorageClient = new Microsoft_WindowsAzure_Storage_Blob(
                                                      'blob.core.windows.net',
                                                       STORAGE_ACCOUNT_NAME,
                                                       STORAGE_ACCOUNT_KEY);
      }
      else
      {
 $blobStorageClient = new Microsoft_WindowsAzure_Storage_Blob();
      }

      $blobStorageClient->createContainerIfNotExists(BLOB_CONTAINER_NAME);
      $blobStorageClient->setContainerAcl(
                               BLOB_CONTAINER_NAME,
                               Microsoft_WindowsAzure_Storage_Blob::ACL_PUBLIC);

      $blobStorageClient->putBlob(BLOB_CONTAINER_NAME, $this->name, $this->picture);
   }
}
?>

 

Note the following about the code above:

  • The $name property is the row key (a GUID) of the corresponding message.
  • The $picture property is set in the index.php script.
  • When no parameters are passed to the Microsoft_WindowsAzure_Storage_Table class, it will use the Compute Storage. If PROD_SITE is defined as false (in the storageConfig.php file), your application will be working against local simulated storage.
  • The createContainerIfNotExists is used to create a container for blobs.
  • By default, the access control policy on blob containers is to make them private. the call the setContainerACL makes our container public.

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;

   /**
    * @azure pictureUrl
    */
    public $pictureUrl = '';

    

    function __construct()

    {

       $this->_partitionKey = date("mdY");

$this->_rowKey = trim(com_create_guid(), "{}");

    }

    

    function send()

    {

       include_once 'storageConfig.php';

       $tableStorageClient->insertEntity(TABLE_NAME, $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, $golferMessage and $pictureUrl 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();
}


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(TABLE_NAME);
$messages = $tableStorageClient->retrieveEntities(TABLE_NAME, null, TABLE_NAME);

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>";
if($message->pictureUrl != '')
{
echo "<br /><img class='resize' src='".$message->pictureUrl."'/>";
}
echo "</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 firstgetTimestamp 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()' enctype='multipart/form-data'>
  <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>
     <dt>
      <label for="FileUpload">Photo:</label>
     </dt>
      <dd>
       <input type="file" name="FileUpload" size="16" />
      </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 thegetMessages.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 theindex.php file:

<?php
if(isset($_POST['txtName']))
{
require_once 'MessagePicture.php';
require_once 'storageConfig.php';
require_once 'MessageBoardEntry.php';
$mbEntry = new MessageBoardEntry();
$mbEntry->golferName = $_POST['txtName'];
$mbEntry->golferMessage = $_POST['txtMessage'];
if($_FILES['FileUpload']['error'] == 0)
{
$mbEntry->pictureUrl = "https://".
                                        STORAGE_ACCOUNT_NAME.
                                        ".blob.core.windows.net/".
                                        BLOB_CONTAINER_NAME.
                                        "/".$mbEntry->getRowKey();

$mbPicture = new MessagePicture($mbEntry->getRowKey());
$mbPicture->picture = $_FILES['FileUpload']['tmp_name'];
$mbPicture->send();
}
else
{
$picture = null;
}
$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 sendmethod() is called.
    • If a picture has been successfully uploaded to the server, the pictureUrl property of the MessageBoardEntry object is set to the URL for the picture, a new MessagePicture object is created, it's name is set to the row key of the message, its picture property is set to the uploaded picture, and the picture is sent.
  • Finally, the message is sent.

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 and pictures will be stored in the Windows Azure Storage Emulator. In the second phase, the application will run in the Compute Emulator, but messages and pictures 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:

1.       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.

2.       When you are prompted to provide administrator privilege, click Yes (you may be prompted more than once):



 

3.       You should now see an icon for the Compute Emulator in your system tray. Right click the icon and select Start Storage Emulator:



 

4.       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…

  1. Open a Web browser and browse to http://windows.azure.com/.
  2. Sign in using the Windows Live ID associated with your Windows Azure account.
  3. Click Use the New Portal if you are prompted; otherwise, skip this step.
  4. In the left pane, click Hosted Services, Storage Accounts & CDN.
  5. In the left pane, click Storage Accounts. Your storage accounts and the associated subscriptions are listed in the middle pane.
  6. From the top menu, click New Storage Account.
  7. 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.)

 

  1. 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.
  2. 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:

1.       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.

2.       Return to the Windows Azure portal and click on New Hosted Service. In the resulting dialog, do the following:

a.       Choose a subscription.

b.      Enter a name for your service.

c.       Enter a unique URL for your service.

d.      Select Create or Choose an affinity group. Select the affinity group that you created when creating a storage account.

e.      Select Deploy to stage environment.

f.        Make sure that Start after successful deployment is checked.

g.       Enter a deployment name.

h.      For Package location, browse to the GolferMessageBoard.cspgk file that you created in the previous step.

i.         For Configuration file, browse to the ServiceConfiguration.cscfg file that you created in the previous step.

j.        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…

  1. From the portal, in the left pane, click Hosted Services.
  2. In the middle pane, expand GolferMessageBoard, and then click v1.0.0.0.
  3. From the top menu, click Swap VIP.
  4. In Swap VIPs, click OK. Wait until the Status for the deployment changes to Ready.
  5. 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 Blob Service with PHP tutorial.