Silverlight and PHP

In this article I will provide an overview of Silverlight, which is a plug-in for web browsers, which simplifies development of rich internet applications. Rich internet application (RIA) is quite popular term in these days - it usually referst to applications that run in the web browsers, but provide functionality similar to desktop applications. This can be achieved using various technologies ranging from pure AJAX solutions, to applications that use Flash or Java on the client-side. Silverlight is a project developed by Microsoft based on .NET and it provides an alternative option to these technologies. There are various reasons why you may consider Silverlight as the right platform for you, but I believe that the most compeling reason is that you can choose from a wide range of programming languages to program the application running in the web browser. Aside from standard .NET languages, Silverlight supports Python, Ruby and - thanks to the Phalanger project - also PHP and indeed, we will focus on the PHP support available for Silverlight in this article.

Silverlight and Phalanger

Let me first shortly introduce Silverlight and Phalanger - the two projects that make it possible to write code running in the web browser in PHP language. Phalanger was already discussed in php|architect in the November 2007 issue. Phalanger... debugging

The runtime is available for Microsoft Windows and Mac OS X, with Linux support available via the third-party Moonlight runtime. silverlight 1.0, 2.0 ff/safari/ie/...?

.. 3 files xaml based on xml code + xaml vs. just code .. later in the article xaml - indexing

Creating Silverlight Smiley in PHP

After the introduction, you may be thinking that there are to many different concepts in Silverlight that you have to understand before starting, but I believe that the first example will show the opposite. In this example, we will write almost everything directly in PHP. We will still need a XAML file, but it will contain just the necessary code for loading Phalanger and we will of course need HTML page (or server-side PHP script) where the Silverlight control can be embedded.

In the first example we will look how to manipulate with simple Silverlight graphics and we will start with a code that creates a smiley. Later, we will look how to add animations and how the Silverlight object can respond to mouse events. To make the code very simple, the smiley will be composed of a yellow ellipse and a transparent PNG image with eyes and mouth. You could of course create the whole icon just using Silverlight shapes, but it would be more work - we will return to more complicated graphics later and mention the tools that you can use to produce graphics for Silverlight.

<? // Listing 1 - 'sample1.phpx'
include("slutils.phpx");

slcreate("Canvas", array(
  "Canvas.Name" => "smiley"), 
  array(
    slcreate("Ellipse", array(
      "Ellipse.Name" => "face",
      "Width" => 100,
      "Height" => 100,
      "Fill" => "#FFFF00")),
    slcreate("Image", array(
      "Source" => "Smiley.png"))),
  $canvas);
?>

Let's now examine the code in listing 1. It starts with an include directive, which includes functions and classes from slutils.phpx file. This file is available with Phalanger and it contains a set of useful Silverlight extensions for PHP language. These extensions make it easy to manipulate with Silverlight directly from the PHP code using various wrappers and functions. The first sample is written mostly using these functions, but later we will also look at some more advanced topics, where we will work with Silverlight objects directly.

The rest of the code consists of several calls to function slcreate, which comes from the slutils.phpx file and can be used for creating Silverlight controls from PHP in a convenient way. In the code we use it to create a Canvas control, which serves as a container for other controls and for creating Ellipse and Image controls in this container. The reason why we use a container is that it makes it easier to change location of the whole smiley later.

The slcreate function is rather flexible - it has four parameters and the last two are optional. The first parameter speicifes a name of the control (e.g. "Ellipse"). The second required parameter is an associative array that specifies properties of the object, so for example when creating an ellipse, we set width and height to 100 using this associative array. The third parameter is again an array, but this time it is just a list of child controls of the newly created control. We use this parameter when creating the canvas to specify two child controls of the canvas - that is the ellipse and an image that form the smiley. Finally, the last parameter specifies the parent of the newly created control - this is useful if you want to add new children to already existing control. In our code we use this feature when creating the canvas that contains the smiley, because we want it to be added to the Silverlight object in web browser, which is stored in a global $canvas variable (this variable is also declared in the slutils.phpx file).

