PHPShout : a shoutcast streamer in PHP: Part 3

Warning: This blogpost has been posted over two years ago. That is a long time in development-world! The story here may not be relevant, complete or secure. Code might not be complete or obsoleted, and even my current vision might have (completely) changed on the subject. So please do read further, but use it with caution.
Posted on 24 Mar 2012
Tagged with: [ C ]  [ extension ]  [ icecast ]  [ PHP ]  [ streaming

In the last post, we started with the implementation of the constructor and one method. Next up, let’s do a bunch more.

Task 4: Populating our store

Let’s take a look at the source from this commit: https://github.com/jaytaph/phpshout/blob/8675c69c27675d4e1159a754a634f08fffc07746/shout.c

You see there is something changed in our store on line 40: there is a pointer to a shout structure. This structure is used by the shout library so every time we create a new object store, we should also initialize a shout object. You will see that things have changed in the constructor method on line 85. We fetch the actual store, and create a new shout object by issuing a shout_new(). If something goes wrong, and the shout object is NULL, we throw an exception.

Because we are adding things to our store, we must also make sure these structures gets freed when we don’t need them anymore. So in our shout_object_free function (line 225), we also free our shout structure by calling shout_free().

If you look closely to this file, you will see that something also has changed on line 352. Here we actually set the handler for cloning objects to NULL, meaning we can’t clone the object (try it, it will fail now). This is done because when we need to clone, we should also clone our internal data like the shout structure. Unfortunately, this data is very complex which means we need to copy over all the data, but also duplicating some of this data (because otherwise we end up changing data in two different places, for two different objects, which is the whole point of cloning to get rid of). So instead of dealing with this issue, I’ve decided to not allow cloning at all for the time being.

Also another thing that has changed, is that i’ve implemented a PHP_MSHUTDOWN function on line 386, and added this to the module entry structure on line 409. This will call the shout_shutdown(), to make sure things gets shutdown properly.

Task 5: Add getters.

A lot of the libshout functionality are actually based on getting and setting information, just like “normal” PHP getters and setters. This shout.c file actually has two ways of dealing with getting values which I will show you both.

The first method is the easiest to explain. Take a look at line 194, where there is a get_host PHP_METHOD.

Since getting stuff do not require any php parameters, the zend_parse_parameters_none() will check if there are any. If so, the php method will return false. On line 201, it will load the internal store. Line 202 will return a string value with the host as returned by the shout_get_host() function. This function needs a shout_t structure, which is the one we are saving in our store. From a PHP point of view, nobody needs to know anything about this shout_t structure so all this is done internally. As said, it will return a string with the current host.

Let’s take a quick look at setting info as well. Line 205 creates a set_host method. In this case, we DO need parameters, namely a string from PHP, which is the host we need to set. Line 210 will parse the parameters we get from PHP, makes sure we only get 1 string (that is the “s”), and this information is placed in the “host” and “host_len” variables. Remember I was talking about binary safe strings? The host is just a block of data which may or may not have \0 in them. The host_len is a integer that actually holds the length of the string. If we are really correct, we cannot assume that we can just use the “host” as a normal string because it isn’t. But for now this doesn’t matter, if you want to set a non-binary safe string as a host, that is perfectly fine, but just don’t think it will work that way:

$shout->set_host("Not a binary\x00 safe string");
print $shout->get_host();  #returns: "Not a binary"

Line 215 will actually set the host name by supplying our shout-t structure and the (non-binary-safe) host string. As a result, it will return a status code (the RET_* constants), which are LONG values, hence the RETURN_LONG. Notice that we don’t need to duplicate longs just like we need to do with RETURN_STRING.

Now, back to the getters, as you can see they are pretty easy to write, but in C, writing a function for each getter and for each setter would result in lots of duplicate code. There must be better ways to deal with this. And there is.

On line 95, I’ve added a (static) function called php_shout_get_handler_string. The argument list looks wierd, but again, it’s a ZEND macro:

static void php_shout_get_handler_string(INTERNAL_FUNCTION_PARAMETERS, const char *(*func)(shout_t *)) {

The INTERNAL_FUNCTION_PARAMETERS is just a way to copy all the parameters over from another function. We deal with that later. The next argument is merely a pointer to a function (that returns a string, and needs a pointer shout_t structure). Line 98 is familiar: we check if there aren’t any PHP arguments given to this function. (don’t worry that this is not a PHP_METHOD).

Line 102, will fetch our internal store, and on line 103 we call the function we just supplied by our argument lists (ie: variable functions), together with our shout structure. There is something to consider, namely the fact that these “get”-functions that returns strings, can also return NULL values when nothing has been set. In that case, we like to return a php NULL value, which we do on line 105. Otherwise, we return the actual string on line 107.

This function is a generic getter function for strings, but we must deal with longs and booleans a little bit different (for instance, we must use RETURN_LONG and RETURN_BOOL). So we have multiple getters functions. Notice that we also have a getter for short integers, something that the libshout also can return (for the port numbers), but we convert that data to a long.

On line 147 and below, you see that we actually define the PHP_METHODS for each of these getters. We call the php_shout_get_handler_* function, depending on the type it will return, together with a INTERNAL_FUNCTION_PARAMETER_PASSTHROUGH, which means all the PHP parameters gets passed onto another function. The last parameter is the actuall libshout function that needs to be called. So in a sense we have written lots of getters in just a few lines of code (can maybe be written better and even in less code).

Again, don’t forget to add these functions to the shout_funcs structure on line 260!