Running multiple Homestead boxes next to each other (PHP 7 and PHP 5.6)

Now that PHP 7 is being adopted by more and more web applications in production environments, dealing with legacy code in local development becomes increasingly more difficult because of the different PHP versions out there and deprecated functionality. The reason I’m writing this article is that for a big project, we’re planning a migration from PHP 5.6.15 to PHP 7.x, but had some issues with the legacy mcrypt extension (which we adapted from Laravel 4.2) in local development.

The mcrypt extension has been abandonware for nearly a decade now, and was also fairly complex to use. It has therefore been deprecated in favour of OpenSSL, where it will be removed from the core and into PECL in PHP 7.2.

I’ve been using Laravel homestead for years now because it’s such an easy tool for Laravel development. But it’s not that easy to setup a 2nd Laravel box, with an older PHP version. In this blog I’ll show you how to do it. Hopefully it will help other developers.

I’m assuming you already have a Homestead box up & running. Make a backup of your Homestead.yaml configs and save it someplace safe, because we’re going to start all over just to make sure. All your code is hosted locally anyway so you’re not going to lose anything, unless you made custom changes to the VM which is not a good idea anyway.

Remove all boxes:

vagrant global-status --prune
 
id       name        provider   state   directory
----------------------------------------------------------------------------
2d2e481  homestead56 virtualbox running /Users/Chris/Homestead56
1042da7  homestead7  virtualbox running /Users/Chris/Homestead
 
vagrant destroy [id]

Now let’s setup our new VM’s:

vagrant box add laravel/homestead
vagrant box add laravel/homestead --box-version 0.3.3

Pull in Homestead:

cd ~
 
git clone https://github.com/laravel/homestead.git Homestead
 
git clone https://github.com/laravel/homestead.git Homestead56

Here’s the tricky part, edit ~/Homestead56/init.sh, change the homesteadRoot variable. Otherwise it will try to overwrite the file in the other Homestead folder.

#!/usr/bin/env bash
 
homesteadRoot=~/.homestead56
 
mkdir -p "$homesteadRoot"
 
cp -i src/stubs/Homestead.yaml "$homesteadRoot/Homestead.yaml"
cp -i src/stubs/after.sh "$homesteadRoot/after.sh"
cp -i src/stubs/aliases "$homesteadRoot/aliases"
 
echo "Homestead initialized!"

Next run:

bash Homestead/init.sh
bash Homestead56/init.sh

This will publish the config files to ~/.homestead/ and ~/.homestead56/.
In order for both environments to function properly we’ll have to make a couple of changes to the Homestead.yaml files.

~/.homestead/Homestead.yaml:

--
ip: "192.168.10.10"
name: "homestead7"
memory: 2048
cpus: 1
provider: virtualbox
 
authorize: ~/.ssh/id_rsa.pub
 
keys:
    - ~/.ssh/id_rsa
 
folders:
    - map: ~/Sites
      to: /home/vagrant/Sites
 
sites:
    - map: randomapp.app
      to: /home/vagrant/Sites/randomapp/public
 
databases:
    - homestead

~/.homestead56/Homestead.yaml:

---
ip: "192.168.10.11"
name: "homestead56"
version: 0.3.3
memory: 2048
cpus: 1
provider: virtualbox
 
authorize: ~/.ssh/id_rsa.pub
 
keys:
    - ~/.ssh/id_rsa
 
folders:
    - map: ~/Sites
      to: /home/vagrant/Sites
 
sites:
    - map: randomapp.app
      to: /home/vagrant/Sites/randomapp/public
 
databases:
    - homestead

As you can see we’re setting up different IP’s for the VM’s (duh), specifying an easy-to-remember name, and most important of all, we’re setting the VM versions. These define what PHP version will be installed.

Bonus, symlink your vagrant commands to your VM environments.

vim ~/.bash_profile

# Homestead
function homestead() {
    ( cd ~/Homestead && vagrant $* )
}
function homestead7() {
    ( cd ~/Homestead && vagrant $* )
}
function homestead56() {
    ( cd ~/Homestead56 && vagrant $* )
}

