pass by reference

When you call a function normally within PHP the variable that you pass is a copy of, for example

function doesNotAlter($alterMe)
{
   $alterMe++;
}
$pleaseAlterMe = 4;
doesNotAlter($pleaseAlterMe);

that will alter the $alterMe variable by adding one to it, but it will only alter it in a local scope of that variable so the actual variable that was passed ($pleaseAlterMe) will not change its value.

But if you pass by reference, then you bring the passing variable into the local scope of that function and thus any alternations to it will act on the variable that has been passed, the only thing that you need to alter is to add a (&), which is very similar to c/c++ which passes the reference to the object (pointer to).

function doesNotAlter(&$alterMe)
{
   $alterMe++;
}
$pleaseAlterMe = 4;
doesNotAlter($pleaseAlterMe);

The & is added to the variable in the passing function parameter list and that is all that it takes to make that remote variable to the function doesNotAlter, to now become a local scoped variable that will alter, so I could change the function name to doesAlter ;).

function doesAlter(&$alterMe)
{
   $alterMe++;
}
$pleaseAlterMe = 4;
doesNotAlter($pleaseAlterMe);
echo $pleaseAlterMe;

So now output would be “5”, because the local variable to the execution of the program is $pleaseAlterMe is first set to 4 and then passed by reference to the doesAlter me function which will actually alter that variable (by adding one to it) and then when you output that variable it will be 5.

CS75 – Assignment 0 – Three Aces Menu

I am doing the Harvards building dynamic websites called CS-75 (also could be called E-75), because someone told me about it and I just thought might as well, it is all learning 🙂 even if allot of it you may already know.

Anyway, to setup the CS75 on my personal laptop ( I am running Ubuntu 10.4) I did a couple of alternations for me so that the local apache instance looks like I am doing it under the top level domain of CS75 :).. so for this assignment Three Aces menu I am calling the website http://threeaces.cs75 on my local host, so to setup that I altered the /etc/hosts file and added

127.0.0.1       threeaces.cs75

so that local IP will also have threeaces.cs75 🙂 and now need to alter apache to allow me to run that as a virtualhost on my laptop, I added the file within

sudo vim /etc/apache2/sites-available/cs75_threeaces

with adding in

<VirtualHost *:80>
       ServerAdmin emailaddress@here.com
       ServerName  threeaces.cs75
       DocumentRoot /var/www/cs75/ass0
       <Directory /var/www/cs75/ass0>
               Options Indexes FollowSymLinks MultiViews
               AllowOverride None
               Order allow,deny
               allow from all
       </Directory>
 
       ErrorLog /var/log/apache2/error.log
       LogLevel warn
       CustomLog /var/log/apache2/access.log combined
 
</VirtualHost>

Which points to the /var/www/cs75/ass0 as the base directory for this assignment :).

I have included the assignments 0 PDF for more information, but basically this assignment is about playing with simpleXML to load in a menu (that you have created from the menu list of the restaurant Three Aces) which is in XML format, you get to pick the format, but here is mine (it has the extras link within the menu->type extras attribute which link to the extras at the bottom of the menu format)

<?xml version="1.0" encoding="ISO-8859-1"?>
<menu>
	<type name="Pizzas" extras="Extra Cheese">
		<item name="Tomato &amp; Cheese">
			<price type="Small">5.50</price>
			<price type="Large">9.75</price>
		</item>
		<item name="Onions">
			<price type="Small">6.85</price>
			<price type="Large">10.85</price>
		</item>
	</type>
	<type name="Salads">
		<item name="Garden">
			<price type="Small">3.50</price>
			<price type="Large">4.50</price>
		</item>
		<item name="Greek">
			<price type="Small">4.50</price>
			<price type="Large">5.50</price>
		</item>
	</type>
	<type name="Grinders">
		<item name="Meatless">
			<price type="Small">4.50</price>
			<price type="Large">4.95</price>
		</item>
		<item name="Hamburger">
			<price type="Small">4.50</price>
			<price type="Large">4.95</price>
		</item>
	</type>
	<type name="Special Dinners">
		<item name="Chicken Wing Dinner">
			<price>7.25</price>
		</item>
		<item name="Gyro Plate">
			<price>7.25</price>
		</item>
	</type>
	<extras name="Extra Cheese">
		<price type="Small">1.25</price>
		<price type="Large">1.85</price>
	</extras>
</menu>

You can implement it anyway that you want to, but to not have two same names of the food for different sizes. So that is why I have a the item name with the price list(s) underneath. It is only part of the menu that I implemented above.

The next part is to only have part of the menu on the main home page, so I am just displaying the main types of food options, I have included the source file for all of the files in the zip file above.

<form name="types" method="get" action="types.php">
<?php
// display the user the different types of foods that are avaible to order
$xml = simplexml_load_file("menu.xml");
// list the different types on the menu
foreach ($xml->type as $types)
{
	echo "<input type=\"radio\" name=\"type\" value=\"" .$types->attributes()->name .  "\"/>".$types->attributes()->name . "<br/>" ;
}
?>
<input type="submit"/>
</form>

