Writing a PHP Script that Takes One Protobuf and Returns Another Protobuf

After we’ve generated the PHP classes from the Protocol Buffers, we can now use those classes in a PHP script. The PHP script will take a StoryRequest Protobuf from the iOS client, and respond with a StoryResponse Protobuf.

We’ll call this file story_generator.php and upload it into the story folder on the server (that we’ve created in the last tutorial). Here’s the entire code:

<?
// first include pb_message
require_once('../message/pb_message.php');
 
// now include the generated files
require_once('./pb_proto_StoryRequest.php');
require_once('./pb_proto_StoryResponse.php');
 
//parse the StoryRequest
$received_story_request = new StoryRequest();
$received_story_request->parseFromString($HTTP_RAW_POST_DATA);
 
//the info we want to gather
$name1 = $received_story_request->character1();
$name2 = $received_story_request->character2();
$ammo = $received_story_request->secret_weapon();
if(empty($ammo))
{
	$ammo = "baseball";
}
 
//build the StoryResponse
$story_response = new StoryResponse();
if(empty($name1) || empty($name2) )
{
	$story_response->set_status(FAILED);
	$story_response->set_story("Oops. Both characters need to be named!");
}
else
{
	$story_response->set_status(SUCCESS);
	$story_choice = rand(0, 2);
	$the_story = "";
	if($story_choice == 0)
	{
		$the_story .= 'Once upon a time, ' . $name1 . ' and ' . $name2 . ' went to slay a dragon that started fires everywhere. ';
		$the_story .= 'When they arrived, the dragon spat fire at them. ';
		$the_story .= 'They were backed into a corner. They were both scared. ';
		$the_story .= 'And then suddenly, ' . $name2 . ' had an idea.';
		$the_story .= '\n\n';
		$the_story .= '\"I know, \" said ' . $name2 . '. \"Why don\'t we throw a ' . $ammo . ' at it?\"\n';
		$the_story .= '\"Good idea!\" agreed ' . $name1 . ', so they did.';
		$the_story .= '\n\n';
		$the_story .= 'And it worked! The dragon was defeated and agreed to stop being such a fire hazard. ';
		$the_story .= 'They agreed that they all should get along and be friends. They lived happily ever after.';
	}
	else if($story_choice == 1)
	{
		$the_story .= 'One day, ' . $name1 . ' and ' . $name2 . ' were playing basketball. ';
		$the_story .= 'Unfortunately, the ball got caught in the tree. ';
		$the_story .= 'And then suddenly, ' . $name2 . ' had an idea.';
		$the_story .= '\n\n';
		$the_story .= '\"I know, \" said ' . $name2 . '. \"Why don\'t we throw a ' . $ammo . ' at it?\"\n';
		$the_story .= '\"Good idea!\" agreed ' . $name1 . ', so they did.';
		$the_story .= '\n\n';
		$the_story .= 'And it worked! They got the ball back. ';
		$the_story .= 'They agreed that this was too much fun and decided to play basketball happily ever after.';
	}
	else if($story_choice == 2)
	{
		$the_story .= 'Long ago, in a galaxy far far away, ' . $name1 . ' and ' . $name2 . ' were looking for a ship. ';
		$the_story .= 'A salesman told them that he got a piece of junk they could sell, but they would need to fix it first. ';
		$the_story .= 'They were told that there was a piece missing from the engine and the ship wouldn\'t fly until they found a replacement. ';
		$the_story .= 'And then suddenly, ' . $name2 . ' had an idea.';
		$the_story .= '\n\n';
		$the_story .= '\"I know, \" said ' . $name2 . '. \"Why don\'t we just jam this ' . $ammo . ' in it?\"\n';
		$the_story .= '\"Good idea!\" agreed ' . $name1 . ', so they did.';
		$the_story .= '\n\n';
		$the_story .= 'And it worked! The ship was good as new. ';
		$the_story .= 'They agreed that the ship was awesome and decided to cruise around in it happily ever after.';
	}
	$story_response->set_story($the_story);
}
 
//respond to client
$serialized_string = $story_response->SerializeToString();
print($serialized_string);
 
?>

A few notes:

  1. You first use the require_once calls to make sure the PHP classes you generated are imported for use.
  2. You create a new (and empty) StoryRequest: $received_story_request = new StoryRequest();
  3. Populate it with the content that we sent from the iOS client (remember this tutorial?). We sent the serialized StoryRequest Protobuf from the client side as the HTTP body, so we are now using the special PHP variable $HTTP_RAW_POST_DATA to get those raw bytes on the server side. There might be a better way. Leave me a comment if you are a more experienced PHP programmer.
  4. Now you have a $receive_story_request filled with actual content, you can use the getters defined in the StoryRequest PHP class (that you compiled from the .proto definitions) to get the character names and the weapon.
  5. Now we make a new StoryResponse: $story_response = new StoryResponse();. This is a Protobuf that’s going to be serialized and sent back to the iOS client as the response.
  6. Use the character names and weapon from the StoryRequest, and make a string representing a silly story using those items. Now use the setters for the StoryResponse to fill in the actual content.
  7. And now you can call SerializeToString() on the StoryResponse to, well… serialize it to string.
  8. And finally, print it so the HTTP request from the client side has something to grab as the result.

Next, we’ll finally parse the response on the client side.

PS. When I was trying this, for some reason, the enum stuff never serialize correctly so it always returned the same number. I wasn’t sure if there’s a bug in the library, or it was just me being stupid (probably the latter). But since I wasn’t the one working on the server portion (and they might not even be using PHP), I did not investigate any further. The idea is that no matter what language that the server and client are using, it should still work as long as you pack the Protocol Buffers correctly.

This entry was posted in Protobuf. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *