|
1 | 1 | require 'rack/legacy'
|
2 |
| -require 'rack/legacy/cgi' |
| 2 | +require 'rack/request' |
| 3 | +require 'rack/reverse_proxy' |
| 4 | +require 'childprocess' |
3 | 5 |
|
4 |
| -class Rack::Legacy::Php < Rack::Legacy::Cgi |
| 6 | +class Rack::Legacy::Php |
5 | 7 |
|
6 |
| - # Like Rack::Legacy::Cgi.new except allows an additional argument |
7 |
| - # of which executable to use to run the PHP code. |
| 8 | + # Proxies off requests to PHP files to the built-in PHP webserver. |
8 | 9 | #
|
9 |
| - # use Rack::Legacy::Php, 'public', 'php5-cgi' |
10 |
| - def initialize(app, public_dir=FileUtils.pwd, php_exe='php-cgi') |
11 |
| - super app, public_dir |
12 |
| - @php_exe = php_exe |
| 10 | + # public_dir:: |
| 11 | + # Location of PHP files. Default to current working directory. |
| 12 | + # php_exe:: |
| 13 | + # Location of `php` exec. Will process through shell so is |
| 14 | + # generally not needed since it is in the path. |
| 15 | + # port:: |
| 16 | + # Requests are proxied off to the built-in PHP webserver. It |
| 17 | + # will run on the given port. If you are already using that port |
| 18 | + # for something else you may need to change this option. |
| 19 | + # quiet:: |
| 20 | + # By default the PHP server inherits the parent process IO. Set |
| 21 | + # this to true to hide the PHP server output |
| 22 | + # |
| 23 | + def initialize app, public_dir=Dir.getwd, php_exe='php', port=8180, quiet=false |
| 24 | + @app = app; @public_dir = public_dir |
| 25 | + @proxy = Rack::ReverseProxy.new {reverse_proxy /^.*$/, "http://localhost:#{port}"} |
| 26 | + @php = ChildProcess.build php_exe, |
| 27 | + '-S', "localhost:#{port}", '-t', public_dir |
| 28 | + @php.io.inherit! unless quiet |
| 29 | + @php.start |
| 30 | + at_exit {@php.stop if @php.alive?} |
13 | 31 | end
|
14 | 32 |
|
15 |
| - # Override to check for php extension. Still checks if |
16 |
| - # file is in public path and it is a file like superclass. |
17 |
| - def valid?(path) |
18 |
| - sp = path_parts(full_path path)[0] |
19 |
| - |
20 |
| - # Must have a php extension or be a directory |
21 |
| - return false unless |
22 |
| - (::File.file?(sp) && sp =~ /\.php$/) || |
23 |
| - ::File.directory?(sp) |
24 |
| - |
25 |
| - # Must be in public directory for security |
26 |
| - sp.start_with? ::File.expand_path(@public_dir) |
27 |
| - end |
28 |
| - |
29 |
| - # Monkeys with the arguments so that it actually runs PHP's cgi |
30 |
| - # program with the path as an argument to that program. |
31 |
| - def run(env, path) |
32 |
| - script, info = *path_parts(path) |
33 |
| - if ::File.directory? script |
34 |
| - # If directory then assume index.php |
35 |
| - script = ::File.join script, 'index.php'; |
36 |
| - # Ensure it ends in / which some PHP scripts depend on |
37 |
| - path = "#{path}/" unless path =~ /\/$/ |
| 33 | + # If it looks like it is one of ours proxy off to PHP server. |
| 34 | + # Otherwise send down the stack. |
| 35 | + def call env |
| 36 | + if valid? env['PATH_INFO'] |
| 37 | + @php.start unless @php.alive? |
| 38 | + @proxy.call env |
| 39 | + else |
| 40 | + @app.call env |
38 | 41 | end
|
39 |
| - env['SCRIPT_FILENAME'] = script |
40 |
| - env['SCRIPT_NAME'] = strip_public script |
41 |
| - env['PATH_INFO'] = info |
42 |
| - env['REQUEST_URI'] = strip_public path |
43 |
| - env['REQUEST_URI'] += '?' + env['QUERY_STRING'] if |
44 |
| - env.has_key?('QUERY_STRING') && !env['QUERY_STRING'].empty? |
45 |
| - super env, @php_exe, "-d cgi.force_redirect=0" |
46 |
| - end |
47 |
| - |
48 |
| - private |
49 |
| - |
50 |
| - def strip_public(path) |
51 |
| - path.sub ::File.expand_path(public_dir), '' |
52 | 42 | end
|
53 | 43 |
|
54 |
| - # Given a full path will separate the script part from the |
55 |
| - # path_info part. Returns an array. The first element is the |
56 |
| - # script. The second element is the path info. |
57 |
| - def path_parts(path) |
58 |
| - return [path, nil] unless path =~ /.php/ |
59 |
| - script, info = *path.split('.php', 2) |
60 |
| - script += '.php' |
61 |
| - [script, info] |
62 |
| - end |
| 44 | + # Make sure it points to a valid PHP file. No need to ensure it |
| 45 | + # is in the public directory since PHP will do that for us. |
| 46 | + def valid? path |
| 47 | + return false unless path =~ /\.php/ |
63 | 48 |
|
64 |
| - # Given a full path will extract just the info part. So |
65 |
| - # |
66 |
| - # /index.php/foo/bar |
67 |
| - # |
68 |
| - # will return /foo/bar, but |
69 |
| - # |
70 |
| - # /index.php |
71 |
| - # |
72 |
| - # will return an empty string. |
73 |
| - def info_path(path) |
74 |
| - path.split('.php', 2)[1].to_s |
| 49 | + path = path[1..-1] if path =~ /^\// |
| 50 | + path = path.split('.php', 2)[0] + '.php' |
| 51 | + path = ::File.expand_path path, @public_dir |
| 52 | + ::File.file? path |
75 | 53 | end
|
76 | 54 | end
|
0 commit comments