First off, what I have to share here is pretty cool. I pieced this together by reading various blogs and so on.
It may come to a point where you need to write something in C and have PHP call it because it can boost your performance. You can do this in about 7 different ways:
* Have a C-based web service sitting on the network, waiting for commands, and responding to them with actions and data.
* Have a C-based daemon waiting in memory on the same machine on another port, waiting for commands, and responding to them.
* Shell out to the OS with backticks or exec, system, or passthrough in order to call a C-based command.
* Create a .so file -- a library extension, specially designed with extra PHP stuff in it, so that the Zend interpreter can add it into the PHP global namespace so that your application can use it.
* Have a C-based daemon waiting on your server on an exclusive port number, ready to interact with you with either GET or POST commands from PHP via curl, or via an AJAX call from a web page. Or perhaps you can load it through Apache2 in a special case that I'm not aware of.
* Pass off the request to a queue/stack system to hold the request, and then have a periodic batch job load your C program to pull it off the queue system for processing. You can do this with System V message queue calls, but if the system ever gets turned off, you lose the storage of those. So, it's best to either use an MQ product or just roll your own with a database to manage the queue.
* Implement this as a cgi-bin call from PHP.
Okay, so then it comes down to building something quick and easy in C. Here's what I wanted to share with you. It won't help you in creating an .so file to be loaded in PHP -- I'm still learning how to do that. But at least I wanted to share with you how to build something in C, and then you can call it in any of the various ways I've mentioned above besides the library extension mechanism in PHP.
And you shouldn't be afraid of C if you're a PHP developer who's serious about system performance. Sometimes you can build functions or use C's #define command to mask the complexity of it, and getting a good book on database interaction with C, and a small book on C, can help you accomplish a lot.
First, I have Ubuntu Linux, so your mileage may vary. You'll have to change the commands below for your OS.
1. Let's get the workstation or server ready. In this example I'm going to use PostgreSQL. You can learn how to change that with some other database, but for purposes here, I'm sticking with PostgreSQL. The reason I am using PostgreSQL is because I want to show the usefulness to this rather than simply showing you a hello world example.
$ sudo su
# apt-get update
# apt-get install libpgeasy
# apt-get install alien
# apt-get install dpkg
# apt-get install postgresql-server
# apt-get install phppgadmin
# cd /tmp
# wget 'http://labs.cybozu.co.jp/blog/kazuho/ar ... 1.i386.rpm'
# alien C-*.rpm
# dpkg -i c-*.deb
2. Okay, now if you know how to install and configure phppgadmin, do so. However, by any means possible, create a PostgreSQL test database called "names" with a user named "names" and password "names" that has access to this on the local host. If you need help with that and the documentation doesn't help, you can reply to this message or hop on IRC chat and look for a chat room that supports PostgreSQL or phppgadmin, or sometimes go into the Ubuntu Server or Linux chat rooms for an answer.
3. Inside the "names" database, create a table called "names" and let your "names" user have access to it. It should have a firstname and a lastname with size of 60 chars. Fill it with some phony data.
4. Now create the following C script. And notice I said "script", not C program. I'll explain in a bit and you'll think it's really cool. Note the script below is definitely case-sensitive, and that's a big C in /usr/bin/C.
Code: Select all
#!/usr/bin/C
#include <postgresql/libpq-fe.h>
#include <libpgeasy.h>
#define PGEOF END_OF_TUPLES
void connect(char *h, char *d, char *u, char *p) {
PGconn * connection;
char s[255]; /* note we can't use char *s because sprintf is picky */
sprintf(s,"dbname=%s user=%s password=%s host=%s",d,u,p,h);
connectdb(s);
}
char firstname [60+1]; /* the +1 gives us space for a null-terminated string */
char lastname [60+1];
connect("localhost","names","names","names");
on_error_stop();
doquery("SELECT * FROM names");
while ( fetch (firstname, lastname) != PGEOF ) {
printf("%s %s\n", firstname, lastname);
}
disconnectdb(); 5. Now, do you see that #!/usr/bin/C directive at the top? That thing will normally let you call a script like this simply by doing:
$ sudo su
# cd {directory where file is}
# ./{script name}
However, this won't work in this case because we're integrating with a libpgeasy library, so we have to call it in the following way. The word "tester" below was my script name, so you can change it to what path and script name you made for step 4.
$ sudo su
# C -cL/usr/lib/libpgeasy.so -clpgeasy tester
What you'll see on output, if you don't receive errors, is a dump of your names table. Note that this will run sort of slow the first time you call it, but for all subsequent times, it will run super fast. This is similar to PHP -- where it first compiles your page into a cache file in /tmp before loading it, and then subsequent calls run the cache file. Note also that Linux is a little slow on dumping things to the console in a GUI -- if this were a PHP page calling this command, it would run lightning fast.
So, if you didn't get it, you just learned how to get C to access a PostgreSQL database to do something useful, and the C language didn't have to look so scary. For anything that seemed kind of foreign-looking to the average PHP user, I used #define or create a function to abstract it. And, the libpgeasy library is also another layer of abstraction to make it easier to interact with PostgreSQL. The C command that you compiled and created (via wget, alien, and dpkg) is a cool tool that lets you make C scripts that get interpreted and compiled into true C programs, and subsequent calls to those scripts will run just as fast as normal C stuff would. You also didn't have to include a main() function for your program, but if you absolutely desire that, then do 'man C' or 'C --help' and read the other command parameters.
Okay, so let's back up a bit and do the simple hello world example. It's also not that hard. Replace step # 4 with this script:
Code: Select all
#!/usr/bin/C
char *sHello;
sHello = "Hello, world!";
printf("%s\n", sHello);# cd /tmp
# ./tester2
or
# /tmp/tester2
Okay, so then to integrate it with PHP, probably the simplest way is to use a backtick operation like so:
Code: Select all
<?php
$sTest = `/tmp/tester2`;
echo $sTest;
Now, as for making this into an .so file and calling from PHP, you're better off just using the gcc command and not the C command, and to read up on the PHP website for how to find a skeleton .so file script and to make it do a hello() example. And on PHP5, you would load that .so file via the extension= parameter in your php.ini file.
As for how to get this to work in a shared hosting plan on the web, your best bet is to implement it via cgi-bin with the rules that your web hosting provider may have for that. They won't need to install the C command in /usr/bin. You just need to find the cached file for your particular command, rename it into your command (like 'tester') and drop it in the cgi-bin folder of your website. Then, call it from PHP like you would normally integrate with a cgi-bin command, or call it with backtick operations like so:
<?php
$sTest = `../cgi-bin/tester2`;
echo $sTest;
So where is that cache file? Good question. You can find it in /tmp/LARGE-C*/cache but you have to dig and look for an 'a.out' file (which you can rename) that runs your command. So, in my case, I had to keep running those a.out files by doing:
$ cd /tmp/LARGE-C*/cache
{cd to one of the folders underneath}
$ ./a.out
and seeing which one ran the hello world example. I then could copy this to my cgi-bin directory, rename it to 'tester2', and call it from PHP as a normal cgi-bin thing.
So there you have it. Now you can impress the senior developer on your team because you can boost your PHP stuff by using stuff compiled in C, and do it super easily.