Note that two of the properties contain a dot in the name of the property. This is a bit unusuall extension that Phalanger adds to allow using so called attached properties from Silverlight. These properties are not declared as a part of the object and have to be set in a non-standard way, but this is handled in the slutils.phpx file. We will shortly discuss attached properties later, so for now it is just important to remember that Name is an attached property and has to be set using this dot-syntax. This means that in this sample, the entire smiley has a name "smiley" and the ellipse has a name "face". Names are in many ways similar to IDs from HTML, because they can be used for manipulating with the objects, once these are created.

Now that we know how the PHP code that creates our smiley looks, we should also look at the HTML page and XAML file. The HTML page in listing 2 is quite simple, but it requires a bit of JavaScript to load the Silverlight object. This code is similar for all Silverlight applications and can be copied from an existing project, so you won't need to write it by hand. The page refers to a Silverlight.js file, which handles the loading and is provided with Silverlight. The page later uses createObjectEx function to load the XAML file, which is specified as one of the arguments.

<!-- Listing 2 - 'sample1.html' -->
<html><head>
  <script type="text/javascript" src="Silverlight.js"></script>
</head><body>
<div id="slHost" 
  style="width:800px;height:600px;">
  <script type="text/javascript">
    Silverlight.createObjectEx({
      id: "slControl",
      source: "sample1.xaml",
      parentElement: document.getElementById("slHost"),
      properties: { width:"100%", height:"100%", version:"1.1" },
      events: {}
    });
  </script>
</div>
</body></html>

The listing 3 shows the content of the XAML file which is embedded in the HTML page above. The XAML file serves as a container for all the visible controls and in our example with smiley, the $canvas variable refers to a root Canvas object in the XAML file. Usually most of the controls are created in the XAML file, but in the first example we focused on the PHP-side of thing, but we will look at some examples that use XAML for creating controls further.

As already mentioned, the XAML file format is based on XML, so you can easily recognize that the file in the listing 3 declares a root element Canvas, which represents a container for all the Silverlight controls. The root element has several attributes that specify basic properties (like width and height) and also defines XML namespaces for the document. The declaration of XML namespace php is interesting, because it imports controls from a specified .NET namespace of a DLL library - in this case PhpNetCore.dll, which is a core Phalanger library and this library also defines the PhalangerLoader control, which is created inside the canvas. As you can see, the control has only Source attribute and specifies the PHPX file, which we examined earlier. The control doesn't create any visible content and just loads and executes the specified PHPX file, so it used as an entry-point for any Silverlight control implemented using Phalanger.

<!-- Listing 3 - 'sample1.xaml' -->
<Canvas Width="800" Height="600" Background="White"
    xmlns:php="clr-namespace:PHP.Silverlight;assembly=ClientBin/PhpNetCore.dll"
    xmlns="http://schemas.microsoft.com/client/2007">
  <php:PhalangerLoader Source="sample1.phpx" />
</Canvas>

Adding Animations to the Smiley

Now we have a nice smiley living in the Silverlight control, but that's still not showing any rich graphical capabilities of Silverlight, so let's add some animations. An animation that we will add will change the color of the ellipse that forms the smiley face. When we created the ellipse earlier, we assigned a name to this control, so we can access it by its name, which is "face".

The traditional approach to animations that you use for example in JavaScript is to create a timer and change the property (in our case a color) every time the timer ticks, however this makes the code quite complicated even for very simple animations, so in Silverlight a different approach is used. We will simply 'say' that we want to change the color of the face from yellow to red in 400 milliseconds and then back to yellow and we'll leave Silverlight to handle the timers automatically. This is exactly what the following code does:

slcreate("Storyboard", array(
  "Storyboard.Name" => "anim", 
  "AutoReverse" => true), array(
  slcreate("ColorAnimation", array(
    "From" => "#FFFF00",
    "To" => "#FF4000",
    "Duration" => "0:0:0.4",
    "Storyboard.TargetName" => "face",
    "Storyboard.TargetProperty" => "(Fill).(Color)"))),
  $canvas->Resources);  

