Index: /tags/2007080101/config.m4 =================================================================== --- /tags/2007080101/config.m4 (revision 11) +++ /tags/2007080101/config.m4 (revision 11) @@ -0,0 +1,94 @@ +dnl $Id: config.m4,v 1.1.1.1 2004/02/17 23:31:43 sterling Exp $ + +AC_DEFUN(PHP_EXSLT_CHECK_VERSION,[ + old_CPPFLAGS=$CPPFLAGS + CPPFLAGS=-I$DOMEXSLT_DIR/include + AC_MSG_CHECKING(for libexslt version) + AC_EGREP_CPP(yes,[ +#include +#if LIBEXSLT_VERSION >= 600 + yes +#endif + ],[ + AC_MSG_RESULT(>= 1.0.3) + ],[ + AC_MSG_ERROR(libxslt version 1.0.3 or greater required.) + ]) + CPPFLAGS=$old_CPPFLAGS +]) + +PHP_ARG_WITH(fastxsl, for fastxsl support, +[ --with-fastxsl Include fastxsl support]) + +if test "$PHP_FASTXSL" != "no"; then + SEARCH_PATH="/usr/local /usr" + SEARCH_FOR="/include/libxslt/xslt.h" + if test -r $PHP_FASTXSL/; then + FASTXSL_DIR=$PHP_FASTXSL + else + AC_MSG_CHECKING([for fastxsl files in default path]) + for i in $SEARCH_PATH ; do + if test -r $i/$SEARCH_FOR; then + FASTXSL_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi + + if test -z "$FASTXSL_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please reinstall the fastxsl distribution]) + fi + + PHP_ADD_INCLUDE($FASTXSL_DIR/include/libxml2) + PHP_ADD_INCLUDE($FASTXSL_DIR/include) + AC_MSG_CHECKING(whether to support libmm based shared template cache) + AC_ARG_ENABLE(libmm, + [ --enable-libmm Enable libmm based shared template cache],[ + if test "$enableval" = "yes" ; then + AC_DEFINE(FASTXSL_MM, 1, [ ]) + AC_MSG_RESULT(yes) + PHP_ADD_LIBRARY_WITH_PATH(mm, $FASTXSL_DIR/lib, FASTXSL_SHARED_LIBADD) + else + AC_MSG_RESULT(no) + fi + ],[ + AC_MSG_RESULT(no) + ]) + PHP_ADD_LIBRARY_WITH_PATH(xslt, $FASTXSL_DIR/lib, FASTXSL_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(xml2, $FASTXSL_DIR/lib, FASTXSL_SHARED_LIBADD) + AC_DEFINE(HAVE_FASTXSLLIB,1,[ ]) + PHP_SUBST(FASTXSL_SHARED_LIBADD) + + PHP_NEW_EXTENSION(fastxsl, fastxsl.c fl_hash.c, $ext_shared) +fi + +PHP_ARG_WITH(exslt, for DOM EXSLT support, +[ --with-exslt[=DIR] FastXSL: Include DOM EXSLT support (requires libxslt >= 1.0.18). + DIR is the libexslt install directory.], no, no) + +if test "$PHP_EXSLT" != "no"; then + if test -r $PHP_EXSLT/include/libexslt/exslt.h; then + DOMEXSLT_DIR=$PHP_EXSLT + else + for i in /usr/local /usr; do + test -r $i/include/libexslt/exslt.h && DOMEXSLT_DIR=$i + done + fi + + if test -z "$DOMEXSLT_DIR"; then + AC_MSG_RESULT(not found) + AC_MSG_ERROR(Please reinstall the libexslt distribution) + fi + + PHP_EXSLT_CHECK_VERSION + + PHP_ADD_LIBRARY_WITH_PATH(exslt, $DOMEXSLT_DIR/lib, FASTXSL_SHARED_LIBADD) + + PHP_ADD_INCLUDE($DOMEXSLT_DIR/include) + + AC_DEFINE(HAVE_DOMEXSLT,1,[ ]) + + PHP_SUBST(FASTXSL_SHARED_LIBADD) + +fi Index: /tags/2007080101/samples/sample.xsl =================================================================== --- /tags/2007080101/samples/sample.xsl (revision 1) +++ /tags/2007080101/samples/sample.xsl (revision 1) @@ -0,0 +1,6 @@ + + + + + + Index: /tags/2007080101/samples/xsl/phpmag_tips_and_tricks.txt =================================================================== --- /tags/2007080101/samples/xsl/phpmag_tips_and_tricks.txt (revision 1) +++ /tags/2007080101/samples/xsl/phpmag_tips_and_tricks.txt (revision 1) @@ -0,0 +1,288 @@ + + + +
+ + Tips & Tricks + LeendertBrouwer + + + + + In this article we're going to look at a few things that might not be + something you intuitively think of when approaching certain problems, or + you might not even see the problem in the first place. As we all know, PHP + has a huge userbase. Logically, if a lot of people use a technology, that + adds to the amount of experience in the big picture. Some programmers + invent neat solutions to solve certain things, and sharing them with peers + is generally the next logical step in the PHP culture. + + + +
+ Making URL tampering less inviting + + The fact that you should never trust a user should be an extension of the + programmer's brain. When programming, a decent amount of paranoia is often + needed to avoid having your application cracked. Visitors can be downright + mean, and we should punish them for that as soon as we can. Ideally, even + before they're tempted to mess with our URL's. How would we go around + that? One way is to encode the parameters in the URL so that it is less + obvious what's in them. Say, you need to pass a username along with the + URL. First, we might choose to not call our parameter "username". Instead, + we could use a name that does not expose the nature of our parameter, so + that Mister "oh-im-so-cool" Cracker doesn't really have a clue about what + the parameter is supposed to represent. To keep our example simple, we'll + just use "u" for the name. Listing 1 shows how we can send the encoded + value along with the URL, and decode it at the other end. To encode the + string we use base64_encode, which is a function that is normally used + to encode binary data for safe transporting, but it works fine for our + purpose too. To keep things nice we encode the base64-encoded string with + rawurlencode to comply with RFC 1738 and pass the parameter that way + using a HTTP Location header. In the receiving script we simply + rawurldecode the incoming GET-parameter "u" and use base64_decode to + get our original string back. Now the visitor will see a somewhat strange + URL like http://www.yourdomain.com/letsgohere.php?u=SG9seUdvYXQ%3D and + will be left in confusion, as we intended. Of course this is not meant to + be used for actually securing your data, but it's a nice trick to scare + off potential script kiddies or leechers. + +
+ +
+ Requiring authentication codes + + Many times, I have gotten mailinglists that had a URL to unsubscribe from + the list through a url like this: + http://www.somesite.com/unsubscribe.php?email=myemail@mydomain.com, and by + clicking the link you're unsubscribing yourself. It's just too tempting to + play with that. Guess what happens when you launch + http://www.thedomain.com/unsubscribe.php?email=info@somesite.com. It is + likely that the people behind somesite.com have subscribed themselves to + the mailinglist to receive their own mailinglists just to confirm it has + been sent. The next time they might just be a little puzzled because + they're not receiving any mail. There are of course a lot of variations on + this particular kind of prank inviting situations. They can often be + avoided, too. In the case of the example mentioned, there's a simple thing + you can do to prevent this from happening. When setting up the + subscription system for the mailinglist, you could store not only the + email address, but also some unique code that goes with the email address. + That way, you can include both in the link you use for unsubscribing, and + the email address will only be unsubscribed when the combination of both + the email address and the unique code is a valid match. Code that could be + used to generate a unique string is visible in Listing 2 (I've used + substr to limit the length of the code because it looks ugly). Now the + link to remove yourself from the list could look like this: + http://www.thedomain.com/unsubscribe.php?email=info@somesite.com&code=78c7c1. + That will take some guessing before someone can do some annoying things, + because without the match of email and code, removal is not possible. This + is an easy fix in case you're writing applications that use information to + trigger certain actions that can easily be tricked, without needing a + complicated or less user friendly authentication system around it. + +
+ +
+ A little more strict on incoming data + + A lot of programmers are stressed because of tight deadlines. That's not + something we can get out of the way, it has been like that for decades now. + However, this also has the unfortunate effect that a lot of sloppy code + gets written, which can lead to strange results at times. For example, too + often I've experienced scripts that only checked if a variable existed + after submitting a form, but did not look at the data that was coming in at + all. Listing 3 shows an example of that. But who is that guy whose name + consists of only a space? Indeed, there is no guy whose name only consists + of a space. That's why we could at least check if the value of the field we + want to validate is containing any characters besides a possible space. + Listing 4 shows how to do just this for a field in a form that's being + submitted using the HTTP POST method. In the if-statement we use trim to + get rid of the whitespace at the beginning and the end of the string. + Therefore, there can be nothing left but other characters than space. We + see if there are in fact any characters left by invoking strlen on the + remaining string. If that value is bigger than zero, we know it is set. If + it's not, there were only spaces in the field. Of course this is by no + means a strict way of dealing with your data, but it sure is better than + just testing if the variable is there and it can save some trouble. If you + really want strict validation of incoming data you're better off with + regular expressions in most cases. + +
+ +
+ Overriding safe_mode with the CGI binary + + A lot of us probably have faced situations in which we were not having much + say in the environment that's going to be used for the things that are + being programmed. That can be extremely annoying at times, primarily + because webhosting companies tend to limit what you can do with PHP on + their webservers, thus limiting the set of functionality that you can use. + There's a nice trick to bypass this kind of "security" in some situations. + A lot of companies (although not recommended for performance reasons) still + install PHP as a CGI binary. They also tend to be a bit meaner than that by + not letting us use .htaccess files to influence the PHP configuration + (which is caused by Apache's AllowOverride directive not allowing Options), + and on top of that, they will run PHP in safe mode. That's not a very nice + working environment, is it? Fortunately, there's a hack, or rather, a fact, + that a lot of people don't know about. When running PHP as a CGI program, + the PHP interpreter always tries to look for a php.ini in the directory + that the script resides. That allows us to just override the safe_mode + directive by putting safe_mode = Off in a php.ini, stuff it in the relevant + directory, and boom. We have a working PHP environment in which we can + actually get things done. + +
+ +
+ throwing data at MS Excel + + I have often seen questions from people who want to output data in MS Excel + format. Most of the time, the only reason people want to do that is so they + can look at the data in nice, organized columns. In that case, you would + not need the logic a spreadsheet program provides and thus, you do not + actually need to use an MS Excel fileformat for that. MS Excel can read + comma-delimited files as well, which are a lot easier to create and only + hold data. Listing 5 shows a simple example to accomplish this. As you can + see, it's pretty easy. For proof of concept, I created an array, looped + through the contents of it and added it to a string. Sending the correct + HTTP headers is next on the list. Ideally, we would like the browser to + come up with a dialog for downloading or direct opening. As the idea is to + load the data into MS Excel, we can simply use the string + "application/ms-excel" as the value for the Content-type header. That will + create the awareness that we're dealing with a MS Excel file here. We set + the Content-Disposition header to "attachment" as we do not want the + content to appear inline (in the browser) and after this we come up with a + name that will be used to save the file to the client's disk. I've chosen + "data.csv". Lastly we print the contents of the string to the client. The + script will now cause the dialog to show up and (depending on your browser) + will give you the option to download or open the file directly. If MS Excel + is installed the contents will now be shown nicely in MS Excel. That's all + there's to it. + +
+ +
+ Running a PHP script as a cron job + + On some occasions you want to automate certain tasks. Tasks that PHP is + good at, in particular. So you thought that's not possible with PHP and + ported your idea to a bash script? Too bad. Doing that with PHP is pretty + trivial. There are actually two common ways to go around that. The first + is to use the command line interpreter directly, so that means you'd just + do the shell scripting in PHP. Starting from version 4.3.0, PHP is compiled + with --enable-cli by default, which means that the command line interpreter + will be available. Listing 6 shows an example of how to write a shell + script in PHP. We just put a she-bang (path to the interpreter) on top of + the file to point to the command line interpreter (the -q parameter is used + to surpress HTTP headers). Give this file permissions so that it can + execute, let the cron daemon know when to execute it (how to do this is in + the docs of your OS), and there you have a nice cron job written in PHP. + Just in case you do not have access to the CLI (because PHP was compiled + with --disable-cli or on older PHP versions that don't have it enabled), + there's an alternative way of doing it which is a bit more tricky, but + still a fairly clean hack. Using this way, you can just put the script that + needs to be executed in a web directory. In listing 7 you can see a regular + shell script. In this script, we invoke the text-based Lynx browser to + execute the PHP file. The -dump parameter makes sure Lynx will exit once + the request is completed. Assuming we don't want the script to be executed + by accident, it's probably the best if you protect the directory the script + resides in with a password. When using HTTP authentication, Lynx needs to + know the authentication data so that it can access the script. This is + accomplished by using the -auth parameter, which can be given a username + and password, delimited by a colon. The PHP script you're calling can be a + regular script, there's nothing special about this. As with the method + mentioned earlier, you give the shell script execute permissions, tell cron + when to execute it, and we're done. + +
+ + + + + + + + + + + + + + + + + + + + + + 0) +{ + // do things +} +]]> + + + + + + 'two', + 'three' => 'four', + 'five' => 'six' +); + +$csv_data = ''; + +foreach($foo as $key => $val) +{ + $csv_data .= "$key,$val\r\n"; +} + +header("Content-type: application/ms-excel"); +header("Content-Disposition: attachment; filename=data.csv"); +echo($csv_data); +?> +]]> + + + + + + +]]> + + + + + + + + + +
Index: /tags/2007080101/samples/xsl/editorial.xml =================================================================== --- /tags/2007080101/samples/xsl/editorial.xml (revision 1) +++ /tags/2007080101/samples/xsl/editorial.xml (revision 1) @@ -0,0 +1,84 @@ + + + +
+ + Dear Readers, + + +
+   + + Welcome to the fifth issue of the International PHP Magazine. Summer is + almost over again, and with a fresh mind I was going to start working on + some of the remaining bugs in PHP. But, that proved more difficult then I + thought. The problem is that there are almost no reported bugs anymore, and + if there are, they're in one of the categories that nobody else knows or + cares about (such as ODBC, Java and OCI). So what does that mean? Is PHP + really becoming bugfree? Well, of course it isn't. There are still some + bugs, but they are unknown to the PHP Development team. Most of these bugs + are in places that are not much used and does have very little chance of + discovering, other might be introduced in PHP 4.3.3, which should be out + already, or in the very near future. And then of course there is PHP 5 with + it's new internals. Especially this part hasn't been tested that much by + real users so it is very likely that there will be found quite a lot of + bugs in there. So it might look like we're out of bugs, but there might be + a lot of them hiding under the grass. + + + If you want to find bugs in your own code, have a look at the last part of + our Error Handling article. What else can you find in this issue? We have + an article on how byte code caches work, and an accompanying benchmark with + four caches: APC, PHP Accelerator, Turck MM Cache and Zend Performance + Suite. We have an introductionary article on SOAP which teach you how to + create a basic SOAP client and SOAP server. Follow ups on this article will + be published in later issues. Steph Fox talks about new PHP-GTK + functionality and Davey Shafik explains us ways on how to 'secure' input + variables with HMACs. + + + Left are the first part of a very extensive article + on PHP 5 and OO, in this part Marcus Börger explains the new OO model + from a users' perspective and tries to implement the methods that were + raised in the OO Introduction article in Issue #1. The next issue will + feature the second part of this article in which Marcus will talk about + some more advanced features. + + + Furthermore we have an article dealing with the under ColdFusion very + popular FuseBox methodology applied to the PHP world and another article on + Portability and Backwards Compatibility. The latter talks about strategies + on how to code defensively for multiple PHP versions and provides ways on + how to fill in missing functions from earlier PHP versions. + + + As always, if you have any questions or suggestions, feel free to post them + at our forum @ http://php-mag.net/forum or write to us, your editors @ + editors@php-mag.net. We hope that you enjoy this issue. + +
+ +
+ + + http://bugs.php.net/bugstats.php?phpver=4 + Bugs Statistics + + + http://www.php.net/~jani/bugs-graph.php + Bugs Graphs + + + http://php-mag.net/itr/online_artikel/psecom,id,284,nodeid,114.html + Leendert Brouwer: Taking PHP the OO way + + +
+
+ Index: /tags/2007080101/samples/xsl/editorial2.xml =================================================================== --- /tags/2007080101/samples/xsl/editorial2.xml (revision 1) +++ /tags/2007080101/samples/xsl/editorial2.xml (revision 1) @@ -0,0 +1,91 @@ + + + +
+ + Dear Readers, + + +
+   + + Welcome to the fourth issue of the International PHP Magazine. PHP 5 is + coming and one of it's main features is the improved OO model, until a few + weeks ago. Work on a few new extensions has already been started: + simplexml, dom and xsl should be the core extensions to use, parse and + transform XML; sqlite, simpledb and mysqli are new database extensions. + The name 'simpledb' has not really settled, the extension will form some + kind of abstraction for all the different database APIs including some + additional functionality that the new OO model made possible. + + + The MySQLi extension, yes, the MySQLi extension causes some head aches. + Because MySQL changed the license for the version 4.x client libraries from + LGPL (The Lesser GNU Public License) to GPL (The GNU Public License) the + PHP project is not allowed to bundle the library anymore; even worse, they + can not even distribute the extension anymore. Not in the core distribution + and not in PECL unless PHP changes its license to GPL, which is + something that will never happen. Actually, it gets even worse. With the + new licensing it is not even allowed to distribute a propriatary web + application that relies on PHP's MySQL API (if it's using the version 4 + libraries), again, unless you distribute your application under the GPL, + which kinda defeats the idea of 'proprietary'. We'll illustrate how it can + affect you with some examples: + + + You made a large webshop relying on MySQL 4.1 functions which you wrote as + a contract job for one specific customer, but you're not distributing + either PHP, the MySQLi extension, the MySQL client library nor MySQL: no + problem at all, because you're not distributing your application and thus + are not bound to the GPL, which only covers distributing (which is weird on + it's own, as normal non-virulent licenses usually deal with usage). Things + change when suddenly you have to possibility to sell the same script to + another customers. As you're now distributing the script you're bound to + the GPL, and if you don't want to relicense your application you will need + to buy a MySQL commercial license. + + + During the interview we had about the licensing issues with MySQL's Open + Source advocate Zak Greant he told us two reasons why they changed license. + "Our users now only have to deal with one non-commercial license" + and "having a GPL-licensed client lets us more easily differentiate + between a proprietary user ... and a free software user who should use the + software under the terms of the GPL". They kinda forget there are a lot + of Open Source developers that do not like the GPL at all because of it's + virulent nature. MySQL with it's large user base is effectively try to + convert its "open source" user base to the church of Emacs, and that also + over the back of the PHP users. And that is going to far as MySQL's + userbase only grew because of PHP and the bundling of its client + library with PHP. People feel backstabbed, lines as "if I knew they were + going this way, I would never helped them". Perhaps that goes a bit too + far, but MySQL definitely succeeded in pissing of quite a lot of PHPers, + and also a lot off SAP DB users, and for what reason? Definitely not only for + the two mentioned in the interview, its apparent that there is only one + real reason: "Maximize our profit, screw the users". Okay, I admit + that that goes a little bit too far, MySQL still isn't Microsoft. + + + What else is in this issue? As a starter we'll have a nicely filled "Ask a + PHP Guru" section with questions from our readers.The main dish consists of + a often requested on Secure Programming with PHP and as desert we have + articles on Error Handling, Template Systems, cURL, XML-RPC and Jabber. Bon + Apetit! + +
+ +
+ + + http://www.mysql.com/ + MySQL Website + + +
+
+ Index: /tags/2007080101/samples/xsl/askaguru.xml =================================================================== --- /tags/2007080101/samples/xsl/askaguru.xml (revision 1) +++ /tags/2007080101/samples/xsl/askaguru.xml (revision 1) @@ -0,0 +1,657 @@ + + + +
+ + Ask a PHP Guru + Any questions? + + + + + In our magazine we give you, our readers, the opportunity to ask a real PHP + guru what you ever wanted to ask. This, of course, is not meant as a + support forum for general PHP questions; the PHP project has plenty of + mailinglists to deal with those kind of questions. The most interesting + questions will be published in the magazine, along with the written answer + of one of our gurus: Ilia Alshanetsky, Stig S. Bakken, Marcus Börger, + Stefan Esser, Wez Furlong, Sterling Hughes, Georg Richter, Zeev Suraski and + Andrei Zmievski. We rather like to see conceptual questions and not + practical ones as they tend to be more interesting for other readers as + well. + + + +
+ Question 1: Threading + + Q: Is there a way to do a form of threading in PHP? + + + + Say for instance you write a PHP application to monitor a service + on a number of servers, it would be nice to be able query a number of + servers at the same time rather then query them one-by-one. + + + + A: People often assume that you need to fork or spawn threads whenever you + need to do several things at the same time - and when they realize that PHP + doesn't support threading they move on to something less nice, like perl. + + + + The good news is that in the majority of cases you *don't* need to fork or + thread at all, and that you will often get much better performance for not + forking/threading in the first place. + + + + Say you need to check up on web servers running on a number of hosts to + make sure that they are still responding to the outside world. You might + write a script like in Listing 1. + + + + + ]]> + + + + + This works fine, but since fsockopen doesn't return until it has resolved + the hostname and made a successful connection (or waited up to $timeout + seconds), extending this script to monitor a larger number of hosts makes + it slow to complete. + + + + There is no reason why we have to do it sequentially; we can make + asynchronous connections - that is, connections where we don't have to wait + for fsockopen to return an opened connection. PHP will still need to + resolve the hostname (so its better to use IP addresses), but will return + as soon as it has started to open the connection, so that we can move on to + the next host. + + + + There are two ways to achieve this; in PHP 5, you can use the new + stream_socket_client function as a drop-in replacement + for fsockopen. In earlier versions of PHP, you need + to get your hands dirty and use the sockets extension. Listing 2 shows how + to do it in PHP 5. + + + + + $host) { + $s = stream_socket_client("$host:80", $errno, $errstr, $timeout, + STREAM_CLIENT_ASYNC_CONNECT); + + if ($s) { + $sockets[$id] = $s; + $status[$id] = "in progress"; + } else { + $status[$id] = "failed, $errno $errstr"; + } +} + +/* Now, wait for the results to come back in */ +while (count($sockets)) { + $read = $write = $sockets; + /* This is the magic function - explained below */ + $n = stream_select($read, $write, $e = null, $timeout); + + if ($n > 0) { + /* readable sockets either have data for us, or are failed + * connection attempts */ + foreach ($read as $r) { + $id = array_search($r, $sockets); + $data = fread($r, 8192); + if (strlen($data) == 0) { + if ($status[$id] == "in progress") { + $status[$id] = "failed to connect"; + } + fclose($r); + unset($sockets[$id]); + } else { + $status[$id] .= $data; + } + } + /* writeable sockets can accept an HTTP request */ + foreach ($write as $w) { + $id = array_search($w, $sockets); + fwrite($w, "HEAD / HTTP/1.0\r\nHost: " + . $hosts[$id] . "\r\n\r\n"); + $status[$id] = "waiting for response"; + } + } else { + /* timed out waiting; assume that all hosts associated + * with $sockets are faulty */ + foreach ($sockets as $id => $s) { + $status[$id] = "timed out " . $status[$id]; + } + break; + } +} + +foreach ($hosts as $id => $host) { + echo "Host: $host\n"; + echo "Status: " . $status[$id] . "\n\n"; +} +?>]]> + + + + + We are using stream_select to wait for events on the sockets that we + opened. stream_select calls the system select(2) function and it works + like this: The first three parameters are arrays of streams that you want + to work with; you can wait for reading, writing and exceptional events + (parameters one, two and three respectively). stream_select will wait up + to $timeout seconds for an event to occur - when it does, it will modify + the arrays you passed in to contain the sockets that have met your + criteria. + + + Now, using PHP 4.1.0 and later, if you have compiled in support for + ext/sockets, you can use the same script as above, but you need to replace + the regular streams/filesystem function calls with their equivalents from + ext/sockets. The major difference though is in how we open the connection; + instead of stream_socket_client, you need to use the function in Listing + 3. + + + + + ]]> + + + + + Now, replace stream_select with socket_select, fread with + socket_read, fwrite with socket_write and fclose with + socket_close and you are ready to run the script. + + + The advantage of the PHP 5 approach is that you can use stream_select to + wait on (almost!) any kind of stream - you can wait for keyboard input from + the terminal by including STDIN in your read array for example, and you can + also wait for data from pipes created by the proc_open function. + + + Although PHP 5 is not ready for prime-time hosting, you should find it + stable enough to use the CLI version in this scenario - feel free to try + out a snapshot! If you are not brave enough to run with PHP 5, go with PHP + 4.3.2 and use ext/sockets. + + + If you want PHP 4.3.2 *and* want to use the native streams approach, I have + prepared a patch that allows fsockopen to work asynchronously. The patch + is unsupported and won't be in an official PHP release, however, I've + provided a wrapper that implements the stream_socket_client function + along with the patch, so that your code will be forwards compatible with + PHP 5. + + + -- Wez Furlong + + + + + http://www.php.net/stream_select + documentation for stream_select + + + + http://www.php.net/socket_select + documentation for socket_select + + + + http://www.php.net/~wez/guru-multiplexing.tgz + patch for 4.3.2 and script to emulate + stream_socket_client. + + + +
+ +
+ Question 2: PEAR on MacOSX + + + Q: I've read articles about it, see a lot of comments on PEAR, so + it would be nice to test it myself. PEAR is installed (sort of) with PHP + 4.3.0, but how do I get it to work, to see the web-based installer + etc.? + + + I can't find any installing information on pear.php.net for MacOS + X. I don't seem to find the logic to get it to work... it would be nice if + anyone can help me find the logic behind it and get the whole thing to + work. + + + A: With the 4.3.0 releases, Macintosh users can now work with their + favourite scripting langage as any other Linux, *BSD or Windows users. Here + I'll explain how to setup PEAR on your box and then enjoy the powerfull + PEAR package manager. + + + First, you need a working PHP installation using PHP 4.2.0 or recent + releases. I do not cover the installation of PHP here, please read the + usefull tutorials in the links sections or uses the binaries available on + entropy.ch. We recommand the use of the CLI (PHP + configure option --enable-cli) interface to install PEAR + in the console mode. The CGI version works well too (available with the + binaries). To use the web installer, you will need a working Apache/PHP + installation. + + + To install PEAR packages you need the installer. This tool includes + everything you need to install or upgrade a pear packages or create your + own package. + + + A crossplatform setup script is available through + http://go-pear.org. Grabing and executing is an easy + task: + + + +% curl http://go-pear.org | php + + + + Or you may always download it from your favourite browser save the file as + go-pear.php and launch it using: + + + +CLI: +% php go-pear.php +CGI: +% php -q go-pear.php + + + + + At this point, the following steps are explained in the setup script + itself. I recommand to carefully read the possible warning or notices + during the installation process. These texts are usefull to solve any + problems during this phase. + + + + To do change permissions on the default PHP installation folder, you can + choose the installation folder as follows: + + + + +Installation prefix: /usr/local +Binaries folders: $prefix/bin +PHP Code folders: $prefix/share/pear + + + + Verify everything works well and your pear command is in your path by + entering this command: + + + +% pear -V +PEAR Version: 1.1 +PHP Version: 4.3.2 +Zend Engine Version: 1.3.0 + + + + You may need to update your include_path according to + your PEAR installation by editing you php.ini: + + + +% bbedit -c /usr/local/lib/php.ini + + + + +Modify or add the following line: + + + +include_path = ".:/usr/local/share/pear"; + + + + + Now you are ready to use PEAR. You can list the installed packages with: + + + +% pear list +Installed packages: +=================== +Package Version State +Archive_Tar 1.0 stable +Auth 1.2.0 stable +Benchmark 1.2 stable +Cache_Lite 1.1 stable +Config 1.5 stable +Install a package (i.e. Text_Statistics) +% pear install Text_Statistics +downloading Text_Statistics-1.0.tgz ... +...done: 0 bytes +install ok: Text_Statistics 1.0 + + + + +
+ pear list-all + +
+
+ + + The web installer is still in an alpha state, but should be usefull. The + first step is to get the go-pear script using your + browser (using "save target as") and save it somewhere under your document + root directory. The easiest way is to create a new directory for pear and + put the files there. + + + Once go-pear.php has been saved, make sure PHP has the + write permissions in the installation folder. Then you can launch the + installation from your browser + (http://localhost/pear/go-pear.php). + + + The setup steps are self explained in the different screen of the + installer. After having running go-pear, you get a link to the web + installer itself. I suggest to bookmark this link. As we did for the CLI + installer, you may need to update the include_path to point to your PEAR + installation directory. + + + To skip wide usage of the PEAR web frontend, I strongly recommand to + protect the PEAR directory and the go-pear script with a password, i.e. + using an .htaccess file. + + + The usage of the PEAR web Frontend is very easy, so I don't explain how to + click on the different buttons :). + + + -- Pierre-Alain Joye + + + +
+ The setup script's installing packages + +
+
+ + +
+ The web frontend in action + +
+
+ + + + PHP 4.3.0 and Mac OS X + http://www.onlamp.com/pub/a/php/2003/01/17/phpcookbook.html + + + PHP binaries for Mac OS X + http://www.entropy.ch/software/macosx/php/ + + + Misc. articles about php and Mac OS X + http://www.phpmac.com/ + + +
+ +
+ Question 3: MySQL login problems + + + Q: Since we use MySQL 4.1 users of our website aren't able to + login anymore. SELECT id from c_user WHERE login='name' and + passwd=password('passwd') always returns an empty result set. This also + happens when I use the external MySQL 4.1 library instead of the bundled + lib. Do we need to switch from ext/mysql to ext/mysqli to solve this + problem? + + + A: As described in the manual the password function + should only be used for operations on the mysql user table. For encrypting + passwords in other tables, you should use md5 or + sha. From MySQL 4.0 to 4.1 the + password-function changed: It has now a stronger and + better encryption: The encrypted string is salted, and it's length changed + from 16 to 45 chars. Fortunately MySQL AB developers implemented a + workaround: the function old_password which supports + the old encryption. + + + select password("php magazine"); + +-----------------------------------------------+ + | password("php magazine") | + +-----------------------------------------------+ + | *9fba5e64a7c11ce87d229c43cdb0f0ab87f7886dd698 | + +-----------------------------------------------+ + 1 row in set (0.00 sec) + + mysql> select old_password("php magazine"); + +------------------------------+ + | old_password("php magazine") | + +------------------------------+ + | 001b8f92205d4470 | + +------------------------------+ + 1 row in set (0.00 sec)]]> + + + Because there is no function to decrypt a stored password, it's only + possible to change the password to an md5 or sha encrypted value, with the + interaction of the user during the login process. + + + -- Georg Richter + +
+ +
+ Question 4: MySQL random rows + + + Q: Last year we launched a single community portal which has over + 100.000 active members. Since we implemented a little box which shows five + random entries with user ads on the entry page we noticed a heavy load and + speed loss on our system. As suggested in the MySQL manual we use the + order by rand method in combination with + LIMIT. + + + A: Order by rand might be an useful feature for + smaller tables. Depending on the selected columns it requires a full index- + or tablescan which is very expensive: For selecting five rows from 100.000 + rows, MySQL has to read all 100.000 rows and sort it by random. The best + way to select random entries from large tables is to use the MySQL Handler + (this requires a MySQL-Version > 4.0.2). + + + First determine the number of rows in your ads table: + + + + + + On client side you now have to create 5 diffrent random values between 1 + and $total (be sure to check possible duplicate values). + + + Hopefully lot of users found a partner via your site and removed or + deactivated their ads. Otherwise it would be easy now to select some random + rows, cause there are no gaps in your (autoincrement-) id's: + + + + + + When you have some gaps in your id, this solution will not work of cause. + In this case HANDLER is a good and fast way. + HANDLER provides a low-level interface for table + handlers which bypasses the optimzer and accesses table content directly: + + + + + + This opens the handler for read access. Now you can fetch the diffrent + rows with your calculated random values: + + + + + + After fetching all rows you have to close the handler: + + + + + + -- Georg Richter + +
+ +
+ Question 5: Syntax highlighting + + Q: I have wanted to hack up a cross-referencer for some time. Sat + down to play with Listings 1 and 2 of 'Under the Hood (3.2003)' but + rapidly realised that there is no apparent linkage between the available + functions (man App CI) and code/printout line numbers. (Yet there is at the + compile/debug phase!?) + + + Looked at a few of the pretty-printer scripts (syntax + highlighter) but none of them (at least of the two/three I examined) seems + to use the technique/token_get_all(). + + + + Short of merging the source code with the tokens how can the array of + tokens be related to code line numbers as required for the + tools/applications of the technique mentioned above/in the article? + + + + A: The T_WHITESPACE as shown on page 70 includes the + newlines, but that was not printed in the magazine for obvious reasons. The + token is matched by the engine on [ \n\r\t]+ which + includes the new line characters. By examinating the + T_WHITESPACE you will be able to deterine the line + number. (Of course you need to check all other tokens that can contain + whitespace too, like T_CLOSE_TAG, + T_ENCAPSED_AND_WHITESPACE, + T_OPEN_TAG, T_CLOSE_TAG, + T_COMMENT, + T_CONSTANT_ENCAPSED_STRING, T_STRING, + T_INLINE_HTML, T_START_HEREDOC and + T_END_HEREDOC. As you see this is quite a list and I + might have even missed some cases. The best way to do it is by using the + highlight_string in PHP. The article Error + handling article in this issue gives you some nice example on how + to use syntax highlighting. + + + -- Derick Rethans + + + + + http://www.php.net/highlight-string + documentation for highlight_string + + +
+
+ Index: /tags/2007080101/samples/xsl/mysql.xml =================================================================== --- /tags/2007080101/samples/xsl/mysql.xml (revision 1) +++ /tags/2007080101/samples/xsl/mysql.xml (revision 1) @@ -0,0 +1,144 @@ + + + +
+ + MySQL and the GPL + An interview + + + + + With MySQL 4.0 the licence of the client libraries changed from LGPL to GPL. + Because this has quite some consequences for PHP users too I had an + interview with MySQL's community advocate discussing why MySQL moved and + what happens for PHP users that also use MySQL. + + + +
+   + + DR: Why has MySQL changed the licence for the client libraries from + GNU LGPL (the GNU Library (or Lesser) General Public License) to GNU GPL + (the GNU General Public License)? + + + ZG: First, having a GPL-licensed client lets us more easily differentiate + between a proprietary user who should buy a commercial license and a free + software user who should use the software under the terms of the GPL. + + + Second, rather than having multiple licenses to deal with, our users now + only have to deal with one license: either the GPL or a commercial license. + +
+ +
+   + + DR: Did you think about how the MySQL community might think of the + license change from LGPL to GPL? + + + ZG: Of course! Our users and community are very important to us. + + + We expected that users who wish to use MySQL without licensing fees, but who + do not wish to pass the freedoms that we give them with the GPL to other + users, would be disappointed by the change. + + + While we want our users and community to be happy, we do not believe that + people should get the product without either commitment to community ideals + or some cost. + + + However, in some cases, we will grant commercial licenses at no cost (or add + an exception to our licensing) so that we can support worthy community, + charity or educational ventures. + + + Community members that believe in the Free Software philosophy would, of + course, support the change. + + + Community members who support Open Source and dislike the GPL would not + like the fact that their code could no longer be linked with the MySQL + client libraries. This aspect concerned us the most. + + + While we support the Free Software ideals, we also support the Open Source + community. We do not want to punish people who choose to release their code + for the greater good. + +
+ +
+   + + DR: But that is what is happening with the PHP community right now + - we cannot distribute the new MySQL client library under its current + licensing terms. Does this affect the thinking inside the + company? + + + ZG: The PHP community has always been a great friend to MySQL and we do not + want to hurt them (or, by side effect, our own community). + + + We are currently working on a license exception that I hope will allow both + Free and Open Source software to link with the MySQL client library. + Unfortunately, this will weaken the application of the GPL to the client + library. However, we feel that something like this is the best type of + compromise for the Free Software community, the Open Source community and + MySQL as a company and a community. + +
+ +
+   + + DR: What about people selling PHP scripts which use MySQL, in which + of the groups do they fall? + + + ZG: If someone is distributing script under a closed license that uses + MySQL, then they are creating a combined work that includes GPL-licensed + software. They should purchase a proprietary license. + + + If they are selling access to a site or service that use proprietary code, + but they are not distributing the application, then they are free to use + GPL-licensed code. + +
+ +
+   + + DR: Will this exception affect distribution only, or usage + too? + + + ZG: If you do not distribute, you will not breach the terms of the GPL. The + exception is only needed for distributed code. + +
+ +
+ + + http://www.mysql.com/ + MySQL Website + + +
+
+ Index: /tags/2007080101/samples/xsl/mag.xsl =================================================================== --- /tags/2007080101/samples/xsl/mag.xsl (revision 1) +++ /tags/2007080101/samples/xsl/mag.xsl (revision 1) @@ -0,0 +1,665 @@ + + + + + + + + + + + + + + + + + + + + + + +

+ PMCode + +

+
+
+
+ + + + urn:schemas-microsoft-com:vml + urn:schemas-microsoft-com:office:office + urn:schemas-microsoft-com:office:word + http://www.w3.org/TR/REC-html40 + + + Content-Type + text/html; charset=windows-1252 + + + ProgID + Word.Document + + + Generator + Microsoft Word 9 + + + Originator + Microsoft Word 9 + + + <xsl:value-of select="articleinfo/title" /> + + + + + EN-US + tab-interval:35.4pt +
+ Section1 + +

+ PMHeadline + +

+

+ PMSubline1 + line-height:14.0pt;mso-line-height-rule:exactly + +

+

+ PMAuthor + +

+ + +
+ + +
+ + + + + + + + + + + <> + + + +

+ PMSubline2 + line-height:14.0pt;mso-line-height-rule:exactly + +

+
+ + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+

+ PMTextboxHeadline + line-height:14.0pt;mso-line-height-rule:exactly + +

+

+ PMTextbox + line-height:14.0pt;mso-line-height-rule:exactly + +

+

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+
+ + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+

+ PMCaption + line-height:14.0pt;mso-line-height-rule:exactly + >>><<< +

+

+ PMCaption + line-height:14.0pt;mso-line-height-rule:exactly + Fig .: +

+

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+
+ + +

+ PMParagraphHeadline + line-height:14.0pt;mso-line-height-rule:exactly + +

+ +
+ + + + + + + + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+

+ PMCaption + line-height:14.0pt;mso-line-height-rule:exactly + Listing +

+ + + + + + + + + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+
+ + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+ + + + + + + + + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+
+ + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly + +

+
+ + + + + + + + + + + + + + + () + + + +

+ PMLinksLiteratureHeadline + line-height:14.0pt;mso-line-height-rule:exactly + Links & Literature +

+ +
+ + +

+ PMLinksLiterature + margin-left:0in;text-indent:0in;line-height:14.0pt;mso-line-height-rule:exactly;mso-list:l2 level1 lfo5;tab-stops:.25in + : +

+
+ + + + + + + +

+ PMEnumeration + margin-left:0in;text-indent:0in;line-height:14.0pt;mso-line-height-rule:exactly;mso-list:l2 level1 lfo5;tab-stops:.25in + +

+
+ + + + + + + +

+ PMEnumeration + margin-left:0in;text-indent:0in;line-height:14.0pt;mso-line-height-rule:exactly;mso-list:l2 level1 lfo5;tab-stops:.25in + : +

+
+ + + +

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+

+ 1 + border-collapse:collapse;border:none;mso-border-alt:solid windowtext .5pt;mso-padding-alt:0in 3.5pt 0in 3.5pt +

+ +

+ PMTableHeadline + Table . + +

+

+ PMText + line-height:14.0pt;mso-line-height-rule:exactly +   +

+
+ + + + + + + 1 + 0 + 0 + +
+
+ + + + + + + + + + + + + + + + + + padding:0in 3.5pt 0in 3.5pt +

+ PMTable + line-height:14.0pt;mso-line-height-rule:exactly + +

+ +
+ + + + padding:0in 3.5pt 0in 3.5pt +

+ PMTable + line-height:14.0pt;mso-line-height-rule:exactly + +

+ +
+ +
+ Index: /tags/2007080101/samples/sample.php =================================================================== --- /tags/2007080101/samples/sample.php (revision 1) +++ /tags/2007080101/samples/sample.php (revision 1) @@ -0,0 +1,6 @@ + Index: /tags/2007080101/samples/sample1.php =================================================================== --- /tags/2007080101/samples/sample1.php (revision 1) +++ /tags/2007080101/samples/sample1.php (revision 1) @@ -0,0 +1,4 @@ + Index: /tags/2007080101/samples/sample2.php =================================================================== --- /tags/2007080101/samples/sample2.php (revision 1) +++ /tags/2007080101/samples/sample2.php (revision 1) @@ -0,0 +1,6 @@ + Index: /tags/2007080101/samples/t.php =================================================================== --- /tags/2007080101/samples/t.php (revision 1) +++ /tags/2007080101/samples/t.php (revision 1) @@ -0,0 +1,7 @@ + Index: /tags/2007080101/samples/sample3.php =================================================================== --- /tags/2007080101/samples/sample3.php (revision 1) +++ /tags/2007080101/samples/sample3.php (revision 1) @@ -0,0 +1,6 @@ + Index: /tags/2007080101/samples/t1.php =================================================================== --- /tags/2007080101/samples/t1.php (revision 1) +++ /tags/2007080101/samples/t1.php (revision 1) @@ -0,0 +1,7 @@ + Index: /tags/2007080101/samples/sample4.php =================================================================== --- /tags/2007080101/samples/sample4.php (revision 1) +++ /tags/2007080101/samples/sample4.php (revision 1) @@ -0,0 +1,6 @@ + Index: /tags/2007080101/samples/sample.xml =================================================================== --- /tags/2007080101/samples/sample.xml (revision 1) +++ /tags/2007080101/samples/sample.xml (revision 1) @@ -0,0 +1,4 @@ + + + Sample Data + Index: /tags/2007080101/fastxsl.c =================================================================== --- /tags/2007080101/fastxsl.c (revision 26) +++ /tags/2007080101/fastxsl.c (revision 26) @@ -0,0 +1,1643 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + | George Schlossnagle | + +----------------------------------------------------------------------+ + + $Id: fastxsl.c,v 1.1.1.1 2004/02/17 23:31:44 sterling Exp $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FASTXSL_MM +#include +#endif +#include +#include +#include +#include + +#include "fl_hash.h" +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_fastxsl.h" + +ZEND_DECLARE_MODULE_GLOBALS(fastxsl) + +static int inshm = 0; + +static int le_fastxsl_stylesheet; +#define le_fastxsl_stylesheet_name "FastXSL Stylesheet" +static int le_fastxsl_document; +#define le_fastxsl_document_name "FastXSL Document" + +#define FASTXSL_PRM_ALLOC 1 +#define FASTXSL_SHARED_ALLOC 2 + +void fastxsl_errorfunc(void *ctx, const char *msg, ...); +static void ShmCache_Stylesheet_Free(php_ss_wrapper *wrapper TSRMLS_DC); +static void _SS_Wrapper_Dtor(php_ss_wrapper *wrapper); +static void _XD_Wrapper_Dtor(php_xd_wrapper *wrapper); + +static php_ss_wrapper * +SS_Wrapper_Alloc(int shared TSRMLS_DC) +{ + php_ss_wrapper *wrapper; + + if (shared) { +#ifdef FASTXSL_MM + if (shared == FASTXSL_SHARED_ALLOC) { + wrapper = (php_ss_wrapper *) mm_calloc(FASTXSL_G(cache)->mm, 1, sizeof(php_ss_wrapper)); + } else { + wrapper = (php_ss_wrapper *) calloc(1, sizeof(php_ss_wrapper)); + } + if(!wrapper) { + mm_display_info(FASTXSL_G(cache)->mm); + } + wrapper->persistant = 1; +#else + wrapper = (php_ss_wrapper *) calloc(1, sizeof(php_ss_wrapper)); + wrapper->persistant = 1; +#endif + } else { + wrapper = (php_ss_wrapper *) calloc(1, sizeof(php_ss_wrapper)); + } + + return wrapper; +} + +static php_xd_wrapper * +XD_Wrapper_Alloc(void) +{ + php_xd_wrapper *wrapper; + + wrapper = (php_xd_wrapper *) calloc(1, sizeof(php_xd_wrapper)); + return wrapper; +} + +static xmlFreeFunc free_ptr; +static xmlMallocFunc malloc_ptr; +static xmlMallocFunc mallocatomic_ptr; +static xmlReallocFunc realloc_ptr; +static xmlStrdupFunc strdup_ptr; + +#ifdef FASTXSL_MM +void +ShmCache_Free(void *ptr) +{ + TSRMLS_FETCH(); +#ifdef DEBUGMM + if(!inshm) assert(0); +#endif + FASTXSL_G(tmp_allocated_size) -= mm_sizeof(FASTXSL_G(cache)->mm, ptr); + mm_free(FASTXSL_G(cache)->mm, ptr); +} + +void * +ShmCache_Malloc(size_t size) +{ + void *ptr; + TSRMLS_FETCH(); +#ifdef DEBUGMM + if(!inshm) assert(0); +#endif + ptr = mm_malloc(FASTXSL_G(cache)->mm, size); + if (!ptr) { + php_error(E_ERROR, "Ran out of Shared memory to allocate data for FastXSL cache, " + "in function %s() cannot allocate %ld bytes (%ld available, %ld allocated)", + get_active_function_name(TSRMLS_C), size, mm_available(FASTXSL_G(cache)->mm), + mm_maxsize() - mm_available(FASTXSL_G(cache)->mm)); + return NULL; + } + + FASTXSL_G(tmp_allocated_size) += size; + return ptr; +} + +void * +ShmCache_Calloc(size_t nmemb, size_t size) +{ + void *ptr; + +#ifdef DEBUGMM + if(!inshm) assert(0); +#endif + ptr = ShmCache_Malloc(nmemb * size); + memset(ptr, 0, nmemb * size); + return ptr; +} + +void * +ShmCache_Realloc(void *ptr, size_t size) +{ + void *newptr; + long oldsize; + TSRMLS_FETCH(); + +#ifdef DEBUGMM + if(!inshm) assert(0); +#endif + oldsize = mm_sizeof(FASTXSL_G(cache)->mm, ptr); + newptr = mm_realloc(FASTXSL_G(cache)->mm, ptr, size); + if (!newptr) { + TSRMLS_FETCH(); + php_error(E_ERROR, "Ran out of Shared memory to allocate data for FastXSL cache, " + "in function %s() cannot allocate %d bytes (%d available, %d allocated)", + get_active_function_name(TSRMLS_C), size, mm_available(FASTXSL_G(cache)->mm), + mm_maxsize() - mm_available(FASTXSL_G(cache)->mm)); + return NULL; + } + FASTXSL_G(tmp_allocated_size) += (size - oldsize); + return newptr; +} + +char * +ShmCache_Strdup(const char *string) +{ + char *newstring; + int string_length; + +#ifdef DEBUGMM + if(!inshm) assert(0); +#endif + string_length = strlen(string); + newstring = ShmCache_Malloc(string_length + 1); + memcpy(newstring, string, string_length); + newstring[string_length] = 0; + + return newstring; +} +#endif + +static void +Php_Free(void *ptr) +{ + efree(ptr); +} + +static void * +Php_Malloc(size_t size) +{ + return emalloc(size); +} + +static void * +Php_Realloc(void *ptr, size_t size) +{ + return erealloc(ptr, size); +} + +static char * +Php_Strdup(const char *string) +{ + return estrdup(string); +} + +#ifdef FASTXSL_MM +static void +ShmCache_UseAllocationFunctions(void) +{ + xmlMemSetup(ShmCache_Free, ShmCache_Malloc, ShmCache_Realloc, ShmCache_Strdup); +} +#endif + +static void +Php_UseAllocationFunctions(void) +{ + xmlMemSetup(Php_Free, Php_Malloc, Php_Realloc, Php_Strdup); +} + +static void +Xml_UseAllocationFunctions(void) +{ + xmlMemSetup(free_ptr, malloc_ptr, realloc_ptr, strdup); +} + +#ifdef FASTXSL_MM +static php_ss_wrapper * +ShmCache_Stylesheet_ParseAndStore(char *filename, size_t filename_len, int mtime TSRMLS_DC) +{ + php_ss_wrapper *wrapper; + int rv; + wrapper = SS_Wrapper_Alloc(FASTXSL_SHARED_ALLOC TSRMLS_CC); + + ShmCache_UseAllocationFunctions(); + wrapper->alloc_type = FASTXSL_SHMALLOC; + FASTXSL_G(tmp_allocated_size) = 0; + zend_set_timeout(0); + wrapper->data.ss = xsltParseStylesheetFile((unsigned char *)filename); + wrapper->data_type = FASTXSL_STYLESHEET; + Xml_UseAllocationFunctions(); + if (!wrapper->data.ss) { + _SS_Wrapper_Dtor(wrapper); + return NULL; + } + wrapper->mtime = mtime; + wrapper->allocsize = FASTXSL_G(tmp_allocated_size); + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + rv = fl_hash_add(FASTXSL_G(cache)->table, filename, filename_len, wrapper); + mm_unlock(FASTXSL_G(cache)->mm); + if(rv == 0) { + /* we failed */ + php_ss_wrapper *fallback; + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + fallback = fl_hash_find(FASTXSL_G(cache)->table, filename, + filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if(fallback && fallback->data_type == FASTXSL_STYLESHEET) { + ShmCache_Stylesheet_Free(wrapper); + wrapper = fallback; + } else { + } + } else { + } + + return wrapper; +} + +static void +ShmCache_Stylesheet_Free(php_ss_wrapper *wrapper TSRMLS_DC) +{ + if (wrapper->data_type == FASTXSL_STYLESHEET) { + if (wrapper->data.ss) { + //xmlCleanupParserr(); + ShmCache_UseAllocationFunctions(); +inshm = 1; + xsltFreeStylesheet(wrapper->data.ss); +inshm = 0; + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); + } + mm_free(FASTXSL_G(cache)->mm, wrapper); + } +} + +static void +ShmCache_XPathObject_Free(php_ss_wrapper *wrapper TSRMLS_DC) +{ + if (wrapper->data_type == FASTXSL_XPATHOBJ) { + if (wrapper->data.ss) { + //xmlCleanupParserr(); + ShmCache_UseAllocationFunctions(); +inshm = 1; + xmlXPathFreeObject(wrapper->data.op); +inshm = 0; + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); + } + mm_free(FASTXSL_G(cache)->mm, wrapper); + } +} + +static void +ShmCache_Document_Delete(char *filename, size_t filename_len) +{ + php_ss_wrapper *wrapper; + + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + wrapper = fl_hash_find(FASTXSL_G(cache)->table, filename, filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if (wrapper) { + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + fl_hash_delete(FASTXSL_G(cache)->table, filename, filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + switch(wrapper->data_type) { + case FASTXSL_STYLESHEET: + ShmCache_Stylesheet_Free(wrapper); + break; + case FASTXSL_XPATHOBJ: + ShmCache_XPathObject_Free(wrapper); + break; + default: + break; + } + } +} +#endif + +#ifdef FASTXSL_MM +/* {{{ proto void fastxsl_CachedDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) + Emulate xsltDocumentFunction but leverage the MM shared cache for speed. */ +static void +fastxsl_CachedDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathFunction func; + xmlXPathObjectPtr idoc, obj; + int lockfd; + int popped = 0; + char *ss_filename; + int ss_filename_len; + struct stat sb; + php_ss_wrapper *ss_wrapper; +#ifdef F_SETLK + struct flock lock; + + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; +#endif + xmlXPathStringFunction(ctxt, 1); + if (ctxt->value->type != XPATH_STRING) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg expecting a string\n"); + goto error; + } + obj = ctxt->value; + ss_filename_len = strlen((char *)obj->stringval); + ss_filename = alloca(ss_filename_len + 1); + strcpy(ss_filename, (char *)obj->stringval); + + lockfd = open(ss_filename, O_RDONLY); + if(lockfd < 0) { + /* FIXME non-existent file */ + goto error; + } + if (!FASTXSL_G(nostat)) { + if (fstat(lockfd, &sb) == -1) { + ShmCache_UseAllocationFunctions(); +inshm = 1; + ShmCache_Document_Delete(ss_filename, ss_filename_len TSRMLS_CC); +inshm = 0; + Xml_UseAllocationFunctions(); + close(lockfd); + goto error; + } + } else { + sb.st_mtime = 0; + } +#ifdef F_SETLK + lock.l_type = F_WRLCK; + fcntl(lockfd, F_SETLKW, &lock); +#else + flock(lockfd, LOCK_EX); +#endif + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RW); + ss_wrapper = fl_hash_find(FASTXSL_G(cache)->table, ss_filename, ss_filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if (!ss_wrapper) { + ss_wrapper = SS_Wrapper_Alloc(FASTXSL_SHARED_ALLOC TSRMLS_CC); + if(ss_wrapper) { + int rv; + ss_wrapper->alloc_type = FASTXSL_SHMALLOC; + ss_wrapper->data_type = FASTXSL_STYLESHEET; + func = xmlXPathFunctionLookup(ctxt->context, (const xmlChar *)"document"); + ShmCache_UseAllocationFunctions(); +inshm = 1; + FASTXSL_G(tmp_allocated_size) = 0; + func(ctxt, nargs); + ss_wrapper->data.op = ctxt->value; + ss_wrapper->allocsize = FASTXSL_G(tmp_allocated_size); + valuePop(ctxt); + popped = 1; +inshm = 0; + Xml_UseAllocationFunctions(); + ss_wrapper->mtime = sb.st_mtime; + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + rv = fl_hash_add(FASTXSL_G(cache)->table, ss_filename, ss_filename_len, ss_wrapper); + mm_unlock(FASTXSL_G(cache)->mm); + if(rv == 0) { + /* we failed */ + php_ss_wrapper *fallback; + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + fallback = fl_hash_find(FASTXSL_G(cache)->table, ss_filename, ss_filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if(fallback && fallback->data_type == FASTXSL_XPATHOBJ) { + ShmCache_UseAllocationFunctions(); +inshm = 1; + ShmCache_XPathObject_Free(ss_wrapper); +inshm = 0; + Xml_UseAllocationFunctions(); + ss_wrapper = fallback; + } else { + } + } else { + } + } + if (!ss_wrapper) { +#ifdef F_SETLK + lock.l_type = F_UNLCK; + fcntl(lockfd, F_SETLK, &lock); +#else + flock(lockfd, LOCK_UN); +#endif + close(lockfd); + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); + goto error; + } + } else { + if (!FASTXSL_G(nostat)) { + if (ss_wrapper->mtime != sb.st_mtime) { + ShmCache_UseAllocationFunctions(); +inshm = 1; + ShmCache_Document_Delete(ss_filename, ss_filename_len TSRMLS_CC); +inshm = 0; + Xml_UseAllocationFunctions(); + ss_wrapper = SS_Wrapper_Alloc(FASTXSL_SHARED_ALLOC TSRMLS_CC); + if(ss_wrapper) { + int rv; + ss_wrapper->alloc_type = FASTXSL_SHMALLOC; + ss_wrapper->data_type = FASTXSL_STYLESHEET; + func = xmlXPathFunctionLookup(ctxt->context, (const xmlChar *)"document"); + ShmCache_UseAllocationFunctions(); +inshm = 1; + FASTXSL_G(tmp_allocated_size) = 0; + func(ctxt, nargs); + ss_wrapper->data.op = ctxt->value; + valuePop(ctxt); + popped = 1; +inshm = 0; + Xml_UseAllocationFunctions(); + ss_wrapper->mtime = sb.st_mtime; + ss_wrapper->allocsize = FASTXSL_G(tmp_allocated_size); + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + rv = fl_hash_add(FASTXSL_G(cache)->table, ss_filename, ss_filename_len, ss_wrapper); + mm_unlock(FASTXSL_G(cache)->mm); + if(rv == 0) { + /* we failed */ + php_ss_wrapper *fallback; + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + fallback = fl_hash_find(FASTXSL_G(cache)->table, ss_filename, ss_filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if(fallback && fallback->data_type == FASTXSL_XPATHOBJ) { + ShmCache_UseAllocationFunctions(); +inshm = 1; + ShmCache_XPathObject_Free(ss_wrapper); +inshm = 0; + Xml_UseAllocationFunctions(); + ss_wrapper = fallback; + } else { + } + } else { + } + } + //xmlCleanupParserr(); + if (!ss_wrapper) { +#ifdef F_SETLK + lock.l_type = F_UNLCK; + fcntl(lockfd, F_SETLK, &lock); +#else + flock(lockfd, LOCK_UN); +#endif + close(lockfd); + goto error; + } + } + } + } + ss_wrapper->hits++; + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); +#ifdef F_SETLK + lock.l_type = F_UNLCK; + fcntl(lockfd, F_SETLK, &lock); +#else + flock(lockfd, LOCK_UN); +#endif + close(lockfd); + + if(!popped) valuePop(ctxt); + valuePush(ctxt, xmlXPathObjectCopy(ss_wrapper->data.op)); + return; +error: + ctxt->error = XPATH_INVALID_TYPE; + return; +} +/* }}} */ +#endif + +static php_ss_wrapper * +PrmCache_Stylesheet_ParseAndStore(char *filename, size_t filename_len, int mtime TSRMLS_DC) +{ + php_ss_wrapper *wrapper; + + wrapper = (php_ss_wrapper *) SS_Wrapper_Alloc(FASTXSL_PRM_ALLOC TSRMLS_CC); + wrapper->alloc_type = FASTXSL_PRMALLOC; + + wrapper->data.ss = xsltParseStylesheetFile((xmlChar *)filename); + wrapper->data_type = FASTXSL_STYLESHEET; + if (!wrapper->data.ss) { + return NULL; + } + wrapper->mtime = mtime; + + if(fl_hash_add(FASTXSL_G(cache)->prmtable, filename, filename_len, wrapper) == 0) { + /* we failed, not much we can do here, and no race */ + } + + return wrapper; +} + +static void +PrmCache_Stylesheet_Free(php_ss_wrapper *wrapper TSRMLS_DC) +{ + if (wrapper->data_type == FASTXSL_STYLESHEET) { + if (wrapper->data.ss) { + xsltFreeStylesheet(wrapper->data.ss); + } + free(wrapper); + } +} + +static void +PrmCache_XPathObject_Free(php_ss_wrapper *wrapper TSRMLS_DC) +{ + if (wrapper->data_type == FASTXSL_XPATHOBJ) { + if (wrapper->data.op) { + xmlXPathFreeObject(wrapper->data.op); + } + free(wrapper); + } +} + +static void +PrmCache_Document_Delete(char *filename, size_t filename_len TSRMLS_DC) +{ + php_ss_wrapper *wrapper; + + wrapper = fl_hash_find(FASTXSL_G(cache)->prmtable, filename, filename_len); + if (wrapper) { + fl_hash_delete(FASTXSL_G(cache)->prmtable, filename, filename_len); + switch(wrapper->data_type) { + case FASTXSL_STYLESHEET: + PrmCache_Stylesheet_Free(wrapper TSRMLS_CC); + break; + case FASTXSL_XPATHOBJ: + PrmCache_XPathObject_Free(wrapper TSRMLS_CC); + break; + default: + break; + } + } +} + +/* {{{ proto array fastxsl_prmcache_getstatistics(void) + Get an array of statistics regarding the fastxsl documents in the process resident memory cache */ +PHP_FUNCTION(fastxsl_prmcache_getstatistics) +{ + php_ss_wrapper *wrapper; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { + return; + } + +} +/* }}} */ + +/* {{{ proto array fastxsl_shmcache_getstatistics(void) + Get an array of statistics regarding the documents in the shared memory cache */ +#ifdef FASTXSL_MM +PHP_FUNCTION(fastxsl_shmcache_getstatistics) +{ + php_ss_wrapper *ss_wrapper; + FL_Bucket *bucket; + zval *files_array; + zval *info_array; + int i; + long allocated_bytes = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { + return; + } + + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + + array_init(return_value); + + MAKE_STD_ZVAL(files_array); + array_init(files_array); + + for (i = 0; i < FL_HASH_SIZE; i++) { + for (bucket = FASTXSL_G(cache)->table->buckets[i]; bucket != NULL; bucket = bucket->next) { + ss_wrapper = (php_ss_wrapper *) bucket->data; + + MAKE_STD_ZVAL(info_array); + array_init(info_array); + + add_assoc_long(info_array, "allocated", ss_wrapper->allocsize); + add_assoc_long(info_array, "hits", ss_wrapper->hits); + add_assoc_long(info_array, "mtime", ss_wrapper->mtime); + + add_assoc_zval(files_array, bucket->key, info_array); + + allocated_bytes += ss_wrapper->allocsize; + } + } + add_assoc_zval(return_value, "files", files_array); + + add_assoc_long(return_value, "apparent_allocated", allocated_bytes); + add_assoc_long(return_value, "allocated", + (FASTXSL_G(memalloc)?FASTXSL_G(memalloc):mm_maxsize()) - mm_available(FASTXSL_G(cache)->mm)); + add_assoc_long(return_value, "shm_size", (long) FASTXSL_G(memalloc)?FASTXSL_G(memalloc):mm_maxsize()); + mm_unlock(FASTXSL_G(cache)->mm); +} + +#endif +/* }}} */ + +/* {{{ proto array fastxsl_version(void) + Returns the version number */ +PHP_FUNCTION(fastxsl_version) +{ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { + return; + } + RETURN_STRING(PHP_FASTXSL_VERSION, 1); +} + +/* }}} */ + +/* {{{ proto resource fastxsl_stylesheet_parsefile(string filename) + Parse a stylesheet file located at filename. */ +PHP_FUNCTION(fastxsl_stylesheet_parsefile) +{ + php_ss_wrapper *wrapper; + char *filename; + size_t filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, + &filename_len) == FAILURE) { + return; + } + + wrapper = (php_ss_wrapper *) SS_Wrapper_Alloc(0 TSRMLS_CC); + wrapper->alloc_type = FASTXSL_PRMALLOC; + Xml_UseAllocationFunctions(); + wrapper->data.ss = xsltParseStylesheetFile((xmlChar*)filename); + if (!wrapper->data.ss) { + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, wrapper, le_fastxsl_stylesheet); +} +/* }}} */ + +/* {{{ proto resource fastxsl_xml_parsestring(string text) + Parse a string containing an XML document and return a resource pointer to the resulting libxml2 tree */ +PHP_FUNCTION(fastxsl_xml_parsestring) +{ + php_xd_wrapper *wrapper; + char *text; + size_t text_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &text, &text_len) == FAILURE) { + return; + } + + wrapper = XD_Wrapper_Alloc(); + wrapper->alloc_type = FASTXSL_PRMALLOC; + Xml_UseAllocationFunctions(); + wrapper->xd = xmlParseDoc((xmlChar *) text); + if (!wrapper->xd) { + _XD_Wrapper_Dtor(wrapper); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, wrapper, le_fastxsl_document); +} +/* }}} */ + +/* {{{ proto resource fastxsl_xml_parsefile(string filename) + Parse an XML file into a FastXSL resource */ +PHP_FUNCTION(fastxsl_xml_parsefile) +{ + php_xd_wrapper *wrapper; + char *filename; + size_t filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, + &filename_len) == FAILURE) { + return; + } + + wrapper = XD_Wrapper_Alloc(); + wrapper->alloc_type = FASTXSL_PRMALLOC; + wrapper->xd = xmlParseFile((const char *) filename); + if (!wrapper->xd) { + _XD_Wrapper_Dtor(wrapper); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, wrapper, le_fastxsl_document); +} +/* }}} */ + +static int +ParseTransformParameters(zval *z_parameters, char ***parameters TSRMLS_DC) +{ + zval **current; + HashTable *h_parameters; + HashPosition pos; + char *key; + uint key_length; + unsigned long ival; + int index = 0; + + h_parameters = Z_ARRVAL_P(z_parameters); + + if (zend_hash_num_elements(h_parameters) == 0) { + *parameters = NULL; + return SUCCESS; + } + + *parameters = calloc(1, (zend_hash_num_elements(h_parameters) * (2 * sizeof(char *))) + 1); + if (!*parameters) { + php_error(E_WARNING, "Cannot allocate parameters array to pass to FastXSL"); + return FAILURE; + } + + for (zend_hash_internal_pointer_reset_ex(h_parameters, &pos); + zend_hash_get_current_data_ex(h_parameters, (void **) ¤t, &pos) == SUCCESS; + zend_hash_move_forward_ex(h_parameters, &pos)) { + if (zend_hash_get_current_key_ex(h_parameters, &key, &key_length, + &ival, 0, &pos) == HASH_KEY_IS_LONG) { + efree(*parameters); + *parameters = NULL; + + php_error(E_WARNING, + "Parameters array passed to %s() may not contain numeric keys", + get_active_function_name(TSRMLS_C)); + return FAILURE; + } + + convert_to_string_ex(current); + + (*parameters)[index++] = key; + (*parameters)[index++] = Z_STRVAL_PP(current); + } + (*parameters)[index] = NULL; + + return SUCCESS; +} + +/* {{{ proto resource fastxsl_shmcache_transform(string filename, resource xmldoc[, array parameters]) + Transform a XML document, "xmldoc", by a XSL stylesheet "filename" with transform "parameters." */ +#ifdef FASTXSL_MM +PHP_FUNCTION(fastxsl_shmcache_transform) +{ + xsltTransformContextPtr ctxt; + char **parameters = NULL; + php_xd_wrapper *xd_wrapper; + php_xd_wrapper *result_wrapper; + php_ss_wrapper *ss_wrapper; + char *ss_filename; + size_t ss_filename_len; + zval *z_xd_wrapper; + zval *z_parameters; + struct stat sb; + int lockfd; +#ifdef F_SETLK + struct flock lock; + + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; +#endif + + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &ss_filename, &ss_filename_len, + &z_xd_wrapper, &z_parameters) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + + if (ZEND_NUM_ARGS() > 2 && Z_TYPE_P(z_parameters) == IS_ARRAY) { + if (ParseTransformParameters(z_parameters, ¶meters TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + } + + result_wrapper = XD_Wrapper_Alloc(); + result_wrapper->alloc_type = FASTXSL_PRMALLOC; + lockfd = open(ss_filename, O_RDONLY); + if(lockfd < 0) { + /* FIXME non-existent file */ + RETURN_FALSE; + } + if (!FASTXSL_G(nostat)) { + if (fstat(lockfd, &sb) == -1) { + ShmCache_UseAllocationFunctions(); +inshm = 1; + ShmCache_Document_Delete(ss_filename, ss_filename_len TSRMLS_CC); +inshm = 0; + Xml_UseAllocationFunctions(); + _XD_Wrapper_Dtor(result_wrapper); + free(parameters); + close(lockfd); + RETURN_FALSE; + } + } else { + sb.st_mtime = 0; + } +#ifdef F_SETLK + lock.l_type = F_WRLCK; + fcntl(lockfd, F_SETLKW, &lock); +#else + flock(lockfd, LOCK_EX); +#endif + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RW); + ss_wrapper = fl_hash_find(FASTXSL_G(cache)->table, ss_filename, ss_filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if (!ss_wrapper) { + ShmCache_UseAllocationFunctions(); +inshm = 1; + ss_wrapper = ShmCache_Stylesheet_ParseAndStore(ss_filename, ss_filename_len, sb.st_mtime); +inshm = 0; + if (!ss_wrapper) { +#ifdef F_SETLK + lock.l_type = F_UNLCK; + fcntl(lockfd, F_SETLK, &lock); +#else + flock(lockfd, LOCK_UN); +#endif + close(lockfd); + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); + _XD_Wrapper_Dtor(result_wrapper); + free(parameters); + RETURN_FALSE; + } + } else { + if (!FASTXSL_G(nostat)) { + if (ss_wrapper->mtime != sb.st_mtime) { + //xmlCleanupParserr(); + ShmCache_UseAllocationFunctions(); +inshm = 1; + ShmCache_Document_Delete(ss_filename, ss_filename_len TSRMLS_CC); + ss_wrapper = ShmCache_Stylesheet_ParseAndStore(ss_filename, ss_filename_len, sb.st_mtime TSRMLS_CC); +inshm = 0; + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); + if (!ss_wrapper) { +#ifdef F_SETLK + lock.l_type = F_UNLCK; + fcntl(lockfd, F_SETLK, &lock); +#else + flock(lockfd, LOCK_UN); +#endif + close(lockfd); + _XD_Wrapper_Dtor(result_wrapper); + free(parameters); + RETURN_FALSE; + } + } + } + } + ss_wrapper->hits++; + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); +#ifdef F_SETLK + lock.l_type = F_UNLCK; + fcntl(lockfd, F_SETLK, &lock); +#else + flock(lockfd, LOCK_UN); +#endif + close(lockfd); + + ctxt = xsltNewTransformContext(ss_wrapper->data.ss, xd_wrapper->xd); +#ifdef FASTXSL_MM + xmlXPathRegisterFunc(ctxt->xpathCtxt, (const xmlChar *) "cached_document", + fastxsl_CachedDocumentFunction); +#endif + result_wrapper->xd = xsltApplyStylesheetUser(ss_wrapper->data.ss, xd_wrapper->xd, + (const char **) parameters, NULL, NULL, ctxt); + xsltFreeTransformContext(ctxt); + + if (parameters) + free(parameters); + + if (!result_wrapper->xd) { + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, result_wrapper, le_fastxsl_document); +} +#endif +/* }}} */ + +/* {{{ proto resource fastxsl_prmcache_transform(string filename, resource xmldoc[, array parameters]) + Transform a XML document, "xmldoc", by a XSL stylesheet "filename" with transform "parameters." */ +PHP_FUNCTION(fastxsl_prmcache_transform) +{ + char **parameters = NULL; + php_xd_wrapper *xd_wrapper; + php_xd_wrapper *result_wrapper; + php_ss_wrapper *ss_wrapper; + char *ss_filename; + size_t ss_filename_len; + zval *z_xd_wrapper; + zval *z_parameters; + struct stat sb; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &ss_filename, &ss_filename_len, + &z_xd_wrapper, &z_parameters) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + + if (ZEND_NUM_ARGS() > 2 && Z_TYPE_P(z_parameters) == IS_ARRAY) { + if (ParseTransformParameters(z_parameters, ¶meters TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + } + + result_wrapper = XD_Wrapper_Alloc(); + result_wrapper->alloc_type = FASTXSL_PRMALLOC; + + if (!FASTXSL_G(nostat)) { + if (stat(ss_filename, &sb) == -1) { + PrmCache_Document_Delete(ss_filename, ss_filename_len TSRMLS_CC); + _XD_Wrapper_Dtor(result_wrapper); + free(parameters); + RETURN_FALSE; + } + } else { + sb.st_mtime = 0; + } + + ss_wrapper = fl_hash_find(FASTXSL_G(cache)->prmtable, ss_filename, ss_filename_len); + if (!ss_wrapper) { + ss_wrapper = PrmCache_Stylesheet_ParseAndStore(ss_filename, ss_filename_len, sb.st_mtime TSRMLS_CC); + if (!ss_wrapper) { + _XD_Wrapper_Dtor(result_wrapper); + free(parameters); + RETURN_FALSE; + } + } else { + if (!FASTXSL_G(nostat)) { + if (ss_wrapper->mtime != sb.st_mtime) { + PrmCache_Document_Delete(ss_filename, ss_filename_len TSRMLS_CC); + ss_wrapper = PrmCache_Stylesheet_ParseAndStore(ss_filename, ss_filename_len, sb.st_mtime TSRMLS_CC); + if (!ss_wrapper) { + _XD_Wrapper_Dtor(result_wrapper); + free(parameters); + RETURN_FALSE; + } + } + } + } + ss_wrapper->hits++; + result_wrapper->xd = xsltApplyStylesheet(ss_wrapper->data.ss, xd_wrapper->xd, + (const char **) parameters); + ////xmlCleanupParserr(); + + if (parameters) + free(parameters); + + if (!result_wrapper->xd) { + _XD_Wrapper_Dtor(result_wrapper); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, result_wrapper, le_fastxsl_document); +} + +/* {{{ proto void fastxsl_nocache_transform(resource stylesheet, resource xmldoc[, array parameters]) + Transform the stylesheet document by the xml document with parameters. */ +PHP_FUNCTION(fastxsl_nocache_transform) +{ + char **parameters = NULL; + php_xd_wrapper *xd_wrapper; + php_xd_wrapper *result_wrapper; + php_ss_wrapper *ss_wrapper; + zval *z_xd_wrapper; + zval *z_ss_wrapper; + zval *z_parameters; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|z", &z_ss_wrapper, + &z_xd_wrapper, &z_parameters) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(ss_wrapper, php_ss_wrapper *, &z_ss_wrapper, -1, "FastXSL Stylesheet", + le_fastxsl_stylesheet); + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + + if (ZEND_NUM_ARGS() > 2 && Z_TYPE_P(z_parameters) == IS_ARRAY) { + if (ParseTransformParameters(z_parameters, ¶meters TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + } + + result_wrapper = XD_Wrapper_Alloc(); + result_wrapper->alloc_type = FASTXSL_PRMALLOC; + result_wrapper->xd = xsltApplyStylesheet(ss_wrapper->data.ss, xd_wrapper->xd, + (const char **) parameters); + if (parameters) + free(parameters); + + if (!result_wrapper->xd) { + _XD_Wrapper_Dtor(result_wrapper); + RETURN_FALSE; + } + + + ZEND_REGISTER_RESOURCE(return_value, result_wrapper, le_fastxsl_document); +} +/* }}} */ + +/* {{{ proto void fastxsl_nocache_profile(resource stylesheet, resource xmldoc[, array parameters, string filename]) + Profile the stylesheet document by the xml document with parameters and output the results to filename (or stderr, if filename doesn't exist). */ +PHP_FUNCTION(fastxsl_nocache_profile) +{ + char **parameters = NULL; + char *filename = "php://stderr"; + php_xd_wrapper *xd_wrapper; + php_xd_wrapper *result_wrapper; + php_ss_wrapper *ss_wrapper; + zval *z_xd_wrapper; + zval *z_ss_wrapper; + zval *z_parameters; + FILE *dbgprof; + int filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|zs", &z_ss_wrapper, + &z_xd_wrapper, &z_parameters, &filename, &filename_len) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(ss_wrapper, php_ss_wrapper *, &z_ss_wrapper, -1, "FastXSL Stylesheet", + le_fastxsl_stylesheet); + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + + if (ZEND_NUM_ARGS() > 2 && Z_TYPE_P(z_parameters) == IS_ARRAY) { + if (ParseTransformParameters(z_parameters, ¶meters TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + } + + if (!strcmp(filename, "php://stdout")) { + dbgprof = stdout; + } else if (!strcmp(filename, "php://stderr")) { + dbgprof = stderr; + } else { + dbgprof = fopen(filename, "w"); + } + + result_wrapper = XD_Wrapper_Alloc(); + result_wrapper->alloc_type = FASTXSL_PRMALLOC; + + result_wrapper->xd = xsltProfileStylesheet(ss_wrapper->data.ss, xd_wrapper->xd, + (const char **) parameters, dbgprof); + if (parameters) + free(parameters); + + fclose(dbgprof); + + if (!result_wrapper->xd) { + _XD_Wrapper_Dtor(result_wrapper); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, result_wrapper, le_fastxsl_document); +} +/* }}} */ + +/* {{{ proto string fastxsl_nocache_tostring(resource stylesheet, resource xmldoc) + Return the contents of an XML stylesheet result as a string */ +PHP_FUNCTION(fastxsl_nocache_tostring) +{ + zval *z_xd_wrapper; + zval *z_ss_wrapper; + php_ss_wrapper *ss_wrapper; + php_xd_wrapper *xd_wrapper; + xmlChar *result = NULL; + int length; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &z_ss_wrapper, + &z_xd_wrapper) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(ss_wrapper, php_ss_wrapper *, &z_ss_wrapper, -1, "FastXSL XML Stylesheet", + le_fastxsl_stylesheet); + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + + xsltSaveResultToString(&result, &length, xd_wrapper->xd, ss_wrapper->data.ss); + + if (result) { + RETVAL_STRINGL((char *) result, length, 1); + xmlFree(result); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string fastxsl_shmcache_tostring(string filename, resource xmldoc) + Return the string representation of xmldoc which is the result of an XSLT transformation on filename */ +#ifdef FASTXSL_MM +PHP_FUNCTION(fastxsl_shmcache_tostring) +{ + zval *z_xd_wrapper; + php_ss_wrapper *ss_wrapper; + php_xd_wrapper *xd_wrapper; + xmlChar *result = NULL; + char *ss_filename; + size_t ss_filename_len; + int length; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &ss_filename, &ss_filename_len, + &z_xd_wrapper) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + Xml_UseAllocationFunctions(); + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RD); + ss_wrapper = fl_hash_find(FASTXSL_G(cache)->table, ss_filename, ss_filename_len); + mm_unlock(FASTXSL_G(cache)->mm); + if (!ss_wrapper) { + RETURN_FALSE; + } + ss_wrapper->hits++; + xsltSaveResultToString(&result, &length, xd_wrapper->xd, ss_wrapper->data.ss); + if (result) { + RETVAL_STRINGL((char *) result, length, 1); + xmlFree(result); + } else { + RETVAL_FALSE; + } + Xml_UseAllocationFunctions(); +} +#endif +/* }}} */ + +/* {{{ proto string fastxsl_prmcache_tostring(string filename, resource xmldoc) + Return the string representation of xmldoc which is the result of an XSLT transformation on filename */ +PHP_FUNCTION(fastxsl_prmcache_tostring) +{ + zval *z_xd_wrapper; + php_ss_wrapper *ss_wrapper; + php_xd_wrapper *xd_wrapper; + xmlChar *result = NULL; + char *ss_filename; + size_t ss_filename_len; + int length; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &ss_filename, &ss_filename_len, + &z_xd_wrapper) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(xd_wrapper, php_xd_wrapper *, &z_xd_wrapper, -1, "FastXSL XML Document", + le_fastxsl_document); + + ss_wrapper = fl_hash_find(FASTXSL_G(cache)->prmtable, ss_filename, ss_filename_len); + if (!ss_wrapper) { + RETURN_FALSE; + } + ss_wrapper->hits++; + xsltSaveResultToString(&result, &length, xd_wrapper->xd, ss_wrapper->data.ss); + + if (result) { + RETVAL_STRINGL((char *) result, length, 1); + xmlFree(result); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +function_entry fastxsl_functions[] = { + PHP_FE(fastxsl_stylesheet_parsefile, NULL) + PHP_FE(fastxsl_xml_parsefile, NULL) + PHP_FE(fastxsl_xml_parsestring, NULL) +#ifdef FASTXSL_MM + PHP_FE(fastxsl_shmcache_transform, NULL) + PHP_FE(fastxsl_shmcache_tostring, NULL) + PHP_FE(fastxsl_shmcache_getstatistics, NULL) +#endif + PHP_FE(fastxsl_version, NULL) + PHP_FE(fastxsl_prmcache_transform, NULL) + PHP_FE(fastxsl_nocache_transform, NULL) + PHP_FE(fastxsl_nocache_profile, NULL) + PHP_FE(fastxsl_nocache_tostring, NULL) + PHP_FE(fastxsl_prmcache_tostring, NULL) + PHP_FE(fastxsl_prmcache_getstatistics, NULL) + {NULL, NULL, NULL} +}; + + +zend_module_entry fastxsl_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "fastxsl", + fastxsl_functions, + PHP_MINIT(fastxsl), + PHP_MSHUTDOWN(fastxsl), + PHP_RINIT(fastxsl), + PHP_RSHUTDOWN(fastxsl), + PHP_MINFO(fastxsl), +#if ZEND_MODULE_API_NO >= 20010901 + "0.1", +#endif + STANDARD_MODULE_PROPERTIES +}; + + +#ifdef COMPILE_DL_FASTXSL +ZEND_GET_MODULE(fastxsl) +#endif + +static void +_SS_Wrapper_Dtor(php_ss_wrapper *wrapper) +{ + if(wrapper->data.ss) { + if(wrapper->alloc_type == FASTXSL_SHMALLOC) { + //xmlCleanupParserr(); + ShmCache_UseAllocationFunctions(); + } else { + Xml_UseAllocationFunctions(); + } + inshm = 1; + xsltFreeStylesheet(wrapper->data.ss); + inshm = 0; + if(wrapper->alloc_type == FASTXSL_SHMALLOC) { + //xmlCleanupParserr(); + Xml_UseAllocationFunctions(); + } + } + if(wrapper->persistant) { +#ifdef FASTXSL_MM + mm_free(FASTXSL_G(cache)->mm, wrapper); +#endif + } else { + free(wrapper); + } +} + +static void +SS_Wrapper_Dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_ss_wrapper *wrapper = (php_ss_wrapper *) rsrc->ptr; + if (wrapper->persistant) { + return; + } + _SS_Wrapper_Dtor(wrapper); +} + +static void _XD_Wrapper_Dtor(php_xd_wrapper *wrapper) { + if (wrapper->xd) { + if(wrapper->alloc_type == FASTXSL_SHMALLOC) { + ShmCache_UseAllocationFunctions(); + } else { + Xml_UseAllocationFunctions(); + } +inshm = 1; + xmlFreeDoc(wrapper->xd); +inshm = 0; + Xml_UseAllocationFunctions(); + } + free(wrapper); +} + +static void +XD_Wrapper_Dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_xd_wrapper *wrapper = (php_xd_wrapper *) rsrc->ptr; + _XD_Wrapper_Dtor(wrapper); +} + +void fastxsl_errorfunc(void *ctx, const char *msg, ...) +{ + char *frag; + int fraglen; + int output = 0; + va_list args; + TSRMLS_FETCH(); + + va_start(args, msg); + fraglen = vspprintf(&frag, 0, msg, args); + while(fraglen && frag[fraglen - 1] == '\n') { + frag[--fraglen] = '\0'; + output = 1; + } + if(fraglen) { + if(FASTXSL_G(errbuf)) { + FASTXSL_G(errbuf) = erealloc(FASTXSL_G(errbuf), fraglen + strlen(FASTXSL_G(errbuf))); + strcat(FASTXSL_G(errbuf), frag); + } else { + FASTXSL_G(errbuf) = frag; + } + } + va_end(args); + if(output) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, FASTXSL_G(errbuf)); + efree(FASTXSL_G(errbuf)); + FASTXSL_G(errbuf) = NULL; + } +} + +static int +Stream_MatchWrapper(const char *filename) +{ + TSRMLS_FETCH(); + return php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) ? 1 : 0; +} + +static void * +Stream_XmlWrite_OpenWrapper(const char *filename) +{ + TSRMLS_FETCH(); + return php_stream_open_wrapper((char *) filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); +} + +static int +Stream_XmlWrite_WriteWrapper(void *context, const char *buffer, int length) +{ + TSRMLS_FETCH(); + return php_stream_write((php_stream *) context, (char *) buffer, length); +} + +static int +Stream_CloseWrapper(void *context) +{ + TSRMLS_FETCH(); + return php_stream_close((php_stream *) context); +} + + +extern int xmlLoadExtDtdDefaultValue; + +/** + * Allocators for the fl_hash storage + */ +#ifdef FASTXSL_MM +static FL_Allocator mm_allocators = {ShmCache_Free, ShmCache_Malloc, ShmCache_Calloc}; +#endif +static FL_Allocator normal_allocators = {free, malloc, calloc}; + +static void +php_fastxsl_init_globals(zend_fastxsl_globals *globals) +{ + memset(globals, 0, sizeof(zend_fastxsl_globals)); + + globals->cache = calloc(1, sizeof(fl_cache)); +} + + +static void +php_fastxsl_destroy_globals(zend_fastxsl_globals *globals) +{ + fl_cache *cache; + + cache = globals->cache; + if (cache) { + if (cache->owner != getpid()) { + return; + } + fl_hash_free(cache->prmtable); + } +} + +/* {{{ php function -> xslt exporting + */ + +static void fastxsl_ext_function(xmlXPathParserContextPtr ctxt, int nargs) +{ + xsltTransformContextPtr tctxt; + zval **params; + zval *param; + zval *function = NULL; + zval *ret = NULL; + xmlChar *fname; + int param_count = nargs - 1; + int i; + TSRMLS_FETCH(); + + tctxt = (xsltTransformContextPtr) xsltXPathGetTransformContext(ctxt); + ////xmlCleanupParserr(); + if (tctxt == NULL) { + xsltGenericError(xsltGenericErrorContext, + "fastxsl extension functions: failed to get the transformation context\n"); + return; + } + /* allocate for args. first position is function name */ + params = emalloc(sizeof(zval *) * param_count); + for(i = param_count - 1; i >=0; i--) { + xmlXPathObjectPtr obj; + xmlChar *tmp; + obj = valuePop(ctxt); + MAKE_STD_ZVAL(param); + switch(obj->type) { + case XPATH_UNDEFINED: + ZVAL_NULL(param); + break; + case XPATH_NODESET: + tmp = xmlXPathCastToString(obj); + ZVAL_STRING(param, (char *) tmp, 1); + xmlFree(tmp); + break; + case XPATH_BOOLEAN: + ZVAL_BOOL(param, obj->boolval); + break; + case XPATH_NUMBER: + ZVAL_DOUBLE(param, obj->floatval); + break; + case XPATH_STRING: + ZVAL_STRING(param, (char *)obj->stringval, 1); + break; + default: + zend_error(E_WARNING, "inexact type conversion %d", obj->type); + tmp = xmlXPathCastToString(obj); + ZVAL_STRING(param, (char *)tmp, 1); + xmlFree(tmp); + break; + } + params[i] = param; + xmlXPathFreeObject(obj); + } + fname = xmlXPathPopString(ctxt); + if(!fname) { + xsltGenericError(xsltGenericDebugContext, + "passed function name is not a string"); + xmlXPathReturnEmptyString(ctxt); + goto cleanup; + } + MAKE_STD_ZVAL(function); + ZVAL_STRING(function, (char *)fname, 1); + xmlFree(fname); + MAKE_STD_ZVAL(ret); + ZVAL_FALSE(ret); + if(call_user_function(EG(function_table), NULL, function, ret, + param_count, params TSRMLS_CC) == FAILURE) { + xsltGenericError(xsltGenericDebugContext, + "function evaluation error"); + xmlXPathReturnEmptyString(ctxt); + goto cleanup; + } + switch(ret->type) { + case IS_BOOL: + xmlXPathReturnBoolean(ctxt, Z_BVAL_P(ret)); + break; + case IS_LONG: + xmlXPathReturnNumber(ctxt, (float) Z_LVAL_P(ret)); + break; + case IS_DOUBLE: + xmlXPathReturnNumber(ctxt, Z_DVAL_P(ret)); + break; + case IS_STRING: + xmlXPathReturnString(ctxt, xmlStrdup((xmlChar *) Z_STRVAL_P(ret))); + break; + default: + convert_to_string_ex(&ret); + xmlXPathReturnString(ctxt, xmlStrdup((xmlChar *) Z_STRVAL_P(ret))); + break; + } +cleanup: + if(params) { + for(i=0; i < param_count; i++) { + zval_ptr_dtor(¶ms[i]); + } + efree(params); params = NULL; + } + if(function) { + efree(function); function = NULL; + } + if(ret) { + efree(ret); ret = NULL; + } + return; +} +/* }}} */ + + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("fastxsl.shmpath", "/tmp/fastxsl_mem", PHP_INI_SYSTEM, OnUpdateString, shmpath, zend_fastxsl_globals, fastxsl_globals) + STD_PHP_INI_BOOLEAN("fastxsl.nostat", "0", PHP_INI_ALL, OnUpdateLong, nostat, zend_fastxsl_globals, fastxsl_globals) + STD_PHP_INI_BOOLEAN("fastxsl.memalloc", "0", PHP_INI_SYSTEM, OnUpdateLong, memalloc, zend_fastxsl_globals, fastxsl_globals) + STD_PHP_INI_BOOLEAN("fastxsl.register_functions", "0", PHP_INI_ALL, OnUpdateLong, register_functions, zend_fastxsl_globals, fastxsl_globals) +PHP_INI_END() + +PHP_MINIT_FUNCTION(fastxsl) +{ + char *shmpath; + size_t shmpath_len; + char euid[30]; + + ZEND_INIT_MODULE_GLOBALS(fastxsl, php_fastxsl_init_globals, php_fastxsl_destroy_globals); + + REGISTER_INI_ENTRIES(); + + le_fastxsl_stylesheet = zend_register_list_destructors_ex(SS_Wrapper_Dtor, NULL, + le_fastxsl_stylesheet_name, module_number); + le_fastxsl_document = zend_register_list_destructors_ex(XD_Wrapper_Dtor, NULL, + le_fastxsl_document_name, module_number); +#ifdef FASTXSL_MM + if (!sprintf(euid, "%d", geteuid())) { + return FAILURE; + } + + shmpath_len = strlen(FASTXSL_G(shmpath)) + strlen(euid); + shmpath = do_alloca(shmpath_len + 1); + + strcpy(shmpath, FASTXSL_G(shmpath)); + strcat(shmpath, euid); + + FASTXSL_G(cache)->owner = getpid(); + FASTXSL_G(cache)->mm = mm_create(FASTXSL_G(memalloc), shmpath); + free_alloca(shmpath); + if (!FASTXSL_G(cache)->mm) { + return FAILURE; + } + + mm_lock(FASTXSL_G(cache)->mm, MM_LOCK_RW); + FASTXSL_G(cache)->table = fl_hash_new(&mm_allocators, NULL); + mm_unlock(FASTXSL_G(cache)->mm); +#endif + FASTXSL_G(cache)->prmtable = fl_hash_new(&normal_allocators, NULL); + //xmlGcMemGet(&free_ptr, &malloc_ptr, &mallocatomic_ptr, &realloc_ptr, &strdup_ptr); + xmlMemGet(&free_ptr, &malloc_ptr, &realloc_ptr, &strdup_ptr); + //xmlCleanupParserr(); + ShmCache_UseAllocationFunctions(); +inshm = 1; + xsltRegisterAllExtras(); +#if HAVE_DOMEXSLT + exsltRegisterAll(); +#endif + xmlSubstituteEntitiesDefault(1); + xmlLoadExtDtdDefaultValue = 1; + + xmlRegisterOutputCallbacks(Stream_MatchWrapper, Stream_XmlWrite_OpenWrapper, + Stream_XmlWrite_WriteWrapper, Stream_CloseWrapper); + xsltSetGenericErrorFunc(NULL, fastxsl_errorfunc); + xmlSetGenericErrorFunc(NULL, fastxsl_errorfunc); + + if(FASTXSL_G(register_functions)) { + xsltRegisterExtModuleFunction ((const xmlChar *) "function", + (const xmlChar *) "http://php.net/fastxsl", + fastxsl_ext_function); + } + xsltRegisterExtModuleFunction ((const xmlChar *) "cached_document", + (const xmlChar *) "http://php.net/fastxsl/cached_document", + fastxsl_CachedDocumentFunction); + //xmlCleanupParserr(); +inshm = 0; + Xml_UseAllocationFunctions(); + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(fastxsl) +{ + UNREGISTER_INI_ENTRIES(); +} +PHP_RINIT_FUNCTION(fastxsl) +{ +#ifdef FASTXSL_MM + /* just in case, set back the allocators */ + Xml_UseAllocationFunctions(); +#endif +} +PHP_RSHUTDOWN_FUNCTION(fastxsl) +{ +#ifdef FASTXSL_MM + /* just in case, set back the allocators */ + Xml_UseAllocationFunctions(); +#endif +} + +PHP_MINFO_FUNCTION(fastxsl) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "fastxsl support", "enabled"); + php_info_print_table_row(2, "libxml Version", LIBXML_DOTTED_VERSION); + php_info_print_table_row(2, "libxslt Version", LIBXSLT_DOTTED_VERSION); +#if HAVE_DOMEXSLT + php_info_print_table_row(2, "libexslt Version", LIBEXSLT_DOTTED_VERSION); +#endif + php_info_print_table_end(); +} Index: /tags/2007080101/fl_hash.c =================================================================== --- /tags/2007080101/fl_hash.c (revision 18) +++ /tags/2007080101/fl_hash.c (revision 18) @@ -0,0 +1,150 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Thies C. Arntzen | + | Sterling Hughes | + +----------------------------------------------------------------------+ + + $Id: fl_hash.c,v 1.1.1.1 2004/02/17 23:31:44 sterling Exp $ +*/ + +#include +#include +#include "php_fastxsl.h" +#include "fl_hash.h" + +FL_Hash *fl_hash_new(FL_Allocator *mems, FL_HashDtor dtor) +{ + FL_Hash *table; + + table = mems->calloc_func(1, sizeof(FL_Hash)); + table->dtor = dtor; + table->mems = mems; + + return table; +} + +void fl_hash_free(FL_Hash *table) +{ + FL_Bucket *cur; + FL_Bucket *next; + FL_Allocator *mems; + int i; + + mems = table->mems; + + for (i = 0; i < FL_HASH_SIZE; i++) { + cur = table->buckets[i]; + while (cur) { + next = cur->next; + + if (table->dtor) table->dtor(cur->data); + mems->free_func(cur->key); + mems->free_func(cur); + + cur = next; + } + } + mems->free_func(table); +} + +static unsigned int hash_hash(const char *key, int length) +{ + unsigned int hash = 0; + + while (--length) + hash = hash * 65599 + *key++; + + return hash; +} + +void *fl_hash_find(FL_Hash *table, const void *key, int length) +{ + FL_Bucket *b; + unsigned int hash = hash_hash(key, length) % FL_HASH_SIZE; + for (b = table->buckets[ hash ]; b; b = b->next) { + if (hash != b->hash) continue; + if (length != b->length) continue; + if (memcmp(key, b->key, length)) continue; + return b->data; + } + + return NULL; +} + +int fl_hash_add(FL_Hash *table, char *key, int length, void *data) +{ + FL_Bucket *b, *ptr; + unsigned int hash; + + hash = hash_hash(key, length) % FL_HASH_SIZE; + /* check hash for dupes */ + ptr = table->buckets[ hash ]; + while(ptr) { + if(!strcmp(ptr->key, key)) { + return 0; + } + ptr = ptr->next; + } + + b = table->mems->malloc_func(sizeof(FL_Bucket)); + b->key = table->mems->malloc_func(length + 1); + memcpy(b->key, key, length); + b->key[length] = 0; + b->length = length; + b->hash = hash; + b->data = data; + b->next = table->buckets[ hash ]; + + table->buckets[ hash ] = b; + table->nElements++; + return 1; +} + +void fl_hash_delete(FL_Hash *table, char *key, int length) +{ + FL_Bucket *b; + FL_Bucket *prev = NULL; + unsigned int hash; + + hash = hash_hash(key, length) % FL_HASH_SIZE; + for (b = table->buckets[ hash ]; b; b = b->next) { + if (hash != b->hash || length != b->length || memcmp(key, b->key, length)) { + prev = b; + continue; + } + + /* unlink */ + if (prev) { + prev->next = b->next; + } else { + table->buckets[hash] = b->next; + } + + if (table->dtor) table->dtor(b->data); + table->mems->free_func(b->key); + table->mems->free_func(b); + + break; + } +} + +/* +Local variables: +tab-width: 4 +c-basic-offset: 4 +End: +vim600: noet sw=4 ts=4 fdm=marker +vim<600: noet sw=4 ts=4 +*/ Index: /tags/2007080101/FastXSL.php =================================================================== --- /tags/2007080101/FastXSL.php (revision 1) +++ /tags/2007080101/FastXSL.php (revision 1) @@ -0,0 +1,325 @@ + + * @since FastXSL 1.0 + * + */ +class FastXSL_Base { + /** + * Whether or not the files passed to FastXSL will be absolute paths. If this + * variable is set to false, then a realpath() will be done on every file to + * assure that it is being accessed through an absolute path. + * + * @access public + * @since FastXSL 1.0 + */ + var $useAbsolutePath = false; + + /** + * Whether or not to stat() the documents in the fastxsl cache. If this is turned off, + * then documents will not be checked for freshness, and you need to gracefully restart + * Apache in order for new documents to be recognized. + * + * @access public + * @since FastXSL 1.0 + */ + var $noStat = false; + + function FastXSL_Base() { + } + + /** + * Resolves a relative path to an absolute path using the realpath() system call which not + * only expands paths, but properly handles symlinks and the like. This function respects + * the $useAbsolutePath property, and will avoid the expensive realpath() call if + * $useAbsolutePath is off. + * + * @param string The filename to resolve + * @param string The class and method name pair for error reporting + * + * @return boolean + * + * @access private + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function resolvePath(&$filename, $class_meth_name) { + if ($this->useAbsolutePath == false) { + $resolved_filename = realpath($filename); + if ($resolved_filename === false) { + trigger_error(E_WARNING, "Cannot resolve path of XML file $filename passed to $class_meth_name"); + return false; + } + $filename = $resolved_filename; + } + + return true; + } + + /** + * Parses an XML file into a FastXSL XML resource for use with the transform() or profile() methods. + * + * @param string The name of the file you'd like to parse + * + * @return resource the FastXSL XML document + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function parseXMLFile($filename) { + $this->resolvePath($filename, "FastXSL::setParseXMLFile()"); + return fastxsl_xml_parsefile($filename); + } + + /** + * Parses XML data into a FastXSL XML resource for use with the transform() or profile() methods. + * + * @param string The XML data to compile + * + * @return resource The FastXSL XML Document + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function parseXMLData($data) { + return fastxsl_xml_parsestring($data); + } + + /** + * Parses an XSL stylesheet into a FastXSL Stylesheet resource for use with the transformParsed() or profile() + * methods. + * + * @param string The stylesheet file to compile + * + * @return resource The FastXSL stylesheet document + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function parseStylesheetFile($stylesheet_file) { + $this->resolvePath($stylesheet_file, "FastXSL::parseStylesheetFile()"); + return fastxsl_stylesheet_parsefile($stylesheet_file); + } + + function &transformParsed($stylesheet, $xml, $parameters = null) + { + $res = fastxsl_nocache_transform($stylesheet, $xml, $parameters); + if (!$res) { + return false; + } + + $resobj = &new FastXSL_Result(FASTXSL_NOCACHE_RESULT, $stylesheet, $res); + return $resobj; + } + + /** + * Profile the transformation of an XML document against a XSL stylesheet. + * + * @param resource The Stylesheet Document to profile + * @param resource The XML Document to profile + * @param array Transformation resources + * @param string The directory to place the profile results + * + * @return mixed A FastXSL result object on success, false on failure + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function &profile($stylesheet, $xml, $parameters, $profiledir) { + $res = fastxsl_nocache_profile($stylesheet, $xml, $parameters, $filename); + if (!$res) { + return false; + } + + $resobj = &new FastXSL_Result(FASTXSL_NOCACHE_RESULT, $stylesheet, $res); + return $resobj; + } +} + +/** + * A container that holds the result of an XSL transformation and provides a set of + * common methods for acting on that container. + * + * This class is instantiated by the transformation methods and should not be directly + * instantiated. + * + * @access private + * @author Sterling Hughes + * @since FastXSL 1.0 + */ +class FastXSL_Result { + var $type; + var $stylesheet; + var $result; + + /** + * Constructor sets up the context for this object. + * + * @param mixed The stylesheet associated with the resulting XML document + * @param resource The resulting XML document from a FastXSL transformation + * + * @access private + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function FastXSL_Result($type, $stylesheet, $result) { + $this->type = $type; + $this->stylesheet = $stylesheet; + $this->result = $result; + } + + /** + * Return the value of this result object as a string + * + * @return string The string value of this XML document object + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function toString() { + switch ($this->type) { + case FASTXSL_NOCACHE_RESULT: + return fastxsl_nocache_tostring($this->stylesheet, $this->result); + case FASTXSL_PRMCACHE_RESULT: + return fastxsl_prmcache_tostring($this->stylesheet, $this->result); + case FASTXSL_SHMCACHE_RESULT: + return fastxsl_shmcache_tostring($this->stylesheet, $this->result); + } + } +} + +/** + * FastXSL_Cache extends FastXSL_Base to provide shared memory cached transformations + * to Apache processes. This class calls the underlying fastxsl api's to transparently + * markup the transformations. + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ +class FastXSL_ShmCache extends FastXSL_Base { + /** + * Constructor - calls the FastXSL_Base constructor. + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function FastXSL_ShmCache() { + $this->FastXSL_Base(); + } + + /** + * Transform an XSL stylesheet from the shared memory cache against an + * XML document with the given parameters. If the passed stylesheet does not + * exist in shared memory, parse the stylesheet and place it in shared memory. + * + * @param string The filename of the sylesheet + * @param resource The XML document to parse + * @param array The parameters for the transformation + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function &transform($stylesheet, $xml, $parameters = null) { + $this->resolvePath($stylesheet, "FastXSL_Cache::transform()"); + + $res = fastxsl_shmcache_transform($stylesheet, $xml, $parameters); + if (!$res) { + return false; + } + + $resobj = &new FastXSL_Result(FASTXSL_SHMCACHE_RESULT, $stylesheet, $res); + return $resobj; + } +} + +/** + * FastXSL_PrmCache extends FastXSL_Base to provide memory cached transformations + * to Apache processes. This class calls the underlying fastxsl api's to transparently + * markup the transformations. + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ +class FastXSL_PrmCache extends FastXSL_Base { + /** + * Constructor - calls the FastXSL_Base constructor. + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function FastXSL_PrmCache() { + $this->FastXSL_Base(); + } + + /** + * Transform an XSL stylesheet from the memory cache against an + * XML document with the given parameters. If the passed stylesheet does not + * exist in shared memory, parse the stylesheet and place it in shared memory. + * + * @param string The filename of the sylesheet + * @param resource The XML document to parse + * @param array The parameters for the transformation + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + */ + function &transform($stylesheet, $xml, $parameters = null) { + $this->resolvePath($stylesheet, "FastXSL_Cache::transform()"); + + $res = fastxsl_prmcache_transform($stylesheet, $xml, $parameters); + if (!$res) { + return false; + } + + $resobj = &new FastXSL_Result(FASTXSL_PRMCACHE_RESULT, $stylesheet, $res); + return $resobj; + } +} + + +/** + * This class implements a version of FastXSL that does not cache the documents in + * shared memory. This class can directly replace the FastXSL_Cache class, and is + * useful for isolating cases where the shared memory class FastXSL_Cache, may be causing + * problems. + * + * Method signatures are the same as those of the FastXSL_Cache class. + * + * @access public + * @author Sterling Hughes + * @since FastXSL 1.0 + * + */ +class FastXSL_NoCache extends FastXSL_Base { + function FastXSL() { + $this->FastXSL_Base(); + } + + function &transform($stylesheet_file, $xml, $parameters = null) { + return $this->transformParsed($this->parseStylesheetFile($stylesheet_file), $xml, $parameters); + } +} + + +?> Index: /tags/2007080101/fl_hash.h =================================================================== --- /tags/2007080101/fl_hash.h (revision 18) +++ /tags/2007080101/fl_hash.h (revision 18) @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Thies C. Arntzen | + | Sterling Hughes | + +----------------------------------------------------------------------+ + + $Id: fl_hash.h,v 1.1.1.1 2004/02/17 23:31:44 sterling Exp $ +*/ + +#ifndef _FL_HASH_H +#define _FL_HASH_H + +typedef void (*FL_HashDtor)(void *); +typedef void (*FL_Free)(void *); +typedef void *(*FL_Malloc)(size_t); +typedef void *(*FL_Calloc)(size_t, size_t); + +typedef struct { + FL_Free free_func; + FL_Malloc malloc_func; + FL_Calloc calloc_func; +} FL_Allocator; + +typedef struct _FL_Bucket { + struct _FL_Bucket *next; + char *key; + int length; + unsigned int hash; + void *data; +} FL_Bucket; + +#define FL_HASH_SIZE 1009 +typedef struct _Hash { + FL_Bucket *buckets[ FL_HASH_SIZE ]; + FL_Allocator *mems; + FL_HashDtor dtor; + int nElements; +} FL_Hash; + +FL_Hash *fl_hash_new(FL_Allocator *, FL_HashDtor); +void fl_hash_free(FL_Hash *table); +void *fl_hash_find(FL_Hash *table, const void *key, int length); +int fl_hash_add(FL_Hash *table, char *key, int length, void *data); +void fl_hash_delete(FL_Hash *table, char *key, int length); +#endif + +/* +Local variables: +tab-width: 4 +c-basic-offset: 4 +End: +vim600: noet sw=4 ts=4 fdm=marker +vim<600: noet sw=4 ts=4 +*/ Index: /tags/2007080101/CREDITS =================================================================== --- /tags/2007080101/CREDITS (revision 14) +++ /tags/2007080101/CREDITS (revision 14) @@ -0,0 +1,4 @@ +fastxsl + +Sterling Hughes +George Schlossnagle Index: /tags/2007080101/README =================================================================== --- /tags/2007080101/README (revision 24) +++ /tags/2007080101/README (revision 24) @@ -0,0 +1,89 @@ +INI Settings +============================================== + + Option Description + ------------------ -------------------------------------------------- + fastxsl.shmpath + The path to key your shared memory segment off of. + Depending on which mm implementation you use, this + path may or may not need to be creatable. + (DEFAULT: "/tmp/fastxsl_mem") + + fastxsl.nostat + Whether or not to check xsl files to determine + if they are changed. If this is set to '1', + then xsl changes will not be picked up until + the server is restarted. + (DEFAULT: 0) + + fastxsl.memalloc + The amount of shared memory to allocate to + FastXSL for caching stylesheets. If this is + set to 0, FastXSL will allocate as much memory + as libmm will permit it to. + (DEFAULT: 0) + + fastxsl.register_functions + Whether or not to register the PHP extension + namespace and allow users to use PHP functions + as XSLT extension functions. + (DEFAULT: 0) + + +EXPORTED FUNCTIONS +================== + +float fastxsl_version() + Returns the version number of the FastXSL library. + +resource fastxsl_xml_parsefile(string filename) + Parses the filename in question and returns it + as a FastXSL XML document resource. + +resource fastxsl_xml_parsestring(string str) + Parses the string in question and returns it + as a FastXSL XML document resource. + +resource fastxsl_shmcache_transform(string xsl, resource xmldoc [, array params]) + Apply the XSL stylesheet given by 'xsl' to the xml + document resource given by xmldoc, with optional + XSL params. The compiled stylesheet will be + cached in shared memory. Returns a FastXSL + result resource. + +resource fastxsl_prmcache_transform(string xsl, resource xmldoc [, array params]) + Apply the XSL stylesheet given by 'xsl' to the xml + document resource given by xmldoc, with optional + XSL params. The compiled stylesheet will be + cached in process-local memory. Because of the + large amount of memory libxslt uses, use of + the prm functions is discouraged. Returns a + FastXSL result resource. + +resource fastxsl_nocache_transform(string xsl, resource xmldoc [, array params]) + Apply the XSL stylesheet given by 'xsl' to the xml + document resource given by xmldoc, with optional + XSL params. The compiled stylesheet will be + not be cached. Returns a FastXSL result resource. + +string fastxsl_shmcache_tostring(string xsl, resource fastxsl_result) + Takes a stylesheet and a result resource and + returns the transformed document. This must + be used with resources created with + fastxsl_shmcache_transform(). + +string fastxsl_prmcache_tostring(string xsl, resource fastxsl_result) + Takes a stylesheet and a result resource and + returns the transformed document. This must + be used with resources created with + fastxsl_prmcache_transform(). + +string fastxsl_nocache_tostring(string xsl, resource fastxsl_result) + Takes a stylesheet and a result resource and + returns the transformed document. This must + be used with resources created with + fastxsl_nocache_transform(). + +array fastxsl_shmcache_getstatistics() + Returns an array of statistics regarding what + data is cached and how much space it is consuming. Index: /tags/2007080101/php_fastxsl.h =================================================================== --- /tags/2007080101/php_fastxsl.h (revision 26) +++ /tags/2007080101/php_fastxsl.h (revision 26) @@ -0,0 +1,130 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + | George Schlossnagle | + +----------------------------------------------------------------------+ + + $Id: php_fastxsl.h,v 1.1.1.1 2004/02/17 23:31:44 sterling Exp $ +*/ + +#include "php.h" + +#ifndef PHP_FASTXSL_H +#define PHP_FASTXSL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_DOMEXSLT +#include +#include +#endif + +#ifdef FASTXSL_MM +#include +#endif + +#include +#include + +#include "fl_hash.h" + +extern zend_module_entry fastxsl_module_entry; +#define phpext_fastxsl_ptr &fastxsl_module_entry + +#ifdef PHP_WIN32 +#define PHP_FASTXSL_API __declspec(dllexport) +#else +#define PHP_FASTXSL_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(fastxsl); +PHP_MSHUTDOWN_FUNCTION(fastxsl); +PHP_RINIT_FUNCTION(fastxsl); +PHP_RSHUTDOWN_FUNCTION(fastxsl); +PHP_MINFO_FUNCTION(fastxsl); + +#define FASTXSL_PRMALLOC 0 +#define FASTXSL_SHMALLOC 1 + +typedef struct { +#ifdef FASTXSL_MM + MM *mm; +#endif + FL_Hash *table; + pid_t owner; + FL_Hash *prmtable; +} fl_cache; + +typedef struct { + xmlDocPtr xd; + int alloc_type; +} php_xd_wrapper; + +#define FASTXSL_STYLESHEET 1 +#define FASTXSL_XPATHOBJ 2 +typedef struct { + union { + xsltStylesheetPtr ss; + xmlXPathObjectPtr op; + } data; + int data_type; + int allocsize; + time_t mtime; + int hits; + int persistant; + int alloc_type; +} php_ss_wrapper; + +typedef struct { + fl_cache *cache; + char *shmpath; + char *errbuf; + long nostat; + long memalloc; + long register_functions; + long tmp_allocated_size; +} zend_fastxsl_globals; + +#define PHP_FASTXSL_VERSION "1.0" + +#ifdef ZTS +#define FASTXSL_G(v) TSRMG(fastxsl_globals_id, zend_fastxsl_globals *, v) +#else +#define FASTXSL_G(v) (fastxsl_globals.v) +#endif + +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ Index: /tags/2007080101/tests/001.phpt =================================================================== --- /tags/2007080101/tests/001.phpt (revision 1) +++ /tags/2007080101/tests/001.phpt (revision 1) @@ -0,0 +1,24 @@ +--TEST-- +Check for fastxsl presence +--SKIPIF-- + +--POST-- +--GET-- +--INI-- +--FILE-- + +--EXPECT-- +fastxsl extension is available