Doing development on multiple projects can be a burden from time to time. One project would be running on PHP 5.3, while another still needs 5.1. Sometimes you need a MySQL server, while on other occasions, you need a NoSQL solution like couchDB or MongoDB together with all kind of gearman functionality. This article shows you how I’ve setup such a development platform that allows you to quickly create new projects, and still maintain flexibility when you need it.
Step 1: VirtualBox
Do you have a MacBook? Great. Do you use Ubuntu? Perfect. Are you running on windows? Super! But just DO NOT USE your own distribution’s PHP / packages. There are actually good reasons for this:
- You can easily work with multiple versions of the components (PHP, Apache, Nginx, MySQL etc) without affecting the rest of your (base) system. Especially when you are working with 3rd party systems like Zend Server, things can go downhill really fast if you need to switch from project A to project B since it might involve downgrading packages on your system (which is annoying at best)
- 9 out of 10 times you are not running on the same OS as your production server will be. Now this isn’t necessarily a problem, but the closer you can mimic such an environment, the better since it will result in less problems during testing and acceptance.
Leave your system as-is, and run through virtualBox. If you don’t like it, try it anyway as I will explain later why. VMWare Fusion might also do the trick, but has some downsides.
The way I’ve setup my box is like this:
I have 2 different base templates: a Debian 6.0 template and a CentOS 6.0 template. Besides those templates, I’ve got a single Debian 6.0 server running which is my “global” project machine. Every project that doesn’t need their own VirtualBox (because it’s a simple project, or a project that doesn’t need any 3rd party components), I create that project on my global machine. Otherwise, I either clone the CentOS 6.0 or Debian 6.0 template. Now here is why you should use VirtualBox instead of VMWare: you can actually create such machines from scratch with the help of Vagrant, which allows you to automatically create and setup virtualboxes from standard templates already available on the internet. Check out http://vagrantup.com/ and http://virtualbox.es for more information, but for now, let’s just assume you use the standard templates like I have, which are nothing more than simple netinstalls of the distribution, with some of some standard tools installed while doing development like ssh/strace/mc/systools etc.
So: need a quick project: use the global development machine, otherwise, clone a new machine and use that one. They are not killing your performance, they are not eating away precious resources and you can have multiple open at any single moment. I run most virtual machines with 512MB of memory (sometimes more when I need to do stuff with data), and I can still have 5+ machines open at any single time on my MacBook without noticing any downside or any lag.
Using this will isolate everything perfectly: you need to work on a PHP4 legacy application? No prob: just fire up a virtualbox with a PHP4 installation and start developing (assuming you are about to port it to the latest PHP version, right?). You can even work on several projects at the same time. Your boss will love it!
Step 2: Virtualhosting & DNS
Now, we have our virtualboxes which all have a connection to the outside world through the “Bridge mode” network adapters. One annoying fact of virtualbox is that you need to connect your Vbox adapter to a physical adapter in your computer. For instance, on my macbook I have to connect adapter 1 to my ethernet adapter. But when I’m connected to a WiFi system, the virtualboxes cannot use the internet. That’s why I have two bridged adapters: adapter 1 for ethernet, and adapter 2 for WiFi. No matter how my MacBook is connected, the virtualboxes still have connectivity to the outside world (although it might require a restart of the network interfaces sometimes).
The other adapter I have is the Host-Only adapter. This allows me to communicate with my virtualboxes on a private subnet (192.168.56.x in my case). I use this for remote debugging and file sharing which we come to later. Normally, the bridged adapters get their IP addresses from a DHCP server you are running at home or at your company network, but you can also opt for some static IP’s if that makes your life easier. I normally do not care since I do most of my work through the private subnet.
VirtualBox also runs a DHCP server for its host-only (192.168.56.x) network. I don’t think (never actually checked though) if there is a way to bind IP’s to your virtualbox MAC addresses, so I normally use static addresses. My global development server is running on 192.168.56.101, all other machines have different IP’s.
Now that we have network connectivity, we can talk about HTTP. On project-specific boxes I just use standard vhosts and everything, but on my global box this is a pain to work with. I don’t want to spend time setting up new vhosts, or create some tools that generate vhosts for me or such.. And I don’t want to place EVERYTHING in one big vhost / documentroot. Apache actually had got a nice feature called “virtualDocumentRoot” that allows you to create virtualhosts on the fly, so to speak.
<VirtualHost *:80> UseCanonicalName Off VirtualDocumentRoot /wwwroot/%1/public php_value suhosin.srand.ignore 0· <Directory /wwwroot> AllowOverride All </Directory> </VirtualHost>
To use the virtualDocumentRoot directive, you must enable the mod_vhost_alias module. For more information, take a look at the documentation at: http://httpd.apache.org/docs/2.3/mod/mod_vhost_alias.html.
So what this does is setup dynamically virtualhost. It will take the first part (%1) of the URL (for instance: the “projecta” for the url http://projecta.example.org), and use /wwwroot/projecta/public as the document root. In essence I can create a directory /wwwroot/<myproject> and it will automatically be up and running. However, I use the /public as an extra directory so that would be my public webroot. This means that my framework and other components I don’t want to be inside my documentroot to be at /www/myproject, while my index.php, css, images and js files live in /www/myproject/public.
With this setup, creating new projects has become literally a five-second job.
Setting up DNS
Because we didn’t specify a server-name inside our virtualhost, and this actually is the only virtualhost available, everything that points to port 80 on the global development virtualbox IP (192.168.56.101, if you have kept track), will automatically use this virtualhost. So what I’ve done is setup a development DNS (which actually is a live domain), and let a wildcard point to this IP. This means that outside my notebook, it isn’t much use for others, so I prepend it with a subdomain.
This means that I’ve created an A-record: *.dev.mydomain.com A 192.168.56.101.
At this point, if I go to http://projecta.dev.mydomain.com, it will resolve in 192.168.56.101, it will be picked up by the virtualhost and use the virtualdocumentroot /wwwroot/projecta/public. If I go to http://whatever.dev.mydomain.com, it will go to /wwwroot/whatever/public. Sweet!
If you need separate project boxes, just create another A-record (or update your /etc/hosts file) and point to the corresponding 192.168.56.x IP).
Step 3: Setup your tools
Now that we are able to connect, we need to start developping. Some will try to use the “shared folders” option that VirtualBox is able to. I don’t. I never really liked it, and for all my purposes, I just use a global samba share pointing to /wwwroot for doing all work. This means I don’t need to fuss around with strange FTP-uploads everything I modify a file, and my IDE is perfectly happy as treating it as a local filesystem since it doesn’t know any better. If you use VIM or another editor, you can work directly from the box through SSH (I always have Iterm2 open to the boxes). Samba is fast enough when using locally on your system. Even things like searching through your projects isn’t a real problem. Especially when you are running from an SSD, but even with a normal SATA drive, I didn’t notice any problems, even on large projects with the Zend of Symfony frameworks.
So connect your samba share to /wwwroot, mount it to your local /wwwroot, create a W:\ drive, whatever. At this point, creating a directory automatically creates a new project.
Please, pretty please with sugar on top, if you don’t use XDebug, install it and try to use it. It will make your life easier and development / debugging faster. I know it will be hard to not use print_r() and var_dumps & die(), but really, don’t do that anymore. XDebug is so much better in doing a debugging job than you ever can with print_r.
Installing XDebug is a breeze and most distributions come with a pre-compiled XDebug package, so an “apt-get install php5-xdebug” or “yum install php53-xdebug” should be more than enough to get it up and running. Note however, that you have to change some settings in XDebug to get it up and running for remote debugging:
xdebug.remote_enable = 1 xdebug.remote_connect_back = 1 xdebug.idekey = "PhpStorm1" xdebug.remote_port = 9000 xdebug.remote_handler=dbgp xdebug.remote_autostart = 1
It basically tells XDebug that whenever it has been started, it must connect to the debugger that is running on the machine that is requesting the page (basically, it connects back to $_SERVER[REMOTE_ADDR]:9000). We talk about connecting XDebug in the next section, for now it’s enough to realize that XDebug will *always* try to connect to a remote debugger because we have configured “xdebug.remote_autostart = 1″. Some might not like this, and want to manually start XDebug. This would mean you have to set the option to 0, and issue a XDEBUG_SESSION_START=<idekey> to start the system automatically. Most browsers have extensions that actually makes it easier for you to do this. For instance, the FireFox extension “easy-debug”. But on the whole I really like the autostart feature, since I don’t have to worry about browsers. Every call, even from curl or standard telnet, will trigger XDebug.
Step 4: Set up your IDE
Now, we should setup our IDE. I personally like PHPStorm, but any other would probably work too (netbeans, eclipse, aptana etc). Since we have connected our /wwwroot directly into our main machine, we can just create new projects inside this directory by creating a subdirectory. PhpStorm saves all project information inside a hidden “.idea” directory inside the project root, but I normally let my version control system (git normally) ignore this by adding the directory to the .gitignore file. For SVN users you can issue a propset svn:ignore to ignore the directory (if you don’t use a VCS, shame on you!).
Now, the only thing really left to do is setting up remote debugging. With PhpStorm, this is a breeze: just hit the menu “Run | Start listening for PHP Debug Connections” and you are good to go! There is a shortcut on the toolbar (the red phone), that turns green when remote debug listening is enabled. What it does is opening a port (9000) on your local system. Since we have ordered xdebug to create a remote connection back to the requesting client, it means that when we start listening for connections, and we hit refresh in our browser, xdebug will automatically connect to phpstorm. You can set a breakpoint at the beginning of your index.php (or other entrypoint) to see xdebug in action.
The only thing that you need to make sure that you setup your path mapping correctly. Sometimes it cannot find the correct source-file to debug. For instance: your PHP file does a “include(“../test.php”)”, it only knows that you want the file /wwwroot/yourproject/test.php (assuming that you called the include file from /wwwroot/yourproject/public/index.php). XDebug will “tell” your IDE that it needs the file /wwwroot/yourproject/test.php.
Now, your IDE doesn’t know anything about /wwwroot/yourproject, since that is the directory on the REMOTE filesystem, which may or may not be mapped differently. On a windows machine, your IDE might know this file as W:\yourproject\test.php, or on your ubuntu machine, /mnt/wwwroot/yourproject/test.php, or on your macbook /Volumes/wwwroot/test.php. So you must tell PHPStorm to map the file to the correct directory. Luckily, PHPSTorm will tell you that it cannot find the file, and gives you the opportunity to map the file. What I normally do is not map that file directly, but map the root-directory. So I will tell phpstorm that the remote “/wwroot/yourproject” directory is locally mapped to “/Volumes/wwwroot/yourproject”. From that mapping, it can deduce where all other files inside your project are so you would only need to do the mapping once (I don’t know if you can create a global mapping in phpstorm: /wwwroot to /Volumes/wwwroot, because that would mean that phpstorm would always find the files, regardless of project.
That’s it, really. There are all kind of nifty things you can add yourself, like using VCS through the IDE, but I prefer command line so I always have a iTerm open to do the git-stuff. And I’ve got a global alias for /phpmyadmin, so I can always directly goto my phpmyadmin screen (yeah, sue me) and some other nifty stuff. On the whole, it’s a very easy setup that works pretty well. I can do symfony2, zend framework side by side without problem. I have a simple setup of memcache, mysql, and other components on my global machine to test stuff and when doing more powerful jobs, I clone a new machine and work from there. It’s easy, fast, and really isolate your work. And I truly work on two or three projects at once, on different PHP versions as well, so isolation like this is perfect, although I assume you all have other ways of setting up your development environment. Do you use this kind of isolation? Do you have another way of setting up projects? Do you use vagrant and/or puppet to maintain your machines? I’m curious on how to improve my system, and how yours work, so do leave a comment :)