As you can see we're again using the slcreate function. This is because animations are similar to other elements and controls that you can use in Silverlight and are created using a declarative programming style. The code creates a Storyboard, which can contain and control one or more animations. The animations are created as a children of the storyboard. In our example we create just a single ColorAnimation which changes a color of the object between colors specified by the From and To properties. The two properties that specify what should be animated ar probably the most important part. The Storyboard.TargetName specifies a name of the control that we're animating (in our example this refers to a "face" ellipse created earlier). The Storyboard.TargetProperty is a path to the property that should be animated. In our example, we want to animate Color, which is a property of the brush that specifies how the ellipse is filled, so we need to access the Fill property of the ellipse first and then the Color property of the brush. The braces around the property name are required, because some properties can contain a dot in the name - these are called attached properties and we shortly mentioned them earlier, but we will get back to them in one more example later.

The last thing that we have to do is to start the animation. The storyboard has a name "anim", so we can access it directly by the name using the global $canvas variable. This is an example, where PHP is really flexible, because we can use this syntax even when the animation (or a control) that we want to access is created at runtime:

$canvas->anim->Begin();

Adding Mouse Events to the Smiley

As a next step, we will add a handling of mouse events to our code. We will do something quite simple - when user clicks on the smiley, the smiley will start the animation that we created earlier and it will move to another place on the screen. We will also create a counter that counts the number of times user clicked on the smiley.

It is important to understand that interactive applications (be it a Silverlight content or other type of GUI application that can be created using Phalanger) work in a different way than usual server-side PHP code. In these interactive application, some code executes at the beginning, but it stays loaded in the memory until the application is closed, so when user performs some action, the state of the application is stored in memory and can easily respond to the action. In our example with smiley this means that we will write a function that will move the smiley and we will tell Silverlight to call this function when user clicks on the image. Using the Silverlight terminology, we will register an event handler (the function that should be called) to a MouseLeftButtonDown event of the image.

Before looking at the events and event handlers, we will first add a single global variable to count the number of clicks and we'll import a .NET namespace using import namespace directive. This is a non-standard PHP language extension in Phalanger that makes it possible to use .NET namespaces (however they will be replaced by PHP 6 compatible namespaces in the future). In this case we're importing a namespace that contains some classes that we'll need later for our event handling code (note that import namespace must be the first thing in your source file):

import namespace System:::Windows:::Input;

global $count;
$count = 0;

Next, we'll create a TextBlock element. TextBlock is simply a control that displays some text and we will use it for displaying the number of the clicks on the smiley. We will need to change the displayed text, so we need to assign a name ("txt") to the control. The code is using the slcreate function, which we already discussed:

slcreate("TextBlock", array(
  "TextBlock.Name" => "txt",
  "Canvas.Top" => 10,
  "Canvas.Left" => 300,
  "FontFamily" => "Arial",
  "FontSize" => 20,
  "Text" => "Count: $count"), array(),
  $canvas);

Now, we can finally start implementing our event handler. We will write a function that starts the animation created earlier, moves the smiley to a random location and displays incremented count of the clicks in a TextBlock, which we just created:

function OnClick()
{
  global $canvas;
  global $count;
  
  $canvas->anim->Begin();
  $canvas->smiley->{ "Canvas.Left" } = rand(100, 600);
  $canvas->smiley->{ "Canvas.Top" } = rand(100, 500);
  $canvas->txt->Text = "Count: ".(++$count);
}

The code that starts the animation and changes the text isn't using any new concepts. It simply uses the global $canvas variable to access the elements created earlier and sets its properties. The code that sets position of the smiley however shows two interesting new things. The first thing is that we can use standard PHP functions like rand even when running in Silverlight. This is possible because Silverlight version of Phalanger contains an implementation of many standard PHP functions. Second, we're using an interesting syntax for setting the property. This is because we're setting attached properties that can be accessed using a name with dot character. This is an extension that can be used thanks to wrappers provided by the slutils.phpx.