Once the user has selected the type of food that they want, you next to have display the items of food within that area, so I am passing in the $_GET string from the index page the type of food, so within the types.php I pick up the type and then display all of the food items within that area. I did do some javascript that will make sure that there is a valid integer value within the, also at the start of the code I am pulling any extra values from the menu to populate the extras option.

<form name="types" method="get" action="checkout.php" onsubmit="return checkout()">
<?php
// display the different items within that type area of foods, with also there different prices and sizes
// normal = normal size for the default size
$xml = simplexml_load_file("menu.xml");
$type = $_GET["type"];
echo $type. "<table border=1>";
echo "<input type=\"hidden\" value=\"".str_replace(" ", "@",$type)."\" name=\"type\"/>";
$result = $xml->xpath("//type[@name='$type']");
if (sizeof($result[0]) == 0)
	echo "<br/><br/>Please go back to home, because cannot find that type<br/><br/>";
else
{
	$extras = $result[0]->attributes()->extras;
	if (strlen($extras) > 0)
	{
		$resultExtras = $xml->xpath("//extras[@name='$extras']");
		if (sizeof($resultExtras[0]) > 0)
		{
			$extraName = $resultExtras[0]->attributes()->name;
			for ($i=0; $i < sizeof($resultExtras[0]); $i++)
			{
				$thePrice =  $resultExtras[0]->price[$i];
				$theType = ($resultExtras[0]->price[$i]->attributes()->type ? $resultExtras[0]->price[$i]->attributes()->type : "Normal");
				$theExtras[$i] = array((string)$thePrice, (string)$theType);
			}
		}
	}
	foreach ($result[0] as $items)
	{
		echo "<tr><td>".$items->attributes()->name."</td><td>Price</td><td>Quantity</td>";
		if (strlen($extras)) echo "<td>$extras</td>";
		echo "</tr>";
		for ($i=0; $i < sizeof($items->children()); $i++)
		{
			echo "<tr><td>". $items->price[$i]->attributes()->type . 
				"</td><td>" . $items->price[$i].
				"</td><td><input type=\"text\" size=\"1\" value=\"\" name=\"".str_replace(" ", "@",$items->attributes()->name)."_".$i."\"/></td>";
			if (strlen($extras))
			{
				foreach ($theExtras as $typeCheck)
				{
					if ($typeCheck[1] == $items->price[$i]->attributes()->type)
					{
						echo "<td><input type=\"checkbox\" name=\"".str_replace(" ","_",$extras)."_".$i."\" value=\"".str_replace(" ", "@",$items->attributes()->name)."_".$i."\"/></td>";
						break;
					}
				}
			}
			echo "</tr>";
		}
	}
}?>
</table>
<input type="submit" name="submit"/>
</form>

Because there is a basket of items that the customer will like to buy, I am also storing the basket details within the $_SESSION within a multi array, the array looks like something like

Array
(
    [Pizzas] => Array
        (
            [Tomato  Cheese] => Array
                (
                    [Small] => Array
                        (
                            [price] => 5.5
                            [quantity] => 6
                        )
                )
        )
)

So in the next page, checkout.php I need to update the basket details and also then display what the customer has already picked (with giving the options to update the quantity of goods and also to remove them if they did not want it), the first part loads the new item(s) into the basket and the second part of the code below will display the basket to the user, and call the update.php file if any quantities/remove of items need to be done.

<form name="types" method="get" action="update.php">
<table>
<?php
// display the updated details of the order with the new order addon's 
// and also any previous details of the order, if you want to place the order
// click the order now link below.
$xml = simplexml_load_file("menu.xml");
// load basket from the session
$basket = $_SESSION["basket"];
$type = $_GET["type"];
$type = str_replace("@", " ", $type);
 