Now you can do something like:

homestead56 up
homestead56 ssh
 
homestead7 up
homestead7 ssh

If you want more information on the available PHP / Homestead versions, try these links:
https://laravel.com/docs/5.4/homestead#old-versions
https://laravel-news.com/using-older-versions-of-homestead

phpMyAdmin on Laravel Homestead

What is Laravel Homestead?

“Laravel Homestead is an official, pre-packaged Vagrant “box” that provides you a wonderful development environment without requiring you to install PHP, HHVM, a web server, and any other server software on your local machine. No more worrying about messing up your operating system! Vagrant boxes are completely disposable. If something goes wrong, you can destroy and re-create the box in minutes!” – laravel.com

That’s great!

But how can I reach my databases via phpMyAdmin?

Unfortunately it doesn’t come with phpMyAdmin out of the box. You’re forced to setup a local PMA install, or use an external application like Sequel Pro.
In this short blog post I’ll teach you how to setup phpMyAdmin on your Laravel Homestead box. Assuming you followed all the instructions on laravel.com to setup your box, start by SSH’ing into it:

homestead ssh

Install phpMyAdmin via apt-get:

sudo apt-get install phpmyadmin

(do NOT select apache2 or lighttpd. Just continue without them).

Next, make a symlink between the PMA directory and your web root:

sudo ln -s /usr/share/phpmyadmin/ /home/vagrant/Sites/phpmyadmin
cd ~/Sites && serve phpmyadmin.app /home/vagrant/Sites/phpmyadmin

Go back to your local environment and modify the hosts file like this:

127.0.0.1  phpmyadmin.app

phpMyAdmin is now reachable via http://phpmyadmin.app:8000

Or like this:

192.168.10.10  phpmyadmin.app

phpMyAdmin is now reachable via http://phpmyadmin.app

That’s it!

Pushing Laravel logs to Loggly

logo_loggly

Laravel uses the Monolog logging library for logging, however, it’s saving all logs to a local directory by default. Not a very useful thing in a production environment.

That’s where Loggly comes into play. Loggly acts as a central (cloud)platform that can receive logs from a multitude of platforms such as php frameworks (Laravel being the one), operating systems, their own Loggly API, and a lot of other frameworks and services. In this short tutorial I’ll show you how to implement Loggly into your multi-environment Laravel project.

1) Update composer

First we need to update composer so we’re 100% that we’re using the latest Monolog version, because older versions don’t support Loggly.

composer self-update
composer update

2) Setup Loggly account

Go to https://www.loggly.com and create a (free) account.

3) Add Loggly credentials to your Laravel environment

Now the nice thing about Loggly is that it’s pretty easy to configure. Go to https://www.loggly.com/tokens and setup a token for your project. Do this to keep things seperated. Got a big project? Then you might want to use source groups as well.
Now create a new file in your Laravel folder: /app/config/services.php and add this:

return array(
	// credentials for loggly.com
	'loggly' => array(
		'key'	=> 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx',
		'tag'	=> 'ProjectName_' .strtolower(App::environment()),
	),
);

Now if you’re using Laravel’s environment configuration you should do this:

return array(
	// credentials for loggly.com
	'loggly' => array(
		'key'	=> getenv('services.loggly.key'),
		'tag'	=> 'ProjectName_' .strtolower(App::environment()),
	),
);

And then store it in a similar way in your .env.local.php file, or on Laravel Forge.

4) Install the logging code

Now this is where the magic happens. Open up /app/start/global.php and find the “Application Error Logger” section. Replace it with this code:

/*
|--------------------------------------------------------------------------
| Application Error Logger
|--------------------------------------------------------------------------
|
| Here we will configure the error logger setup for the application which
| is built on top of the wonderful Monolog library. By default we will
| build a rotating log file setup which creates a new file each day.
|
*/
 
$logFile = 'log-'.php_sapi_name().'.txt';
Log::useDailyFiles(storage_path().'/logs/'.$logFile);
 
/*
 * Setup Loggly Handler
 */
