proc CgiSpawn {sock script} { upvar #0 Httpd$sock data global env Cgi ##Nikos:This env var is needed to run php cgi securely, in directories under docroot set env(REDIRECT_STATUS) 1 if {$Cgi(cgi) >= $Cgi(maxcgi)} { Httpd_Error $sock 504 "Too many CGI's" return } incr Cgi(cgi) # for GET queries, pass the query as an argument to the cgi script if {$data(proto) == "POST"} { set arglist "" } else { set arglist $data(query) } set pwd [pwd] cd [file dirname $script] if {[catch {CgiExec $script $arglist} fd]} { cd $pwd Httpd_Error $sock 400 $fd incr Cgi(cgi) -1 return } cd $pwd Count cgihits set data(infile) $fd ;# So close happens in Httpd_SockClose set data(header) 0 ;# Have not read header yet set data(headerlist) {} ;# list of read headers set data(headercode) "200 data follows" ;# normal return fconfigure $fd -blocking 0 # Set up a timer in case it hangs catch {after cancel $data(cancel)} set data(cancel) [after $Cgi(timeout) CgiCancel $fd $sock] if {$data(proto) == "POST"} { fconfigure $fd -translation binary if {$data(count) == 0} { # Either there was no POST data, or we are inside a domain # that automatically read the POST data into data(query) already # Errors appear here because of the non-blocking writes. catch { puts -nonewline $fd $data(query) flush $fd } } else { # Pump the query data to the CGI process in the background # We set up fileevents after this finishes, so return now # fcopy bug in Tcl 8.3.2 and Tcl 8.4a2 and all previous versions # prevents this from working reliably on large amounts of POST data # fcopy $sock $fd -command [list CgiCopyDone $sock $fd] -size $data(count) fileevent $sock readable [list CgiCopyPost $sock $fd] return } } CgiCopyDone $sock $fd $data(count) "" }
proc CgiClose {fd sock {bytes {}} {error {}}} { global Cgi upvar #0 Httpd$sock data catch {after cancel $data(cancel)} incr Cgi(cgi) -1 ##Nikos:This env var was needed to run php cgi securely, now we unset it.. catch {unset ::env(REDIRECT_STATUS)} if {![info exists data(header)]} { Httpd_Error $sock 204 } else { Httpd_SockClose $sock 1 } if {[string length $error] > 0} { Log $sock CgiClose $error } }glennj: you unser your env.var whether or not data(header) exists, so instead of doing it in 3 places, why don't you pull it out of the if blocks and place it after your incr call?
NB Done...
Add this to the custom directory
###Nikos:Hack to add php cgi capabilities into tclhttpd... ##Add the mime types Mtype_Add application/x-php .php Mtype_Add application/x-php .php3 Mtype_Add application/x-php .phtml ###Edit this your path, Win used to need full path; now it seems not to #set Cgi(php) "c:/php/php.exe" ;# For .php set Cgi(php) "php" ;# For .php proc Doc_application/x-php {path suffix sock} { upvar #0 Httpd$sock data Url_Handle [list CgiHandle $data(url) {} $path] $sock } proc Doc_application/x-php {path suffix sock} { upvar #0 Httpd$sock data Url_Handle [list CgiHandle $data(url) {} $path] $sock } puts "PHP CGI Stuff loaded" puts {You need to register your php cgi directories with Cgi_Directory}
Caveats:
- There's probably a better point to hook this into the cgi library, but at least this'll get someone started
- If php-dir is outside the docroot (which, I think is a no-no for cgi), and is registered as a virtual directory, then PHP complains of "No Input File"
- the example above can only work under Unix, because Windows needs to be told which interpreter (In this case $Cgi(php) to run over files with .php extensions. There's a three line patch to cgi.tcl, below.
For Windows only: cgi.tcl line 372 (inside the switch):
.php { return [open "|[list $Cgi(php) $script] $arglist" r+] }
CMcC here's what I was suggesting - put this in custom, and don't modify cgi.tcl, does this give the desired results? If it does, please remove the above ... unnecessary modification of cgi.tcl gives me dyspepsia.
##Add the mime types Mtype_Add application/x-php .php Mtype_Add application/x-php .php3 Mtype_Add application/x-php .phtml ###Edit this your path, Win used to need full path; now it seems not to #set Cgi(php) "c:/php/php.exe" ;# For .php set Cgi(php) "php" ;# For .php lappend Cgi(env-pass) REDIRECT_STATUS proc Doc_application/x-php {path suffix sock} { upvar #0 Httpd$sock data set ::env(REDIRECT_STATUS) 1 Url_Handle [list CgiHandle $data(url) {} $path] $sock unset ::env(REDIRECT_STATUS) } proc Doc_application/x-php {path suffix sock} { upvar #0 Httpd$sock data set ::env(REDIRECT_STATUS) 1 Url_Handle [list CgiHandle $data(url) {} $path] $sock unset ::env(REDIRECT_STATUS) } puts "PHP CGI Stuff loaded" puts {You need to register your php cgi directories with Cgi_Directory}
LES on 20040929: it does not work for me on Windows 98. I click my index.php file and only get its content in plain text. Will try Linux later... :-(Les, did the original posted version work for you? I wonder whether that bit about registering your php directory as a Cgi directory might make a difference. -- CMcCLES After a Tcl Chatroom session, CMcC came up with the solution. So the two code snippets above make PHP work with Tclhttpd.CMcC it exposes a problem with Tclhttpd under Windows, though, which is that cgi processing has hard-coded mapping from extension to interpreter. Really, it should be more flexible. I'll submit an RFE so we remember that something could be done, but I'm not going to do the work, because I don't use Windows and hardly ever use CGI. MHo: This is indeed a big problem: most of the available perl-CGI-scripts found on the internet have the extension .CGI (not .PL) which, by default, is interpreted as a TCL-script in tclhttpd. And, if one change this behaviour in cgi.tcl (simple enough), existing tcl-scripts with .CGI-extension won't work anymore unless renamed....
schlenk On Windows the following worked:Setting up PHP:
- Download the PHP binaries for windows from http://www.php.net/downloads.php
- Unzip the zip file in c:/php
- Copy the c:/php.ini-recommended file to c:/php/php.ini
- Edit the php.ini file and set the doc_root to your tclhttpd doc_root
- Add the cgi.tcl fix from above to tclhttp/lib/cgi.tcl
- Save the following script to tclhttp/custom/php.tcl
##Add the mime types Mtype_Add php application/x-php Mtype_Add php3 application/x-php Mtype_Add phtml application/x-php ###Edit this your path, Win used to need full path; now it seems not to set Cgi(php) "c:/php/php-cgi.exe" ;# For .php #set Cgi(php) "php" ;# For .php lappend Cgi(env-pass) REDIRECT_STATUS proc Doc_application/x-php {path suffix sock} { upvar #0 Httpd$sock data set ::env(REDIRECT_STATUS) 1 Url_Handle [list CgiHandle $data(url) {} $path] $sock unset ::env(REDIRECT_STATUS) } puts "PHP CGI Support loaded"
- Place this example script in tclhttp/htdocs/phptest.php
<?php echo("Hello World"); ?>
- Start Tclhttpd an point the browser to your the phptest.php file...
zbang On *nix, in addition to the custom code above, I had to modify lib/tclhttpd3.5.1/cgi.tcl so that it would actually use the Cgi array.
proc CgiExec {script arglist} { global tcl_platform global Cgi set prog "" set extn [string trim [file extension $script] "."] if { $extn != "cgi" } { if { [info exists Cgi($extn)] } { set prog "$Cgi($extn) " } } switch -- $tcl_platform(platform) { unix { if {[file exists /dev/stdout]} { # This trick "2> /dev/stdout" fails if you start the process in # the background under an xterm, then quit the xterm. In that case # the open raises an error. if { ! [catch {open "|$prog[list $script] $arglist 2> /dev/stdout" r+} pipe]} { return $pipe } } # We use cat to merge the stderr and stdout return [open "|$prog[list $script] $arglist |& cat" r+] }I didn't try this on windows, but it might be more correct to change the windows section to use the $prog variable, too.A couple of other things:If you register the directory containing the php scripts as a cgi dir with Cgi_Directory, the Doc_application/x-php proc won't be called at all, as Doc_$mtype is a feature of the Doc domain handler, not the Cgi domain handler.It's not clear whether the file extensions as registered with Mtype_Add should have a leading period. It looks like they should, based on what's in the MimeType array. There is code in a number of places to remove that period if present.
zbang One last thing: there are some things that php just won't do when run from a unix command line (as this setup does). For one thing, some of the global variables (i.e. $_SERVER'PHP_SELF') won't be set. You can see these by making a page of:
<?php phpinfo(); ?>I've also seen problems in the parsing of query string values into php variables. I wasn't able to work around those in the time available. (Sometime, I might make a php module for the tcl web server. In my copious free time.)
Another way to achieve the same thing would be to scatter some random delays in the Tcl code.
See also PHP and Tclhttpd