Attached properties used for setting a location are a good place to explain what is the purpose of these. The location of a control is clearly managed by the parent Canvas element, so it doesn't make much sense to specifying a coordinates of a control that will not be placed in a canvas. This is why attached properties exist - they are present in the control only when it is created in some specified context, where the properties make sense. In our example this means that setting these properties makes sense only when the smiley is placed in a canvas, wich also explains the name of the property - the part before a dot specifies the owner of the property and the part after the dot specifies the name of the property.

Finally, we have to tell Silverlight, that it should call our OnClick function when user clicks on the smiley:

$canvas->smiley->MouseLeftButtonDown->Add
  (new MouseEventHandler("OnClick"));

The code uses an event called MouseLeftButtonDown, which has an Add method. This method can be used for registering a methods that should be called when the event occurs. Silverlight requires the argument to be an instance of special object that represents the event handler, so we have to create the event handler first. This is a concept that comes from .NET, so the same principle is used in other kinds of GUI applications created using Phalanger. The event handler can take either a name of a function as an argument or an array with a reference to an object and a member function name if you want to call a member function of an object when the event occurs.

Creating Elements Using XAML

So far in the article we slightly overlooked the XAML files and we focused on creating Silverlight content from the PHP code. Although creating content programatically is very important (and in PHP it is quite ellegant thanks to the extensions provided by slutils.phpx), it is usually easier to create some parts of the GUI declaratively using XAML.

As already mentioned, XAML is based on XML and it is in many ways similar to HTML. When creating a Silverlight control declaratively, you just create XML element with the same name as the control and use XML attributes to specify properties of the control. XAML is published on the web as a plain text, which also has one very important advantage - the content (for example text displayed using TextBlock element) can be indexed by web search engines. Finally, XML (and therefore also XAML) is very tool-friendly format, so you can create your XAML files in any editor which supports XML. Also, it can be generated by various tools and graphical editors. Aside from Microsoft Expression familly tools, which are in particular focused on XAML, there is also a plugin allowing export of XAML content from Adobe Illustrator.

<!-- Listing 4 - 'sample2.xaml' -->
<Canvas Width="800" Height="600" Background="White"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:php="clr-namespace:PHP.Silverlight;assembly=ClientBin/PhpNetCore.dll">
  <php:PhalangerLoader Source="sample2.phpx" />
  
  <Canvas.Resources>
    <Storyboard x:Name="anim" AutoReverse="true">
      <ColorAnimation From="#FFFF00" To="#FF4000" 
        Duration="0:0:0.4" Storyboard.TargetName="face" 
        Storyboard.TargetProperty="(Fill).(Color)" />
    </Storyboard>
  </Canvas.Resources>
  
  <Canvas x:Name="smiley">
    <Ellipse x:Name="face" Width="100" 
      Height="100" Fill="#FFFF00" />
    <Image Source="Smiley.png" />
  </Canvas>

  <TextBlock x:Name="txt" Canvas.Top="10" Canvas.Left="300" 
    FontFamily="Arial" FontSize="20" Text="Count: 0" />
</Canvas>

Let's now look at the listing 4, which shows exactly the same smiley face as we created earlier in the article, but this time created declaratively using XAML rather than programatically from PHP code. The root Canvas element with XML namespace declarations and the PhalangerLoader element (which specifies what PHPX file contains the program code of the application) were already discussed earlier. The root element then contains an element called Canvas.Resources - this doesn't specify a new control to be created and instead serves as a more flexible way for setting properties of the root element. When the name of the element contains a dot character, it means that we're actually setting a property of the parent element, so the element Canvas.Resources specifies that we're setting Resources property of the root canvas. You may be wondering why the complicated syntax? The answer is that sometimes you want to assign a complex value to some property and this syntax gives us a way for creating the value of the property using XAML elements.

In the example we first create a Storyboard and add it to the root canvas resources. The Storyboard contains one animation which has the same properties as the animation created earlier in the code. Finally, the root element contains two child elements that simply declare two controls - the TextBlock element, which displays the count and a Canvas, which represents the smiley composed of an ellipse and an image.