$handler = new \Monolog\Handler\LogglyHandler(Config::get('services.loggly.key'),\Monolog\Logger::DEBUG);
$handler->setTag(Config::get('services.loggly.tag'));
 
$logger = Log::getMonolog();
$logger->pushHandler($handler);

This way, every environment will have it’s own tag and you can easily see in the Loggly dashboard when something happens on your dev/staging environment, or if it’s on live. I also strongly advise to setup alerts within Loggly so you get notified once you got a couple of 500 errors coming around.
Now the nice thing about Loggly is that you can go back in time and easily see in the stats how many errors occured in the past 24 hours, good luck doing that with log files! This is especially useful for keeping a close eye on cronjobs and incoming API calls.

5) Testing

Throw some 404 errors by opening up a page in your project and appending some random characters. Wait a couple of minutes and there we go. You should see something like this:
Screen Shot 2014-10-02 at 11.05.36

Also worth noting is that all other Laravel logging functions will also push to Loggly.

More info about Laravel’s logging features: http://laravel.com/docs/4.2/errors#logging
More info about Loggly: https://www.loggly.com

Laravel 4 released

Great news! My favorite PHP framework Laravel finally released version 4 after 5 public beta’s and months of waiting. Why am I so happy? Well.. it makes my job as a developer a lot simpler compared to my Zend Framework days.

Here’s why you must try Laravel

  • – Smart framework: write less code (faster + easier)
  • – Works with composer, the PHP plugin tool
  • – Filters, events, rest, built-in auth, … it has it all out of the box
  • – Elequent ORM: the smartest and easiest PHP ORM today!
  • – Unit testing for all components

So what’s new in Laravel 4?

  • – L4 now runs on PHP >= 5.3.7
  • – PSR-0 code standards are used
  • – Composer (packagist.org) instead of Laravel bundles
  • – Lot’s of small but convenient little tweaks and options
  • – Mail is now built in
  • – L4 is built with Unit Testing in mind
  • – Great community!
  • – The command line tool (artisan) is now more powerful

 

Here’s what changed in L4:
https://github.com/laravel/framework

Laravel 4 introduction

Download Laravel @ http://laravel.com/, and make sure to follow them on Twitter: https://twitter.com/laravelphp

DirectAdmin API packagist/composer package

Because Laravel Four is trading in bundles for Composer I decided it’s time to create a package for this. After all, it’s a very handy library!

You can find it here:
https://packagist.org/packages/directadmin/directadmin

And here:
https://github.com/kryap/php-directadmin

Test case:

$da = new \DirectAdmin\DirectAdmin();
$da->connect('domain.be', 2222);
$da->set_login('username', 'password');
 
$da->set_method('get');
$da->query('/CMD_API_SHOW_DOMAINS');
$da = $da->fetch_body();
 
var_dump($da);

Again, make sure you read the DirectAdmin API docs @ http://www.directadmin.com/api.html

Laravel DirectAdmin bundle

I recently started playing with the Laravel PHP framework and my god I love it! So fast and so easy.. yet you have total freedom!
Anyway I wrote my first bundle for Laravel (a bundle is something like a package you can use in multiple projects) which is an implementation of the DirectAdmin API.

You can download it from github (https://github.com/kryap/laravel-directadmin) or use the laravel artisan tool:

php artisan bundle:install directadmin

Now you only have to tell your Laravel application you want to use this bundle so add this to application/bundles.php:

return array(
    ...
    'directadmin' => array(
        'auto'  => true
    ),
    ...
);

You might want to change the config/settings.php default values in case you’ll just access 1 static DA server.

return array(
    'host'          => '127.0.0.1',
    'port'          => 2222,
    'login'         => '',
    'password'      => ''
);

And you’re ready to play with it! Get an instance:

$da = IoC::resolve('DirectAdmin');

And fetch some DA info:

$da->set_method('get');
$da->query('/CMD_API_POP',
    array(
        'domain' => 'domain.com',
        'action' => 'list'
    ));
$da = $da->fetch_body();

By the way, make sure you read the DirectAdmin API docs @ http://www.directadmin.com/api.html