Building your commercial Virtual Earth Website using PHP

This article is written for an old version of the Virtual Earth platform. While still available for reference purposes, it is unlikely to work if implemented.

MapSearchControl is JavaScript control that can bring the magic of Virtual Earth to your own website.

Microsoft provides this control free of charge, in exchange for reserving the right to place location specific advertising content on the maps. The control also comes with its own built in search widget.

However, the mechanisms for returning search results and for receiving location-specific advertsing content have one common problem - they both rely on receiving JavaScript from a non-local domain (in this case, virtualearth.msn.com).

Cross-site scripting is a tremendous security vulnerability and is quite rightly turned off by default in modern browsers. So we must do a bit of extra plumbing work before we can utilise MapSearchControl.

We have already seen some basic C# code behind ASP pages that proxy the search and advertising queries. However, being a web based control you are not limited to working solely with Microsoft on the server side. In this article, we will achieve the same result using PHP.

The need for proxy pages

What happens when you enter something in the 'what' box on a MapSearchControl?

  1. MapSearchControl formulates a HTTP query. It wants to make this to http://virtualearth.msn.com/search.ashx, but can't due to cross site scripting restrictions (search results from this page are actually JavaScript).
  2. Instead, MapSearchControl queries a proxy page that you specify, exactly as if you supported the same interface as the aforementioned search.ashx.
  3. Your proxy page makes the request to search.ashx, captures the result, and returns it as output.
  4. MapSearchControl takes this output and renders the search results on the control.

The steps followed when MapSearchControl wants to render location-specific advertising content are pretty much the same, with the exception of what URL it queries.

Including MapSearchControl in an HTML page.

We will start once again with a basic page containing the MapSearchControl widget.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>Sample 1</title>
    <link href="VE_MapSearchControl.css" type="text/css" rel="stylesheet"/>
    <script src="VE_MapSearchControl.js">
    </script>
</head>
<script>
    var map = null;
    function MyOnLoad()
    {
        map = VE_MapSearchControl.Create(
                  47.6, -122.33, 12, 'r', "absolute", 
                  10, 10, 600, 500, 
                  "what.php", 
                  "ads.php");

        document.getElementById("contents").appendChild(map.element);
    }
</script>
<body onLoad="MyOnLoad()">
    <div id="contents" style="font-size:10pt">
    </div>
</body>
</html>

The only difference here from the C# implementation is that we now specify two php files as arguments when we create the MapSearchControl.

Implementing what.php

Remember the job of these proxies is to take a set of submitted HTTP variables, pass them on to a particular URL, then collect the reply from that URL and return it as output. Here is how I have implemented the what.php page:


<?
    include_once("queryMsnSearch.php");
    $expectedArgs = array("a", "b", "c", "d", "e", "f", "g", "i", "r");
    $searchUrl = "http://virtualearth.msn.com/search.ashx";
    print queryMsnSearch($expectedArgs, $searchUrl);
?>

This page is quite simple. First, we include a file containing our helper function "queryMsnSearch". We then declare $expectedArgs, which is a list of HTTP arguments that we expect to be passed to us. If any of these arguments are presented to what.php, we'll pass them on to $searchUrl, collect the results, and print them out.

Implementing 'queryMsnSearch'

I have generalised most of the functionality into queryMsnSearch, since the only thing that changes between what.php and ads.php is our list of $expectedArgs and $searchUrl. Let's take a look at this function:


function queryMsnSearch($expectedArgs, $url)
{
    $queryItems = array();

    foreach ($expectedArgs as $p)
        if (isset($_REQUEST[$p]) && strlen($_REQUEST[$p]) > 0)
            array_push($queryItems, "$p=" . $_REQUEST[$p]);

    if (count($queryItems) < 1)
        return;

    $queryString = join("&", $queryItems);

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $queryString);

    $content = curl_exec($ch);

    curl_close($ch);

    return $content;
}

We shall step through this line by line, because the code is fairly terse. First, we take two arguments as described above.


function queryMsnSearch($expectedArgs, $url)

The next thing we want to do is see how many of the $exectedArgs were actually passed to our PHP page. We want to gather them up in preparation for passing them to the $url.


foreach ($expectedArgs as $p)
	if (isset($_REQUEST[$p]) && strlen($_REQUEST[$p]) > 0)
		array_push($queryItems, "$p=" . $_REQUEST[$p]);

We achieve this by checking whether any of our $expectedArgs are defined in the $_REQUEST hash. $_REQUEST is a global hash containing all GET and POST HTTP keys and their values (if you ever need to differentiate between GET and POST variables, you can examine the global hashes $_GET and $_POST respectively).

Having found one of our expectedArgs, we push a string of the form "key=value" onto the array $queryItems. We will need the arguments in this key=value form for later anyway, so we may as well start constructing the strings now.


if (count($queryItems) < 1)
     return;

If we found none of our $expectedArgs in $_REQUEST, we simply return nothing.


$queryString = join("&", $queryItems);

Now we wish to build the query string that will be sent to the remote website. This is simply the key=value strings in the queryItems array joined together with "&" symbols. The join() function will do exactly this.


$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $queryString);

Now we are only left with making the query to the remote website and returning its contents. The easiest way to do this is through the Curl library, which is a standard extension to PHP.

After initialising the $ch variable, we simply set a number of options describing what we'd like to do with it. Firstly, we set the URL we'd like to access (CURLOPT_URL). We then state that we'll be making a POST submission to this URL (CURLOPT_POST). Finally, we pass the string we'd like to POST (CURLOPT_POSTFIELDS). Pretty easy, eh?


$content = curl_exec($ch);
curl_close($ch);

Having set the required variables, we now tell Curl to execute this query and collect the response, then tidy up the Curl variable by closing it. We aren't so worried about trapping errors here, since if we feed the Virtual Earth control anything unexpected it will politely inform the user that an error occurred on our behalf.

Implementing ads.php

Our proxy for ads.php is now only a very minor code change from what.php


<?
    include_once("queryMsnSearch.php");
    $expectedArgs = array("a", "b", "c", "d");
    $searchUrl = "http://virtualearth.msn.com/Ads.ashx";
    print queryMsnSearch($expectedArgs, $searchUrl);
?>

Conclusion

Web 2.0 has ushered in a new era of platform independence. Not only do we have a great deal of flexibility on the client side, but we can also leverage a variety of technologies on the server side, since so much of our application can now live inside the browser. This means that the millions of PHP users on the server side can enjoy fantastic Web 2.0 innovations like Virtual Earth without switching technologies.

Article contributed by Luke Burton. Have you got something to contribute?

Copyright 2009. Sponsored by nsquared.   |  Terms Of Use  |  Privacy Statement
Content on this site is generated from the developer community and shared freely for your enjoyment and benefit. This site is run independently of Microsoft and does not express Microsoft's views in any way.