$result = $xml->xpath("//type[@name='$type']");
if (sizeof($result[0]) == 0)
{
	if (strlen($type) > 0)
		echo "<br/><br/>Please go back to home, because cannot find that type<br/><br/>";
}
else
{
	$extras = $result[0]->attributes()->extras;
	if (strlen($extras) > 0)
	{
		$resultExtras = $xml->xpath("//extras[@name='$extras']");
		if (sizeof($resultExtras[0]) > 0)
		{
			$extraName = $resultExtras[0]->attributes()->name;
			for ($i=0; $i < sizeof($resultExtras[0]); $i++)
			{
				$newname = str_replace(" ", "_" , $extras) . "_" . $i;
				$$newname = $_GET[$newname];
				if (strlen($$newname) > 0)
				{
					$thePrice =  $resultExtras[0]->price[$i];
					$theType = ($resultExtras[0]->price[$i]->attributes()->type ? $resultExtras[0]->price[$i]->attributes()->type : "Normal");
					$theExtras[$i] = array($$newname, (string)$thePrice, (string)$theType);
				}
			}
		}
	}
 
	foreach ($result[0] as $items)
	{
		for ($i=0; $i < sizeof($items->children()); $i++)
		{
			$newname = $items->attributes()->name."_".$i;
			$newname = str_replace(" ", "@", $newname);
			$$newname = $_GET[$newname];
			if ($$newname > 0)
			{
				$priceType = $items->price[$i]->attributes()->type;
				if (strlen($priceType) <=1)
					$priceType = "Normal";
				$extraPrice = 0;
				foreach ($theExtras as $extraAdd)
				{
					if ($extraAdd[0] == $newname)
						$extraPrice = $extraAdd[1];
				}
				$basket[(string)$type][(string)$items->attributes()->name][(string)$priceType . ($extraPrice > 0 ? (string)" (".$extras.")" : "")] = 
							array("price" =>(float) ($items->price[$i]) + (float)$extraPrice, 
									"quantity" => (int)($$newname 
										+ $basket[(string)$type][(string)$items->attributes()->name][(string)$priceType]["quantity"]) );
				// going with the theory of adding to the previous quantity
				echo "Have added " . $type . " (". $items->attributes()->name . " " . $priceType . ")<br/>";
			}
		}
	}
	$_SESSION["basket"] = $basket;
}
// lets print out the basket in a form of the user to confirm and also remove / alter the quantity
$totalPrice = 0;
echo "<table border=1><tr><td width=50>Type</td><td width=50>Item</td><td width=50>Size</td><td>Price</td><td>Quantity</td><td>Total Price</td><td>Remove</td></tr>";
foreach ($basket as $type => $typevalue)
{
	echo "<tr><td colspan=\"6\">$type</td>";
	foreach ($typevalue as $item => $itemvalue)
	{
			echo "<tr><td></td><td colspan=\"5\">$item</td></tr>";
			foreach ($itemvalue as $size => $sizevalue)
			{
				$namevalue = $type."_".$item."_".$size;
				$namevalue = str_replace(" ","@",$namevalue);
				echo "<tr><td></td><td></td><td>$size</td>";
				echo "<td>&pound;".number_format($sizevalue["price"],2) . "</td><td><input size=5 value=" . $sizevalue["quantity"]  . " name=\"".$namevalue."_quantity\"/></td><td>&pound;".number_format($sizevalue["price"] * $sizevalue["quantity"],2) ."</td><td><input type=\"checkbox\" name=\"".$namevalue."_remove\"/></td></tr>";
				$totalPrice +=$sizevalue["price"] * $sizevalue["quantity"];
			}
	}
	echo "</tr>";
}
echo "</table>";
 
echo "The total price is &pound;".number_format($totalPrice,2)."<br/>";
?>
</table>
<input type="submit"/>
</form>

here is the update.php, this will update the basket from the requested action from the above php file.

<?php
// this will update the session basket from the checkout.php page
session_start();
$xml = simplexml_load_file("menu.xml");
// load basket from the session
$basket = $_SESSION["basket"];
foreach ($_GET as $key => $value)
{
	$key = str_replace("@", " ", $key);
	list($type, $item, $size,$command) =  split("_", $key);
	if ($command== "quantity" && $value > 0)
	{
		$basket[$type][$item][$size]["quantity"] = $value;
	}
	elseif ($command =="remove")
	{
		unset($basket[$type][$item][$size]);
		// clean out the type -> item if none more left
		if (sizeof($basket[$type][$item])==0)
			unset($basket[$type][$item]);
		// clean out the type if none left
		if (sizeof($basket[$type])==0)
			unset($basket[$type]);
	}
}
$_SESSION["basket"] = $basket;
// redirect back to checkout.php
header('Location: checkout.php');
?>

and redirects back to checkout.php.

The last page displays the order in total and also says thanks very much for the order :).. and deletes the basket details :).

<table>
<?php
// display the order details and also thanks for placing the order
$basket = $_SESSION["basket"];
// lets print out the basket in a form of the user to confirm and also remove / alter the quantity
$totalPrice = 0;
echo "<table border=1><tr><td width=50>Type</td><td width=50>Item</td><td width=50>Size</td><td>Price</td><td>Quantity</td><td>Total Price</td></tr>";
foreach ($basket as $type => $typevalue)
{
	echo "<tr><td colspan=\"6\">$type</td>";
	foreach ($typevalue as $item => $itemvalue)
	{
			echo "<tr><td></td><td colspan=\"5\">$item</td></tr>";
			foreach ($itemvalue as $size => $sizevalue)
			{
				echo "<tr><td></td><td></td><td>$size</td>";
				echo "<td>&pound;".number_format($sizevalue["price"],2) . "</td><td>" . $sizevalue["quantity"]  . "</td><td>&pound;".number_format($sizevalue["price"] * $sizevalue["quantity"],2) ."</td></tr>";
				$totalPrice +=$sizevalue["price"] * $sizevalue["quantity"];
			}
	}
	echo "</tr>";
}
echo "</table>";
 