The file containing the PHP code for this example is quite similar to what we already created in the first example, so we won't include a complete listing here. The only difference is that all the calls to slcreate function can be removed, because the controls are created by Silverlight runtime from the XAML file in listing 4. This means that the file will contain only the OnClick function and the code that registers an event handler for the MouseLeftButtonDown event.

Communicating with The Server-Side

There are many advanced topics that we could discuss, but probably the most important topic when developing any real-world application is manipulation with data. In client-server environment this topic is almost essential. Silverlight applications run on the client (as a component in the web browser), so they form the client-side tier of the distributed application. The server-side tier will usually be PHP running on the server, but Phalanger support for Silverlight doesn't require any specific server-side environment - it can be standard PHP, Phalanger running on the server or any other server-side technology. Finally, the data tier is typically an SQL database server, which can be accessed from the server-side code.

The typical requirement will be to load and visualize some data on the client-side or collect some data from the user and submit them to the server. For this, we need a server-side code that will connect to the database and publish the data or process the request with entered data and store the data in the database. In this section we show how to connect from the client (Silverlight application running in a web browser) to the server (in our next example PHP code on a web server). As you can easily guess, the communication will be done using mechanism similar to the famous XML HTTP request, so the client can send HTTP requests to the server. We'll implement a simple application that displays famous quotes and loads a new quote from the server when some event occurs on the client.

As you're already familiar with all the boilerplate code, the following snippets present just the important part of the code. The first one shows the XAML markup that creates controls needed in this example:

<php:PhalangerLoader Source="sample3.phpx" />
<TextBlock x:Name="quote" 
  Text="Click below to load a quote!"/>
<TextBlock x:Name="new" Text="New quote" Canvas.Top="30"
  Foreground="Blue" TextDecorations="Underline" />

As a next thing, we will implement a server-side PHP script (sample3.php) that will generate a random quote. The script doesn't have any arguments, but the parameters could be send either using HTTP GET/POST mechanism or as a raw POST data (in that case you could read it for example from php://input stream):

header("Cache-Control: no-cache"); 
$quotes = array(
  "Life, loathe it or ignore it, you can't like it.",
  "Life? Don't talk to me about life!",
  "I've got this pain in all the diodes down my left side.",
  "I think you ought to know I'm feeling very depressed.");
echo $quotes[rand(0, sizeof($quotes))];  

This is a standard PHP script running on the server, so there is nothing complicated. The only thing that is worth mentioning is that the script generates a different content for every request and so we have to disable caching in the web browser. The page will be requested by the XML HTTP request from Silverlight, but it still supports caching, so we would get the same quote every time.

Let's now move to the client-side. As in earlier samples, we'll need to write a function that will handle MouseLeftButtonDown event of a control. In this example we want to handle click events of the control called new, which simulates a hyperlink. When user clicks on the control, we want to send an HTTP request to the server and request the page sample3.php, which we implemented earlier. A typical way for downloading a web page in PHP is using fopen function, which also supports HTTP protocol:

$fh = fopen(sl_mkabsolute("sample3.php"), 'r');
$canvas->quote->Text = fgets($fh);
fclose($fh);    

The fopen function requests a data from HTTP path only when the URL starts with http:// prefix and otherwise it treats the path as a relative file path, so we need to specify a complete URL. If you know the location of your application, you can use a string, but if you want to request an URL relatively to the URL of the HTML page loaded currently in the web browser, you can use sl_makeabsolute function provided by Silverlight. This function uses HtmlPage object from Silverlight, which reveals the current URL and combines this URL with the given argument, so in this example the HTML page URL (something like http://svr/sample3.html) will be combined with sample3.php and the result will be http://svr/sample3.php. Finally, if you want to specify additional properties for the request, you can use the stream_context_create function. You can specify POST or GET method, additional POST parameters or raw POST data.

Conclusion

http://silverlight.net http://dev.live.com/silverlight