Easily Integrate C with PHP -- Click Me, Don't Be Scared

XML, Perl, Python, and other languages can be discussed here, even if it isn't PHP (We might forgive you).

Moderator: General Moderators

Post Reply
User avatar
volomike
Forum Regular
Posts: 633
Joined: Wed Jan 16, 2008 9:04 am
Location: Myrtle Beach, South Carolina, USA

Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by volomike »

(This discussion below is for Linux and Mac users (because Mac users are based on BSD Unix). Sorry, Windows users -- parts of the discussion won't work on your OS.)

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(); 
Note that you may have to change the path of your includes in order to point to your .h header files. Some people may have a subdir in /usr/include like postgres or pgsql or pg instead of postgresql.

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);
If I saved this as /tmp/tester2, and did 'chmod a+x /tmp/tester2', then I could call it like so:

# 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;
 
 
However, a more appropriate mechanism might be to use a message queue or cgi-bin call, or implement this as a daemon waiting on a TCP socket port locally on the server, sending and receiving calls. And if you go the daemon route, also consider using a UDP mechanism in an error handler if the TCP socket is unresponsive within a certain timeout. The UDP mechanism could be a separate daemon that receives communication on a UDP socket (same port number) and can be told to kill and restart the daemon on the TCP socket.

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.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by alex.barylski »

Cool. :)

Although I'm not sure why I wouldn't just compile a C program? Does the interpreter have the sense to prevent buffer overflows? If not, then using unchecked buffers:

Code: Select all

char lastname [60+1];
I believe you are asking for serious security ramifications doing that...worse than SQL injection.

On second thought...by including the shebang line in the C script the binary is probably compiled using a native compiler which is why you say it executes at C speed on second call?

Using C might be neat and certainly does increase speed but it introduces a whole new game for security problems and other issues. You have to delete dyanmically allocated memory, there is no in-built garbage collection and resources that run wild will crash the program, maybe hang your system who knows.

Cheers,
Alex
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by VladSun »

//offtopic
PCSpectra wrote:Does the interpreter have the sense to prevent buffer overflows? If not, then using unchecked buffers:

Code: Select all

char lastname [60+1];
I believe you are asking for serious security ramifications doing that...worse than SQL injection.
Since they put in action the "address space randomization" in kernel ... 2.6.15 I think, the buffer overflow exploits are much harder and some of the remote ones are even impossible (one should have access to the executable file itself).
http://www.milw0rm.com/papers/94


And since 2.6.25 it's very, very, very hard to create a successful buffer overflow exploit.
There are 10 types of people in this world, those who understand binary and those who don't
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by alex.barylski »

Thats an interesting fix. I wonder if Windows does the same.
buffer overflow exploits are much harder and some of the remote ones are even impossible (one should have access to the executable file itself).
Why do remote attacks not work?

Cheers,
Alex
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by VladSun »

I can't get the idea of using this "C" tool - wouldn't it be easier just to compile the C code?



PCSpectra wrote:I wonder if Windows does the same.
I do not have an idea ;) Probably yes.
PCSpectra wrote:Why do remote attacks not work?
Because "one should have access to the executable file itself" - you need to find an address of ffe4 (that is jmp %esp), if it exists, in the executable.
Unfortunately, linux-gate.so has it, but I think it's solved now.
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
volomike
Forum Regular
Posts: 633
Joined: Wed Jan 16, 2008 9:04 am
Location: Myrtle Beach, South Carolina, USA

Re: Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by volomike »

PCSpectra wrote: I'm not sure why I wouldn't just compile a C program?
Well, the advantage of the C script technique versus C is that you don't experience the compiling and linking issue. You just edit a script and go. But, the /usr/bin/C command -- it just compiles on the fly and sticks the result in /tmp/LARGE_C*/cache in a subdirectory there as an a.out file. That file could be found, then renamed as your command and moved somewhere if you wanted.

It's just a matter of convenience.

Now if I could get it so that it could do PostgreSQL stuff without me having to pass command-line parameters -- that's where it could start to be really cool. But I haven't figured that out yet with C running through GCC.

Still, I'd like to try my hand at making a PHP extension. Anyone got a sample hello world example that one could start with, and which works with Linux?
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Easily Integrate C with PHP -- Click Me, Don't Be Scared

Post by alex.barylski »

Well, the advantage of the C script technique versus C is that you don't experience the compiling and linking issue. You just edit a script and go. But, the /usr/bin/C command -- it just compiles on the fly and sticks the result in /tmp/LARGE_C*/cache in a subdirectory there as an a.out file. That file could be found, then renamed as your command and moved somewhere if you wanted.
So like using PHP but everything is compiled into C. I gotcha!
Still, I'd like to try my hand at making a PHP extension. Anyone got a sample hello world example that one could start with, and which works with Linux?
Nope. I've read up on it in the past as well but the docs are limited. You'd proabbly bet bets off looking at a skeleton example and just adding as you learn. If you figure it out, you should write an article as I'm not the only one who would appreciate it. :)
Post Reply