OS X: Automatic multi-version PHP + Nginx dev stack
This is a pretty old post. I haven't used this for a long time, and I'm not sure it still works the way it did. Since Docker got a huge popularity, you'll probably find a better tutorial with that. Who installs PHP on a host machine nowadays anyway, huh? 😁
Hey there! After a long time I have managed myself to provide you my current local php development setup. Why you should be interested? Well, it has the following features:
- Based on a domain, it runs the code under php 5.6 or php 7.0. Running project on different php version is as easy as changing
example.dev5
toexample.dev7
, without a need to change a single line in config. Other php versions are really easy to add. - It has support for common php frameworks as well as for custom directory structures. By default, it knows how to run Symfony 2, Laravel & Nette project.
- Running new project is as easy as checking it out to
~/Sites/project
directory and running it atproject.dev7
.
Best of all? Setting this up takes you only few minutes.
Step one — Dnsmasq
You will need to setup local DNS server, to point *.devX
requests to localhost. You will find instructions how to setup dnsmasq on OS X in this Passing Curiosity article.
Are you done? Great! Now update your /usr/local/etc/dnsmasq.conf
file like this:
address=/.dev5/127.0.0.1
address=/.dev7/127.0.0.1
This will allow us to handle multiple domains in next step. Specifically dev5
and dev7
. Save the file and restart the dnsmasq by running sudo brew services restart dnsmasq
. Sudo is important here as it’s system service. Now let’s handle these domains in nginx.
Step two — PHP-FPM
First, run this command to make php packages available in homebrew:
brew tap homebrew/homebrew-php
Then install PHP 7 using brew: brew install php70
. Now, open /usr/local/etc/php/7.0/php-fpm.conf
and update it with this. Make sure you replace user
value with your OS X username.
[global]
error_log = /usr/local/etc/php/7.0/logs/php-fpm.log
daemonize = no
[www]
user = wod
group = staff
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
php_admin_value[error_log] = /usr/local/etc/php/7.0/logs/error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
I will not go into details of this, only important line is the one setting listen to localhost at port 9001. This needs to be unique across your php versions.
Also, make sure to create logs
directory:
mkdir /usr/local/etc/php/7.0/logs
Now, continue by installing php 5.6: brew install php56
. and configuring it in similar way (/usr/local/etc/php/5.6/php-fpm.conf
). Again, remember to replace wod
with your username.
[global]
error_log = /usr/local/etc/php/5.6/logs/php-fpm.log
daemonize = no
[www]
user = wod
group = staff
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
php_admin_value[error_log] = /usr/local/etc/php/5.6/logs/error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
Notice that we run this one on port 9000.
Again, create logs
directory:
mkdir /usr/local/etc/php/5.6/logs/
Don’t forget to restart services: brew services restart php56
and brew services restart php70
.
Step 3 — Nginx
Install nginx: brew install nginx
and update the /usr/local/etc/nginx/nginx.conf
. Again, you need to replace wod
with your username.
worker_processes 1;
user wod staff;
error_log /usr/local/etc/nginx/logs/error.log debug;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 10M;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /usr/local/etc/nginx/logs/access.log main;
error_log /usr/local/etc/nginx/logs/wildcard-error.log debug;
index index.html index.php app_dev.php;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
fastcgi_read_timeout 600;
server {
listen 80;
set $domain $host;
# defaults to php 5.6
set $passto "127.0.0.1:9000";
# update with your username!
set $baseroot "/Users/wod/Sites";
set $index "index.php";
# example.dev5 should go to example directory
if ($domain ~ "^(.[^.]*)\.dev5$") {
set $domain $1;
set $servername "${domain}.dev5";
}
# whatever.example.dev5 should still go to example directory
if ($domain ~ "^(.[^.]*).(.[^.]*)\.dev5$") {
set $domain $2;
set $servername "$1.${domain}.dev5";
}
# example.dev7 to example
if ($domain ~ "^(.[^.]*)\.dev7$") {
set $domain $1;
set $passto "127.0.0.1:9001";
set $servername "${domain}.dev7";
}
# whatever.example.dev7 to example
if ($domain ~ "^(.[^.]*).(.[^.]*)\.dev7$") {
set $domain $2;
set $passto "127.0.0.1:9001";
set $servername "$1.${domain}.dev7";
}
set $root "${baseroot}/$domain";
# Nette Sandbox structure
if (-d "${root}/www"){
set $root "${root}/www";
set $index "index.php";
}
# Laravel structure
if (-d "${root}/public"){
set $root "${root}/public";
set $index "index.php";
}
# Symfony app
if (-d "${root}/web"){
set $root "${root}/web";
set $index "app_dev.php";
}
server_name $servername sandbox.dev;
root $root;
access_log /usr/local/etc/nginx/logs/$domain-access.log main;
charset utf-8;
location / {
try_files $uri $uri/ /$index$is_args$args;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass $passto;
fastcgi_index $index;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
Important parts have description in comment above, basically what we are doing here is that based on domain we set some variables. That’s all.
Save & restart nginx: sudo brew services restart nginx
. sudo
is required for nginx because it listens at port 80.
Well, I think we are done — you should be able to access project in ~/Sites/project
directory at project.dev5
and project.dev7
now.
Please leave me a response if you are having any troubles following these steps.
Thank you for taking time to go through my post!