echo "The total price is &pound;".number_format($totalPrice,2)."<br/>";
session_destroy();
?>
</table>

The file above does include all of the php files from the above comments and the PDF of the assignment. You can also view the assignment live on my CS – 75 assignment 0 test area.

PHP SOAP server and .NET Client

On a previous post, it was the other way around a .NET SOAP server and a PHP client and this one is a PHP SOAP server talking to a .NET client. I am using a similar PHP SOAP Server output as before, but having to alter the return type to a complexType instead of a normal PHP SOAP server type.

The basic WSDL generation is very similar to the previous SOAP post, apart from parameter passed the Zend_Soap_AutoDiscover which is “Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex”, which creates the start of the newer return complexTypes.

if(isset($_GET['wsdl'])) {
    $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex');
    $autodiscover->setClass('QuoteOfTheDay');
    $autodiscover->handle();

here is the new return type for the function to return, it is a class that has public variable that is a string (you define as “” which sets up a string in PHP)

// the return class type
class theQuote {
// have a return value of type string
/** @var string */
  public $getTheQuote="";
}

and then just alter the phpDoc notation for the auto discovery with the Zend SOAP to create a return of theQuote class as above and alter the return variable to the class., below I have included the new WSDL output generated.

  /* phpdoc notation to return a complex type (a class) */
  /**
  * @return theQuote
  */
 function getQuote($quote) {
    $theQuoteR = new theQuote();
    /* just encase the string is in uppercase*/
    $symbol = strtolower($quote);
    /* if there is a quote for the day requested */
    if (isset($this->quotes[$quote])) {
      $theQuoteR->getTheQuote=$this->quotes[$quote];
      return $theQuoteR;
    } else {
      /* else error with default response*/
      $theQuoteR->getTheQuote=$this->quotes["monday"];
      return $theQuoteR;
    }
  }

here is the full source code for the PHP SOAP server

<?php
/* setup the including path for the zend library framework */
ini_set('include_path', '/usr/share/php/libzend-framework-php/');
 
//****************************************************
// Zend Framework 1.8
include_once 'Zend/Loader/Autoloader.php';
require_once "Zend/Soap/Server.php";
require_once "Zend/Soap/AutoDiscover.php";
$loader = Zend_Loader_Autoloader::getInstance();
$loader->setFallbackAutoloader(true);
$loader->suppressNotFoundWarnings(false);
//****************************************************
 
if(isset($_GET['wsdl'])) {
    $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex');
    $autodiscover->setClass('QuoteOfTheDay');
    $autodiscover->handle();
} else {
    $soap = new Zend_Soap_Server("http://localhost/projects/webservice/zend_soap_server_net.php?wsdl"); // this current file here
    $soap->setClass('QuoteOfTheDay');
    $soap->handle();
}
 
// the return class type
class theQuote {
// have a return value of type string
/** @var string */
  public $getTheQuote="";
}
 
class QuoteOfTheDay {
 
  /* the quotes to be used from within the function getQuote */
  private $quotes = array("monday" => "Monday's child is fair of face", "tuesday"=>"Tuesday's child is full of grace","wednesday" =>"Wednesday's child is full of woe",
  "thursday" =>"Thursday's child has far to go", "friday" => "Friday's child is loving and giving", "saturday" =>"Saturday's child works hard for a living",
  "sunday" =>"But the child who is born on the Sabbath Day - Is bonny and blithe and good and gay");  
 
  /* phpdoc notation to return a complex type (a class) */
  /**
  * @return theQuote
  */
 function getQuote($quote) {
    $theQuoteR = new theQuote();
    /* just encase the string is in uppercase*/
    $symbol = strtolower($quote);
    /* if there is a quote for the day requested */
    if (isset($this->quotes[$quote])) {
      $theQuoteR->getTheQuote=$this->quotes[$quote];
      return $theQuoteR;
    } else {
      /* else error with default response*/
      $theQuoteR->getTheQuote=$this->quotes["monday"];
      return $theQuoteR;
    }
  }
}
 
?>

Here is the newer WSDL output (snipped) from the new PHP SOAP server that creates a complexType return type for the .NET environment to be able to use.

<types>
-
<xsd:schema targetNamespace="http://localhost/projects/webservice/zend_soap_server_net.php">
-
<xsd:complexType name="theQuote">
-
<xsd:all>
<xsd:element name="getTheQuote" type="xsd:string"/>
</xsd:all>
</xsd:complexType>
</xsd:schema>
</types>
-
<portType name="QuoteOfTheDayPort">
-
<operation name="getQuote">
<documentation>@return theQuote</documentation>
<input message="tns:getQuoteIn"/>
<output message="tns:getQuoteOut"/>
</operation>
</portType>

Here is the .NET client

Since we have a new WSDL generation, will need to generate a newer DLL that will allow the .NET to use the class structure. Please note I am using mono as my .NET environment, so if you are using .NET in Windows then you could either include the WSDL within the Visual Studio or use very similar commands as below.

wsdl2 http://localhost/projects/webservice/zend_soap_server_net.php?wsdl
gmcs QuoteOfTheDayService.cs /t:library /r:System.Web.Services

which will generate the QuoteOfTheDayService.dll and with the code below

using System;
 
namespace codingfriendssoap
{
    class Test_Php_Soap
    {
 
static public void Main()
{
  QuoteOfTheDayService quoteService = new QuoteOfTheDayService();
 
  Console.WriteLine("mondays quote " + quoteService.getQuote("monday").getTheQuote);
}
    }
}

to compile that you once again I am using mono so these are the commands that I am using, but with .NET framework in Windows the commands may be different to compile the program e.g. (mcc)

gmcs web_service_client_php.cs /r:QuoteOfTheDayService.dll

the /r includes the dll generated of the above generated PHP SOAP server this is the output

mondays quote Monday's child is fair of face

here is the XML response from the PHP SOAP server, which includes getTheQuote return wrapped around the theQuote class.

<SOAP-ENV:Body><ns1:getQuoteResponse><return xsi:type="ns1:theQuote"><getTheQuote xsi:type="xsd:string">Monday's child is fair of face</getTheQuote></return></ns1:getQuoteResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

Soap Client calling .NET Web service

Since Web Service Description Language (WSDL) is to be used between language so that a PHP program can call a .NET web service or the other way around. So here we create a PHP Soap Client that will call the .Net Web service created from a previous post (Web Service).

As before with the soap client for a PHP server (PHP server previous post) we just need to link to the WSDL output of the .NET web service as below

$client = new SoapClient("http://localhost/csharp/web_service.asmx?wsdl")

so if you tried to call the function / web method.

echo $client->Add(2,3);

The error would be

Catchable fatal error: Object of class stdClass could not be converted to string in

this does not mean that much or really point to where the problem is, the actual problem is not the passing parameters (even through they are in a complex format) but the return from the $client object, because .NET returns a array object as such, so if you

print_r($client->Add(2,3));

the output would be

stdClass Object ( [AddResult] => 0 )

thus to gain access to the actual result you would need to reference the first part of the returned array named AddResult

echo $client->Add(2,3)->AddResult;

but the parameters being passed are not!!, so if you look at the WSDL file generated from the .NET web service it has

      <xs:element name="Add">
        <xs:complexType>
          <xs:sequence>
            <xs:element minOccurs="1" maxOccurs="1" name="a" type="xs:int"/>
            <xs:element minOccurs="1" maxOccurs="1" name="b" type="xs:int"/>
          </xs:sequence>
        </xs:complexType>

complex types and thus if you try to pass in parameters to the function Add (as the web service client does) as a normal PHP way it is not passing in the numeric values from the php code, the reason for this PHP has to specify the actual names of the passing parameters within a array.. as

print_r( $client->Add(array("a" => "5", "b" =>"2")));

which displays a better result for us..

stdClass Object ( [AddResult] => 7 )

So the best way to remember is that if you are calling a .NET WSDL web service, then always pass in a array with the WSDL function parameters within a index array and the return result is always function name + Result (AddResult)

echo $client->Add(array("a" => "5", "b" => "2"))->AddResult;

So here is the full code to pull back the code with some debugging information.

<?php
  // create a connection to the local host mono .NET pull back the wsdl to get the functions names
  // and also the parameters and return values
  $client = new SoapClient("http://localhost/csharp/web_service.asmx?wsdl",
    array(
      "trace"      => 1,		// enable trace to view what is happening
      "exceptions" => 0,		// disable exceptions
      "cache_wsdl" => 0) 		// disable any caching on the wsdl, encase you alter the wsdl server
  );
 
  // get a response from the WSDL zend server function getQuote for the day monday
  print_r( $client->Add(array("a" => "5", "b" =>"2")));
 
  // display what was sent to the server (the request)
  echo "<p>Request :".htmlspecialchars($client->__getLastRequest()) ."</p>";
  // display the response from the server
  echo "<p>Response:".htmlspecialchars($client->__getLastResponse())."</p>";
?>

SOAP – the client with requested and responses

From the SOAP server page here, this is the client that can connect to the PHP server page, the client uses the WSDL (Web Services Description Language) part of the server to find out the functions that are available.

To connect to the server WSDL, you create a new Soap Client with the WSDL as the URI link

  $client = new SoapClient("http://localhost/projects/webservice/zend_soap_server.php?wsdl")

in this example I am passing in some variables for debugging

    array(
      "trace"      => 1,		// enable trace to view what is happening
      "exceptions" => 0,		// disable exceptions
      "cache_wsdl" => 0) 		// disable any caching on the wsdl, encase you alter the wsdl server

so to only get a response from the server you just need to call the function on the server like it is running within the local PHP environment

  echo $client->getQuote("monday");

What is happening behind the scenes, is that the client has created a XML request and send that to the server, the server processes the request and responds with a XML response to the client, the client then pulls out the result from the XML response, here is how with using the soap client trace debugging out

  // display what was sent to the server (the request)
  echo "<p>Request :".htmlspecialchars($client->__getLastRequest()) ."</p>";
  // display the response from the server
  echo "<p>Response:".htmlspecialchars($client->__getLastResponse())."</p>";

here is the full source code, save as zend_soap_client.php (it is not using any zend framework, but the server is)

<?php
  // create a connection to the local host zend soap server, pull back the wsdl to get the functions names
  // and also the parameters and return values
  $client = new SoapClient("http://localhost/projects/webservice/zend_soap_server.php?wsdl",
    array(
      "trace"      => 1,		// enable trace to view what is happening
      "exceptions" => 0,		// disable exceptions
      "cache_wsdl" => 0) 		// disable any caching on the wsdl, encase you alter the wsdl server
  );
 
  // get a response from the WSDL zend server function getQuote for the day monday
  echo $client->getQuote("monday");
 
  // display what was sent to the server (the request)
  echo "<p>Request :".htmlspecialchars($client->__getLastRequest()) ."</p>";
  // display the response from the server
  echo "<p>Response:".htmlspecialchars($client->__getLastResponse())."</p>";
?>

and here is the output on the web page

Monday's child is fair of face
 
Request :<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ns1="http://localhost/projects/webservice/zend_soap_server.php" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><ns1:getQuote><quote xsi:type="xsd:string">monday</quote></ns1:getQuote></SOAP-ENV:Body></SOAP-ENV:Envelope>
 
Response:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ns1="http://localhost/projects/webservice/zend_soap_server.php" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><ns1:getQuoteResponse><return xsi:type="xsd:string">Monday's child is fair of face</return></ns1:getQuoteResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

SOAP – Server

SOAP is the acronym for Simple Object Access Protocol, it uses the web and XML for a client to find out what functions are available on a server using the WSDL (Web Server Description Language) (this is also in XML format) and once the client knows what functions are available and also what parameters can be passed, the client is then able to call the function and gain the response from the server.

I am using the Zend Framework to auto generate the WSDL file since there does not appear to be at present a function within the PHP standard framework to create one, I have included below how to install the Zend framework. Once you have installed the framework you need to either alter the php.ini file to tell it where the framework is or within the .php file for your server include details on where the framework is within that and include the relevant files.

Below I have told the php runtime within the .php file where the zend library framework is by using ini_setup include path settings.

/* setup the including path for the zend library framework */
ini_set('include_path', '/usr/share/php/libzend-framework-php/');
/* and include the zend soap files */
require_once 'Zend/Soap/AutoDiscover.php';
require_once "Zend/Soap/Server.php";

with including the auto discover (the WSDL generator part of the zend framework that we are wanting) and the server php files.

For the auto discover to work you need to use the php doc notation so that the WSDL can be generated correctly, so for example if you was using a function that was taking in a string and returning a string as in the main example below, you would use something like this (where the php doc is /** start)

  /**
   * @param string $quote
   * @return string
  */
  function getQuote($quote) {

To generate WSDL file I am using the parameter within the URL (?wsdl) and thus if that is present auto generate the WSDL file else act as the server, so

/* if the client is requesting the WSDL file (the web service definition language) */
if(isset($_GET['wsdl'])) {
   // use the zend soap autodiscover function to create the wsdl file from using the phpdoc info from the class QuoteOfTheDay */
    $autodiscover = new Zend_Soap_AutoDiscover();
    $autodiscover->setClass('QuoteOfTheDay');
    $autodiscover->handle();

with using the Zend_Soap_AutoDiscover class and then setting up the class that you want to expose to the WSDL generator / server and then output (handle) the WSDL request.

Here is the full code

<?php
 
/* setup the including path for the zend library framework */
ini_set('include_path', '/usr/share/php/libzend-framework-php/');
/* and include the zend soap files */
require_once 'Zend/Soap/AutoDiscover.php';
require_once "Zend/Soap/Server.php";
 
/* this is the class to be *expose* to the SOAP interface */
class QuoteOfTheDay {
 
  /* the quotes to be used from within the function getQuote */
  private $quotes = array("monday" => "Monday's child is fair of face", "tuesday"=>"Tuesday's child is full of grace","wednesday" =>"Wednesday's child is full of woe",
  "thursday" =>"Thursday's child has far to go", "friday" => "Friday's child is loving and giving", "saturday" =>"Saturday's child works hard for a living",
  "sunday" =>"But the child who is born on the Sabbath Day - Is bonny and blithe and good and gay");  
 
/* you have to use the phpDoc format for the zend soap auto discover to pick up the parameters and return types */
  /**
   * @param string $quote
   * @return string
  */
  function getQuote($quote) {
    /* just encase the string is in uppercase*/
    $symbol = strtolower($quote);
    /* if there is a quote for the day requested */
    if (isset($this->quotes[$quote])) {
      return $this->quotes[$quote];
    } else {
      /* else error */
      throw new SoapFault("Server","Unknown Symbol '$quote'.");
    }
  }
}
 
/* if the client is requesting the WSDL file (the web service definition language) */
if(isset($_GET['wsdl'])) {
   // use the zend soap autodiscover function to create the wsdl file from using the phpdoc info from the class QuoteOfTheDay */
    $autodiscover = new Zend_Soap_AutoDiscover();
    $autodiscover->setClass('QuoteOfTheDay');
    $autodiscover->handle();
} else {
  // otherwise I am the server in question, setup a new soap server with the a handle on the class QuoteOfTheDay 
    $soap = new Zend_Soap_Server("http://localhost/projects/webservice/zend_soap_server.php?wsdl"); // this current file here
    $soap->setClass('QuoteOfTheDay');
    $soap->handle();
}
?>

Save that as the zend_soap_server.php within your local PHP web serving directory you need to have the zend framework installed, below may help (within the php.ini there will either a extension list normally within a Windows setup)

in Linux (Ubuntu/Kubuntu etc) I installed the zend frame work for php by

aptitude install libzend-framework-php

or on windows this URL may be of use ? installing zend framework on windows

This is the output of the XML for the WSDL generation, it would be the output of a URL similar to http://localhost/zend_soap_server.php?wsdl

<definitions name="QuoteOfTheDay" targetNamespace="http://localhost/projects/webservice/zend_soap_server.php">
-
<types>
<xsd:schema targetNamespace="http://localhost/projects/webservice/zend_soap_server.php"/>
</types>
-
<portType name="QuoteOfTheDayPort">
-
<operation name="getQuote">
<documentation>@param string $quote</documentation>
<input message="tns:getQuoteIn"/>
<output message="tns:getQuoteOut"/>
</operation>
</portType>
-
<binding name="QuoteOfTheDayBinding" type="tns:QuoteOfTheDayPort">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
-
<operation name="getQuote">
<soap:operation soapAction="http://localhost/projects/webservice/zend_soap_server.php#getQuote"/>
-
<input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost/projects/webservice/zend_soap_server.php"/>
</input>
-
<output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost/projects/webservice/zend_soap_server.php"/>
</output>
</operation>
</binding>
-
<service name="QuoteOfTheDayService">
-
<port name="QuoteOfTheDayPort" binding="tns:QuoteOfTheDayBinding">
<soap:address location="http://localhost/projects/webservice/zend_soap_server.php"/>
</port>
</service>
-
<message name="getQuoteIn">
<part name="quote" type="xsd:string"/>
</message>
-
<message name="getQuoteOut">
<part name="return" type="xsd:string"/>
</message>
</definitions>

Web page inputs and insert into database – Part 2

As from here, where I outlined the javascript, html part of the exercised here is the php and mysql parts of the problem.

Here is the table that I created within MySQL

CREATE TABLE `User` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `firstName` VARCHAR(50) DEFAULT NULL,
  `lastName` VARCHAR(50) DEFAULT NULL,
  `email` VARCHAR(50) DEFAULT NULL,
  `phoneNum` VARCHAR(20) DEFAULT NULL,
  `guid` VARCHAR(36) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

As from my other post about MySQL triggers, so to find out more information about why please check out that page, and here is the trigger that I created for the above table.

CREATE TRIGGER User_insert 
BEFORE INSERT ON USER 
FOR EACH ROW 
BEGIN 
        SET NEW.guid = uuid(); 
END

The reason why I created this for was because within the exercise they asked “would forward planning, i.e. adding a unique code to the user details that could be used to validate their email address”, which by using a uuid, you can use that as a unique code.

so the only part left is the PHP that will connect to the database and also sanitize the inputs within sql insert. So created a class that has a constructor that will open a database connection to a database

      private $dbLink;
      /* connect to the database*/
      function __construct($host = "localhost", $user = "user", $password = "password", $database = "database")
      {
	$this->dbLink = mysql_connect($host, $user, $password);
	if (!$this->dbLink) die("no database present");
	if (!mysql_select_db($database, $this->dbLink))
	  die("no database within the database");
      }

Here I try to sanitize the insert string so that it will use the mysql_real_escape_string, this will use a php function that helps with SQL injections, also I am using the stripslashes and then trim the string to make sure that there is no white spaces (or any valid text left).

      /* santize the input for a mysql database */
      public function santizeInput($theStr)
      {
	return trim(stripslashes(mysql_real_escape_string($theStr)));
      }

and then to insert the data, just use the mysql_query and the sanitize function above to insert data into the database.

      /* insert the data into the database */
      public function insertData($sqlData)
      {
	mysql_query($this->santizeInput($sqlData), $this->dbLink) or die("Error inserting data");
      }

And here is where I get the data from the form post from the web page and then double sanitize the data and then create a database link, which in-turn use to insert the data.

    // could use foreach loop $_POST inputs, but I personally prefer to pick them up.
    $firstname = $_POST['firstname'];
    $secondname =$_POST['secondname'];
    $email = $_POST['email'];
    $phonenumber = $_POST['phonenumber'];
 
    /* could do additional checks on input incase it is sent via backend POST and not via the webpage,  could do with regular expression as well ? */
    $db = new databaseAccess("localhost", "username", "password", "database");
    /* can santize the inputs to make sure that there is some data to "play" with */
    $firstname = $db->santizeInput($firstname);
    $secondname =$db->santizeInput($secondname);
    $email =$db->santizeInput($email);
    $phonenumber = $db->santizeInput($phonenumber);
 
    if (checkLength($firstname) && checkLength($secondname))
    {	
	$sql = "insert into User (firstname, lastname, email, phoneNum) values (\"$firstname\",\"$secondname\",\"$email\", \"$phonenumber\")";
	$db->insertData($sql);
	echo "Data inserted";
    }

I did write within the exercise that since someone may try and post the data to the server within using the webpage (naughty people that they are!!) you could also check the inputs again for there data validity.

Here is the full code for the web page in total.

<?php
    class databaseAccess
    {
      private $dbLink;
      /* connect to the database*/
      function __construct($host = "localhost", $user = "user", $password = "password", $database = "database")
      {
	$this->dbLink = mysql_connect($host, $user, $password);
	if (!$this->dbLink) die("no database present");
	if (!mysql_select_db($database, $this->dbLink))
	  die("no database within the database");
      }
 
      /* disconnect */
      function __destruct()
      {
	if (!$this->dbLink) mysql_close($this->dbLink);
      }
 
      /* santize the input for a mysql database */
      public function santizeInput($theStr)
      {
	return trim(stripslashes(mysql_real_escape_string($theStr)));
      }
 
      /* insert the data into the database */
      public function insertData($sqlData)
      {
	mysql_query($this->santizeInput($sqlData), $this->dbLink) or die("Error inserting data");
      }
    }
 
    function checkLength($theStr)
    {
      if (strlen($theStr) > 0) 
	return true; 
      else 
	return false;
    }
 
    // could use foreach loop $_POST inputs, but I personally prefer to pick them up.
    $firstname = $_POST['firstname'];
    $secondname =$_POST['secondname'];
    $email = $_POST['email'];
    $phonenumber = $_POST['phonenumber'];
 
    /* could do additional checks on input incase it is sent via backend POST and not via the webpage, not sure if SOAP are looking for that as well ? 
      could do with regular expression as well ? */
    $db = new databaseAccess("localhost", "User", "PW", "Test");
    /* can santize the inputs to make sure that there is some data to "play" with */
    $firstname = $db->santizeInput($firstname);
    $secondname =$db->santizeInput($secondname);
    $email =$db->santizeInput($email);
    $phonenumber = $db->santizeInput($phonenumber);
 
    if (checkLength($firstname) && checkLength($secondname))
    {	
	$sql = "insert into User (firstname, lastname, email, phoneNum) values (\"$firstname\",\"$secondname\",\"$email\", \"$phonenumber\")";
	$db->insertData($sql);
	echo "Data inserted";
    }
 
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<script language="javascript">
  /* check the length of the element, focus is none present */
  function lengthCheck(elem, thename)
  {
    if (elem.value.length> 0) 
      return true;
    else
    {
      alert("Please insert the " + thename);
      elem.focus();
    }
  }
 
  /* check a email address, using regular expression */
  function emailChecker(elem)
  {
    var reg = /^[\w\-\.\+]+\@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
    if (elem.value.match(reg))
      return true;
    else
    {
      alert ("Please insert a valid email address");
      elem.focus();
      return false;
    }
  }
 
  /* check against a phone number. a number being between 11-15 numbers*/
  function checkPhone(elem)
  {
    var reg = /^[0-9]{11,15}$/;
    if (elem.value.match(reg))
      return true;
    else
    {
      alert ("Please insert a valid phone number");
      elem.focus();
      return false;
    }
  }
 
  function checkInputs()
  {
    // obtain inputs
    var firstname = document.getElementsByName("firstname").item(0);
    var secondname = document.getElementsByName("secondname").item(0);
    var email= document.getElementsByName("email").item(0);
    var phonenum = document.getElementsByName("phonenumber").item(0);
    /* check the inputs */
    if (lengthCheck(firstname, "first name")) 
      if (lengthCheck(secondname, "second name"))
	if (emailChecker(email))
	  if (checkPhone(phonenum))
	    return true;
    return false;
  }
</script>
</head>
<body>
<form name="input" action="insertData.php" method="post" onSubmit="return checkInputs()">
First Name :
<input type="text" name="firstname"/>
 
Second Name : 
<input type="text" name="secondname"/>
 
Email : 
<input type="text" name="email"/>
 
Phone number : 
<input type="text" name="phonenumber"/>
 
<input type="submit" value="Submit"/>
</form>
</body>
</html>

If you save that as insertData.php then open up within your web-server. You will be able to insert data into a database with some javascript / php checks.