Help With PHP Buildpack and httpd-php.conf File

246 views
Skip to first unread message

Ryan Baxter

unread,
Mar 24, 2014, 4:44:17 PM3/24/14
to vcap...@cloudfoundry.org
Hi Everyone,

Let me start off saying I am not a PHP developer by any means but I have been asked to build a simple app to demonstrate the PHP runtime on CF so I am taking a swing at it.

The app I am building is pretty simple.  I am just using PHP to provide a REST API for the client side code in my app (the client side code is using Backbone).  I came across the Slim framework [1] and figured I would give that a shot.

Obviously I will need to do some routing in my app so that my REST API, which is located at the api/* endpoint, gets routed to my PHP file.  I was able to successfully configure my local .htaccess file so that it routes everything that is not on the file system to index.php and this is working fine for me locally.  Here is what I have in my .htaccess file.

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]


After looking into how I would do this with the PHP buildpack [2] I came across this issue [3] which seemed to answer my question.  I added a file named http-php.conf to .bp-config/httpd/extra in my application and copied exactly what I had in my .htaccess file to http-php.conf.  However, when I push the app to CF and go to the URL of either the static resources or the REST endpoints (that should be routing to the php file) I get back a 400.  I would have thought that what I had in my .htaccess file would be able to translate over to the http-php.conf file directly but it doesn't appear to be the case.  Can anyone point me in the right direction?

Daniel Mikusa

unread,
Mar 24, 2014, 5:14:39 PM3/24/14
to vcap...@cloudfoundry.org
I would guess, please let me know if I’m wrong, that your local environment is using mod_php whereas the CF Build Pack uses FastCGI. I mention this because it can cause some differences in behavior with your rewrite rules and with how PHP apps / frameworks behave. If you’re going to build locally and push to CF, I’d suggest that you do a FastCGI setup locally so that you have similar environments.

In regards to Slim, I haven’t worked with it, but I’d suggest starting with this and adjusting the RewriteCond to meet your needs.

https://github.com/dmikusa-pivotal/cf-ex-code-igniter/blob/master/.bp-config/httpd/extra/httpd-php.conf

Let me know if that doesn’t work and I’ll see if I can put a demo using Slim together.

Dan
> To unsubscribe from this group and stop receiving emails from it, send an email to vcap-dev+u...@cloudfoundry.org.

Ryan Baxter

unread,
Mar 25, 2014, 4:18:27 PM3/25/14
to vcap...@cloudfoundry.org
I THINK I made some progress today. I forked the buildpack and added

LogLevel alert rewrite:trace6

to defaults/config/httpd/2.4.x/extra/httpd-logging.conf to get some
logging about the routing was being done. After being able to see
what the URLs ended up looking like I changed my http-php.conf to

RewriteCond $1 ^/?(api.*)$
RewriteRule ^/?(.*)$ index.php/$1 [QSA,L]

After doing this I can see my URLs look like they are being rewritten
correctly. My static resources (HTML, CSS, JS) can be rendered and my
REST API is being redirected to my php file.

From the logs...

20:13:00 httpd | [Tue Mar 25 20:13:00.660862 2014]
[rewrite:trace2] [pid 55:tid 139666132276992] mod_rewrite.c(475):
[client 75.126.23.243:40712] 75.126.23.243 - -
[myapp.cf.com/sid#23df320][rid#24701f0/initial] init rewrite engine
with requested uri /api/todos
20:13:00 httpd | [Tue Mar 25 20:13:00.660911 2014]
[rewrite:trace3] [pid 55:tid 139666132276992] mod_rewrite.c(475):
[client 75.126.23.243:40712] 75.126.23.243 - -
[myapp.cf.com/sid#23df320][rid#24701f0/initial] applying pattern
'^/?(.*)$' to uri '/api/todos'
20:13:00 httpd | [Tue Mar 25 20:13:00.660957 2014]
[rewrite:trace4] [pid 55:tid 139666132276992] mod_rewrite.c(475):
[client 75.126.23.243:40712] 75.126.23.243 - -
[myapp.cf.com/sid#23df320][rid#24701f0/initial] RewriteCond:
input='api/todos' pattern='^/?(api.*)$' => matched
20:13:00 httpd | [Tue Mar 25 20:13:00.660971 2014]
[rewrite:trace2] [pid 55:tid 139666132276992] mod_rewrite.c(475):
[client 75.126.23.243:40712] 75.126.23.243 - -
[myapp.cf.com/sid#23df320][rid#24701f0/initial] rewrite '/api/todos'
-> 'index.php/api/todos'
20:13:00 httpd | [Tue Mar 25 20:13:00.660991 2014]
[rewrite:trace2] [pid 55:tid 139666132276992] mod_rewrite.c(475):
[client 75.126.23.243:40712] 75.126.23.243 - -
[myapp.cf.com/sid#23df320][rid#24701f0/initial] local path result:
index.php/api/todos
20:13:00 httpd | 75.126.23.243 - - [25/Mar/2014:20:13:00 +0000]
"GET /api/todos HTTP/1.1" 400 226

So what is happening is my client side code is making a request to
/api/todos which is being rewritten as index.php/api/todos. index.php
contains my Slim app code and index.php/apis/todos is a valid URL to
access the REST APIs [1]. Yet as you can see the server is returning
a 400. I cannot figure out why I am getting a 400 back. One odd
thing I noticed is if I go to index.php directly I see the PHP source
code while I would expect it to be evaluating the PHP code. How can I
figure out where the 400 is coming from?

[1] http://docs.slimframework.com/#Route-URL-Rewriting

Ryan Baxter

unread,
Mar 26, 2014, 3:57:28 PM3/26/14
to vcap...@cloudfoundry.org
Some more progress today. I was able to get things working using
nginx [1]. Still struggling with why Apache does not work....

[1] https://www.ibmdw.net/answers/questions/9697/url-rewriting-in-zend-servernginx-buildpack/?utm_campaign=answers&utm_medium=email&utm_source=answers-answer-accepted&utm_content=answers-question

Daniel Mikusa

unread,
Mar 26, 2014, 5:34:16 PM3/26/14
to vcap...@cloudfoundry.org
Ryan,

I feel your pain here. I’ve been playing with this for a bit and as I understand it, the problem is with the way that mod_proxy_fcgi is setting the environment variables like PATH_INFO. The way that it is setting them is not consistent with what Slim is expecting. The mismatch causes 404’s and other weird issues.

In my testing, I got a couple configurations that were close to working, but each had a few minor issues that I couldn’t resolve through my HTTPD configuration. In the end, got it working by doing the following.

1.) In .bp-config/httpd/extra/httpd-php.conf, set this.

RewriteEngine on

RewriteCond $1 !^(/index\.php|/images|/robots\.txt)
RewriteRule ^(.*)$ /index.php$1 [PT]

ProxyPassMatch ^(/index.php.*)$ fcgi://#{PHP_FPM_LISTEN}${HOME}/htdocs$1

This rewrites URLs that match the RewriteCond (i.e. anything but /index.php, /robots.txt or /images) by stacking the URL onto the end of “/index.php”. Ex: “/info” -> “/index.php/info”.

It then proxies that URL to index.php through to PHP-FPM.

2.) This got me most of the way there, but Slim’s routing still didn't work. As far as I could tell, it was because mod_proxy_fgci was setting the SCRIPT_NAME environment variable to something that Slim was not expecting. From what I could gather from Slim’s documentation, SCRIPT_NAME should be set to “/index.php”. Thus in my index.php file, I simply hard code SCRIPT_NAME to ‘/index.php’.

<?php
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_ENV['SCRIPT_NAME'] = '/index.php’;
...
// go on to initialize and use Slim

?>

After doing this, the Slim example worked OK for me.

That said, if you’re seeing it work with Nginx, you could go with that too. Either one should work here.

Dan

Ryan Baxter

unread,
Mar 26, 2014, 6:01:46 PM3/26/14
to vcap...@cloudfoundry.org
I finally got it working for me, here is what I have in my httpd-php.conf file.

LogLevel alert rewrite:trace6

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} ^/?(api.*)$
RewriteRule ^/?(.*)$ /index.php [PT]

ProxyPassMatch ^/?(.*php.*)$ fcgi://#{PHP_FPM_LISTEN}${HOME}/htdocs/$1


What is the fcgi URL doing? It seems to be key.

Also why does the buildpack download things from DropBox, shouldn't
this be downloaded from a more stable location?

On Wed, Mar 26, 2014 at 5:34 PM, Daniel Mikusa <dmi...@gopivotal.com> wrote:
> Ryan,
>
> I feel your pain here. I've been playing with this for a bit and as I understand it, the problem is with the way that mod_proxy_fcgi is setting the environment variables like PATH_INFO. The way that it is setting them is not consistent with what Slim is expecting. The mismatch causes 404's and other weird issues.
>
> In my testing, I got a couple configurations that were close to working, but each had a few minor issues that I couldn't resolve through my HTTPD configuration. In the end, got it working by doing the following.
>
> 1.) In .bp-config/httpd/extra/httpd-php.conf, set this.
>
> RewriteEngine on
>
> RewriteCond $1 !^(/index\.php|/images|/robots\.txt)
> RewriteRule ^(.*)$ /index.php$1 [PT]
>
> ProxyPassMatch ^(/index.php.*)$ fcgi://#{PHP_FPM_LISTEN}${HOME}/htdocs$1
>
> This rewrites URLs that match the RewriteCond (i.e. anything but /index.php, /robots.txt or /images) by stacking the URL onto the end of "/index.php". Ex: "/info" -> "/index.php/info".
>
> It then proxies that URL to index.php through to PHP-FPM.
>
> 2.) This got me most of the way there, but Slim's routing still didn't work. As far as I could tell, it was because mod_proxy_fgci was setting the SCRIPT_NAME environment variable to something that Slim was not expecting. From what I could gather from Slim's documentation, SCRIPT_NAME should be set to "/index.php". Thus in my index.php file, I simply hard code SCRIPT_NAME to '/index.php'.
>
> <?php
> $_SERVER['SCRIPT_NAME'] = '/index.php';
> $_ENV['SCRIPT_NAME'] = '/index.php';
> ...
> // go on to initialize and use Slim
> ...

Daniel Mikusa

unread,
Mar 27, 2014, 7:45:43 AM3/27/14
to vcap...@cloudfoundry.org
On Mar 26, 2014, at 6:01 PM, Ryan Baxter <rbax...@gmail.com> wrote:

> I finally got it working for me, here is what I have in my httpd-php.conf file.

Awesome!

> LogLevel alert rewrite:trace6
>
> RewriteEngine on
>
> RewriteCond %{REQUEST_FILENAME} ^/?(api.*)$
> RewriteRule ^/?(.*)$ /index.php [PT]
>
> ProxyPassMatch ^/?(.*php.*)$ fcgi://#{PHP_FPM_LISTEN}${HOME}/htdocs/$1
>
>
> What is the fcgi URL doing?

ProxyPassMatch tells HTTPD what to proxy. It uses the first argument which is a regular expression to determine what it matches. The second argument is where it should proxy to. In this case, it’s going to proxy to a PHP-FPM, which is a FastCGI manager.

The “#{PHP_FPM_LISTEN}” part is from the build pack. It’s a place holder that gets replaced with the location of PHP-FPM. You can use “cf files <app> app/httpd/conf/extra/httpd-php.conf” to look at the file after this value has been replaced.

The “${HOME}” is an environment variable substitution that translates to “/home/vcap/app” at runtime. This gets paired with “$1” to create the full path to your PHP scripts (i.e. /home/vcap/app/htdocs/index.php).

> It seems to be key.
>
> Also why does the buildpack download things from DropBox, shouldn't
> this be downloaded from a more stable location?

The answer to this is because DropBox is free. I need somewhere to host the binary files used by the build pack and since this is a project that I do in my spare time, the fact that it is free is a big help. Since I’ve been developing the build pack, DropBox has worked out very well for the apps I’ve been running apps on PWS (Pivotal’s Hosted CF deployment). The download speeds from AWS, where PWS is hosted, to DropBox have been good and so has the reliability.

If you’re not seeing the same performance from DropBox to your CF installation, I would suggest hosting your own copy of the repo. You can then use the DOWNLOAD_URL option in options.json to override where the build pack downloads the binaries from.

Dan

Ryan Baxter

unread,
Mar 27, 2014, 8:13:41 PM3/27/14
to vcap...@cloudfoundry.org
Thanks for the explanation Dan. The download speeds are fine, just
wondering. Shouldn't you be able to download the binaries from where
ever they are released from? Just trying to understand better so I
can answer these questions when I am asked :)

Also the documentation says that the buildpack contains extensions for
mongo, redis, etc, however I don't see those extensions being
deployed. Are they really part of the buildpack?

Ryan Baxter

unread,
Mar 28, 2014, 9:29:34 AM3/28/14
to vcap...@cloudfoundry.org
On Thu, Mar 27, 2014 at 8:13 PM, Ryan Baxter <rbax...@gmail.com> wrote:
> Thanks for the explanation Dan. The download speeds are fine, just
> wondering. Shouldn't you be able to download the binaries from where
> ever they are released from? Just trying to understand better so I
> can answer these questions when I am asked :)
>
> Also the documentation says that the buildpack contains extensions for
> mongo, redis, etc, however I don't see those extensions being
> deployed. Are they really part of the buildpack?

I figured this out, need to use PHP_EXTENSIONS :)

Daniel Mikusa

unread,
Mar 30, 2014, 5:44:58 PM3/30/14
to vcap...@cloudfoundry.org
On Mar 27, 2014, at 8:13 PM, Ryan Baxter <rbax...@gmail.com> wrote:

> Thanks for the explanation Dan. The download speeds are fine, just
> wondering. Shouldn't you be able to download the binaries from where
> ever they are released from? Just trying to understand better so I
> can answer these questions when I am asked :)

It depends on what you’re trying to download. Most of the projects utilized by the build pack require compilation, such as Nginx, HTTPD & PHP. There are no binary distributions of them available through the official channels. Because you don’t want to wait on every push to compile that stuff, I build the binaries ahead of time and download them instead (it’s possible to build your own binaries and use those instead, if you’d prefer).

There are also some tricks that I use to try and speed up downloads, like splitting the binaries up into core and module packages (ex PHP & extensions are packaged separately), which allows the build pack to only pull down what it needs, as it is needed. This means that while the binary files I publish are the same as if you built them from source yourself, the binaries are packaged into archives in a specific way (like how Debian or Redhat splits their binaries into multiple packages). The scripts here, will take care of building and packaging if you want to do that yourself. Just run them on an Ubuntu machine / vm with the appropriate packages installed.

https://github.com/dmikusa-pivotal/cf-php-buildpack-binary-build-scripts

> Also the documentation says that the buildpack contains extensions for
> mongo, redis, etc, however I don't see those extensions being
> deployed. Are they really part of the build pack?

That’s the suggested way to do it. You could also include a php.ini file with your application and manually enable the extensions. This is more work though, so I don’t recommend it.

Dan

Ryan Baxter

unread,
Mar 30, 2014, 6:45:10 PM3/30/14
to vcap...@cloudfoundry.org
Thanks for the info Dan. Makes sense to me. And thanks for all you help!!!
Reply all
Reply to author
Forward
0 new messages