In the last post, we created a template extension for our shout class. Next up, we need to do the actual implementation.
Part 3: create initial functionality.
Let’s take a look at the source from this commit: https://github.com/jaytaph/phpshout/blob/d9ad66512f23ff04ff2bb5822b833090d0a5d05b/shout.c
Here we actually created some simple functionality so we can use our shout-class in PHP (although it’s not doing very much).
We’ve added a php method on line 49. We use the PHP_METHOD macro for this, so php knows this is a method called “get_version” for the shout class. Internally it will create a function called zim_shout_get_version method since C isn’t OO or namespaced. This way, we can still distinguish our php methods (among other things). Line 50 will check if there are any parameters given to this method. If there are, it will return false since we do not accept any parameters.
Upon return on line 54, it will return the version of our shout library by calling shout_version, and returning this information as a PHP string. This is what the RETURN_STRING does, which is a macro that creates a PHP value in C the easy way. the “1″ parameter means that the string must be duplicated. Depending on your needs, this might be always the case since this will allow PHP to garbage collect unused variables.
So, this is a really easy function that does not much, but it shows you a bit on how “wrapping” a library works: we have a library function, we define a php function for that, we modify (optional) arguments from php so they can be fed into the library function and we return a result back to PHP. In fact, lot of PHP extensions work this way.
Now, let’s skip the constructor method on line 59 for now, and pay attention to line 148. Here you see we are filling our “shout_funcs” structure with the new get_version we just defined. we use ZEND_ACC_PUBLIC to tell that this function is a public function. So all that is left to do is create our PHP methods, add them to this structure and we’re done!
But there are some other things we can do, for instance creating class constants. This we do on line 240 to 261 (there are a lot of constants).
Again, PHP/Zend makes it really easy for use to create consants (strings or longs doesn’t matter). We just call zend_declare_class_constant_long, with our class_entry we just have saved ,the actual name and length of the class (the ERR_* constants for instance), and the actual value they represent. In our case, our constants are mapped to constants from the library file.
The ZEND_STRL macro:
Strings in PHP are binary safe. In C, they are not. Strings in C are normally terminated with a 0-byte, meaning that the string itself cannot hold a \0 value. And normally this is ok, exception when you are dealing with binary information in strings. So a lot of times, functions in PHP ask for a string PLUS the actual length of that string since it might not be possible for PHP itself to check the length by looking at the \0 byte. The ZEND_STRL macro returns the string PLUS the length of that string (assuming it’s NOT a binary string), so it’s easy to use in your code.
So in essence, these two functions are 100% similar:
test("hello world", strlen("hello world")-1); test(ZEND_STRL("hello world");
Now, PHP has got lots of these nifty macro’s, and not everybody uses them, or even know them.
Let’s go back to our constructor method. On line 147, we see the constructor is added to our shout_funcs structure again. You will see that is has another flag namely ZEND_ACC_CTOR, which tells us this is a constructor. (I don’t know what happens when you place this flag onto anther function though). And the actual constructor method we define from line 59.
This looks a bit complicated with a lof of #if statements, but this is because things have changed in the past on how to handle things. This way, we can be sure that our extension can run on older versions of the zend-module-api (and thus older php versions) as well. There are actually more places where stuff like this needs to be done (when you want to support REALLY old PHP versions), but we can assume we don’t (since we are doing OO here).
We are basically saving and setting our own exception class, so when exceptions are thrown, it will be handled by our exception class. (I noticed there is a (big) bug in this code, which I let you try to find, while I will update my code :p). On line 78 we fetch our store object, something we will do a lot and store this in intern. It’s a common and easy way to get to our internal data, in case we need to. For now, this line doesn’t serve any point since we don’t use the intern value anywhere.
On line 81, we are making a call to shout_init(), which initializes the shout library. There are some things we need to consider.
Right now, we are calling shout_init for EVERY object we are creating since it’s in it’s __construct(). However, this might not be what we want. We only want to initialize it once. So another option would be to place it in our MINIT function, which gets called only once. A perfect spot, but this means the shout_init() gets called, even for scripts that do not even use any shout functionality, which is something we don’t want either. For now, we don’t really care, since calling shout_init() multiple times isn’t a problem, but some library inits might have a problem with this. So make sure that when you call initialization functions, it’s done at the proper time.
Tying it all together:
Why not try and see if this thing actually works? :-)
- Make sure you have got the libshout3-dev package installed.
- Download the tarball for this blog-post: https://github.com/jaytaph/phpshout/tarball/blogpost-002
- Untar this into a directory.
- Run “phpize”.
- Run “./configure”. If there is a complaint about not finding the library, install the library first or use “./configure –with-shout=<path>” with the path to the library (if it’s on /home/user/libshout/include/shout/shout.h”, use /home/usre/libshout as the path).
- Run “make”, this will actually build the extension
- Run “sudo make install”, (or make install as root) so the library will be installed.
- Add the extension to your php.ini: “extension=shout.so”
- Check if the extension is working by issuing “php -m | grep shout”. This should show “shout”. You can also check phpinfo() on your webserver, but make sure the server has been restarted
Now you run your own scripts with shout capabilities (allthough very limited :p).
<?php $shout = new Shout(); print "Our shout vesion: ".$shout->get_version()."\n";
which should output:
Our shout vesion: 2.2.2