Chris Hope's LAMP Blog - The Electric ToolboxChris Hope's LAMP Blog - The Electric Toolbox

Linux Apache MySQL and PHP articles by Chris Hope

This is Chris Hope's blog for Linux, Apache, MySQL and PHP (known as LAMP) and Javascript/jQuery. I started this website several years ago with articles about web programming, Linux and Windows tips and tricks, howtos etc.

The ten most recent articles can be found below in their entirety. Navigating the sections in the right navigation (under Categories) will bring up all the other posts, and you can also use the search box at the top of the page to find what you might be looking for.

Flaxmill Bay website wins NZ Tourism Guide's weekly awardFlaxmill Bay website wins NZ Tourism Guide's weekly award

Posted July 28th, 2014 in Case Studies

We recently re-launched the Flaxmill Bay website with a fully responsive, beautiful design. And today we discovered it had won New Zealand Tourism Guide's weekly award!

They noted (my emphasis added):

Flaxmill is located in Mercury Bay on the east coast of the Coromandel Peninsula, and is the perfect place unwind, relax and recharge. Their website features responsive design at its very best! Add to that some stunning imagery, ease of navigation, website design and layout, this makes for one of the best winners in recent time.

Nice!

Check out the Flaxmill Bay website, New Zealand Tourism Guide website and award website, and the Reserve Group who did the design work in Photoshop.

.gitignore to ignore vendor directory but include composer.gitignore to ignore vendor directory but include composer

Posted May 24th, 2014 in Laravel PHP Framework and PHP

This post shows how to exclude the vendor directory but include the vendor/composer directory when using a PHP project e.g. Laravel. I'm not saying you should do this; this post is more as a future reference in case I decide I need to do this in the future.

.gitignore file

After a bit of fiddling around I got it to work with these two lines. The first excludes everything under vendor and the second makes an exception for vendor/composer and allows it to be included in git.

/vendor/*
!/vendor/composer/

Again, just because I've posted this here doesn't mean I'm suggesting you should do this. Once I've spent more time with Laravel and deploying projects I'll make my decision. I've simply posted this as a reference for myself for the future.

502 Bad Gateway error after upgrading Nginx and/or PHP502 Bad Gateway error after upgrading Nginx and/or PHP

Posted May 23rd, 2014 in Nginx Web Server and PHP

After doing an upgrade on my Debian virtual server, which upgraded PHP and Nginx, I got a "502 Bad Gateway" error when browsing websites on that server. This post shows how to fix this problem, and the configuration option to prevent it occurring again on reboot. 

tl;dr

Edit /etc/php5/fpm/pool.d/www.conf and uncomment the following:
    listen.owner = www-data
    listen.group = www-data
    listen.mode = 0660

Then run:
    sudo service php5-fpm restart

Longer answer

As well as the error in the browser, I was getting this error in the Nginx error log: 

[crit] 2686#0: *1 connect() to unix:/var/run/php5-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.50.1, server: [...], request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "[...]"

The problem is caused by the permissions and ownership of the /var/run/php5-fpm.sock file, which after I'd done the upgrade change to something like root:root and 0660, so it couldn't be accessed by the www-data user which Nginx was running as.

The immediate solution is to change the permissions and/or ownership of the file like so:

chmod 0666 /var/run/php5-fpm.sock

OR

chmod 0660 /var/run/php5-fpm.sock
chown www-data:www-data /var/run/php5-fpm.sock

The only catch is this won't persist after the server is restarted. To prevent the issue from occurring again, edit the /etc/php5/fpm/pool.d/www.conf file:

sudo nano /etc/php5/fpm/pool.d/www.conf

Locate the following lines and uncomment them:

listen.owner = www-data
listen.group = www-data
listen.mode = 0660

If you already changed the ownership/permissions of the socket file as shown above, then you don't need to do anything else now. If you didn't, then run this: 

sudo service php5-fpm restart

This re-creates the socket file with the ownership and permissions as configured in the file.

Debian on VMWare Fusion Mac host shared files issue with PHPDebian on VMWare Fusion Mac host shared files issue with PHP

Posted May 22nd, 2014 in Linux/Unix/BSD, OSX, PHP and VMWare

I was getting PHP parse errors after updating files using composer or Laravel's artisan on a VMWare Fusion 6.0.2 Debian Linux guest on a Mac host. The files were shared using VMWare Fusion's sharing settings and mounted on the guest.

tl;dr

Upgrade to the latest version of VMWare Fusion (6.0.3 at the time of this post) and re-install VMWare Tools. You may possibly need to apt-get update & apt-get upgrade. YMMV.

A little more detail about the problem

I was running:
- VMWare Fusion 6.0.2
- Debian 7 Wheezy
- Mac OSX 10.9.3

The files being updated were on the Mac host, but mounted on the Debian guest using VMWare Fusion's sharing settings.

Every time I'd update composer, or run e.g. "php artisan dump-autoload" I'd end up with PHP parse errors, such as 'PHP Parse error:  syntax error, unexpected '' => $vendorDir . '/laravel/fr' (T_ENCAPSED_AND_WHITESPACE) in [...] /vendor/composer/autoload_classmap.php on line 1952'

Running the same artisan or composer command from Terminal on the Mac would work fine.

Re-saving the file on the Mac would fix the problem.

Restarting the guest would fix the problem.

Unmounting the host filesystem and remounting it would fix the problem.

None of these solutions is any good.

The fix that worked for me

Upgrading to the latest version of VMWare Fusion (6.0.3 at the time of writing) and re-installing VMWare Tools fixed the problem.

I also do a "apt-get update & apt-get upgrade" to get Debian up to date, which I did inbetween updating Fusion and re-installing Tools. Whether you need to do this or not, I am not sure.

The only catch with this "fix" is that the probem may come back again in later updates to Fusion, and of course it may not work for you. Good luck!

Laravel Nginx ConfigLaravel Nginx Config

Posted May 17th, 2014 in Laravel PHP Framework, Nginx Web Server and PHP

I've just started learning the Laravel PHP framework and it comes with an .htaccess file for working with Apache, but I use Nginx these days, so obviously the .htaccess file doesn't work. Here's what I used in the Nginx config for Laravel.

Nginx Laravel config

Replace your.domain.name with your domain name, and /path/to/laravel/public to the public directory under your Laravel project:

server {

    server_name your.domain.name;
    root /path/to/laravel/public;
    index index.php;
    include includes/php.conf;
    location / {
        try_files $uri $uri/ /index.php$is_args$
    }
    
}

I have a php.conf file which I use in Nginx (included in the above config) to have the PHP settings used across all my websites. This contains the following:

fastcgi_pass unix:/var/run/php5-fpm.sock;

fastcgi_param	QUERY_STRING		$query_string;
fastcgi_param	REQUEST_METHOD		$request_method;
fastcgi_param	CONTENT_TYPE		$content_type;
fastcgi_param	CONTENT_LENGTH		$content_length;

fastcgi_param	SCRIPT_FILENAME		$request_filename;
fastcgi_param	SCRIPT_NAME		$fastcgi_script_name;
fastcgi_param	REQUEST_URI		$request_uri;
fastcgi_param	DOCUMENT_URI		$document_uri;
fastcgi_param	DOCUMENT_ROOT		$document_root;
fastcgi_param	SERVER_PROTOCOL		$server_protocol;

fastcgi_param	GATEWAY_INTERFACE	CGI/1.1;
fastcgi_param	SERVER_SOFTWARE		nginx/$nginx_version;

fastcgi_param	REMOTE_ADDR		$remote_addr;
fastcgi_param	REMOTE_PORT		$remote_port;
fastcgi_param	SERVER_ADDR		$server_addr;
fastcgi_param	SERVER_PORT		$server_port;
fastcgi_param	SERVER_NAME		$server_name;

fastcgi_param	HTTPS			$https if_not_empty;

fastcgi_buffer_size         128k;
fastcgi_buffers           4 256k;
fastcgi_busy_buffers_size   256k;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param	REDIRECT_STATUS		200;

fastcgi_split_path_info ^(.+\.php)(.*)$;

If there's anything else you think needs to into the config (either the server config or the include file), please add it using the comments below.

Make multiple displays on Mavericks work like previous versions of OS XMake multiple displays on Mavericks work like previous versions of OS X

Posted May 15th, 2014 in OSX (Updated June 20th, 2014)

OS X Mavericks introduced having the menu bar, dock and alt-tab app switcher displaying on multiple displays. I was having an odd frustration where whenever I switched to Google Chrome and used it on my MacBook Pro's display, TextWrangler would pop over the top of every app including Chrome in the external monitor. It's possible to restore the display behaviour to how it was before in the previous version of OS X as shown in this post.

tl;dr

System Preferences -> Mission Control -> uncheck "Displays have separate spaces" -> logout and log back in again.

This solved my issue with TextWrangler popping over the top all the time, but after a week I reverted back again because I preferred the way Mavericks works by default. Since reverting back, and after 8 hours of work, TextWrangler hasn't once popped back over the top of other apps. Go figure...

Apple information about Spaces

Just before we get into it, here's the information about Spaces from the Apple website here:

App windows are assigned to a single space. You can use Mission Control to see all of the Spaces assigned to each display and to move them. To move a Space, drag it from one display to the other.

Windows in Mavericks reside in a single space by default, so they usually don't span multiple displays. When a window is dragged between displays, it appears translucent on one of the displays to indicate this. After you are done dragging the window, it snaps to one display.

If you need an app window to span multiple displays, deselect the option "Displays have separate Spaces" in the Mission Control pane of System Preferences.

Longer answer with screenshots

Go into the System Preferences app and click the "Mission Control" option as highlighted in the screenshot below. You don't need to type "mission" or "mission control" into the search box, I just used it in the screenshot to nicely highlight the icon :)

select mission control in system preferences

In the Mission Control settings, there's an option labelled "Displays have separate Spaces" as highlighted with the red box in the screenshot below. Uncheck this option.

unheck the displays have separate spaces option

You'll now see that it says "requires log out" next to the label. So log out and then back in again. 

and now you need to log out

Displays will now work like in previous versions of OS X; some of the things include:

  • app window "glow" appears on the other screen when the app is highlighted
  • there's only one menu on the primary display
  • there's a single dock on the primary display
  • apps can span multiple displays instead of being locked/snapped into one

And finally

As noted at the top of this post, I did this myself because of an issue with TextWrangler always popping on top of other apps when I'd tab into Chrome on the MBP.

Switching off displays having separate spaces fixed this issue, but I found it annoying not having the menu bar on both screens, etc having gotten used to it.

So I switched it back on this morning. 8 hours have passed and I've been working on my MBP for most of the day, and not once has TextWrangler done this again. Fingers crossed it doesn't ever do it again!

Update May 15th 2014: I've found every now and then TextWrangler starts popping up on top again when changing to the app on the MBP display; oddly enough quitting TextWrangler and starting again stops this from happening. Not really what I'd call a solution, but good enough for now.

Using the Pandora API with PHPUsing the Pandora API with PHP

Posted May 13th, 2014 in PHP

Pandora is a music streaming service which can be accessed via a web browser, smartphone/tablet app or desktop app. There is a closed API which can be used to extract some information from Pandora (and edit radio stations etc). This post looks at using a PHP API by Alex Dallaway to access radio stations and bookmarks from Pandora.

tl;dr

Download the code here and try it out. Note that I test this sort of stuff using the PHP CLI and not in a web browser, so all the line breaks are \n and there's no HTML formatting.

Disclaimer

As far as I am aware, the Pandora API is closed and not documented officially. It is therefore subject to change at any time and the code on this page may not work in the future. The PHP API can be downloaded from GitHub, and there's some 3rd party documentation of the API here.

Motivation for doing this

I wanted to be able to export my list of thumbed up tracks from Pandora with PHP. Unfortunately there doesn't appear to be a (known) API call for doing this so my efforts didn't achieve much. However, I thought I should post the code examples here for other people who would like to have a go as well.

There are a variety of in browser methods to extract the list of likes, including browser plugins such as this one which will turn Pandora likes into Spotify playlists. I haven't tried it myself yet so don't know how well it works.

Example - logging in

Set the correct path to the Pandora.php API library in the require_once() statement and set up $username and $password variables with your Pandora username and password.

I set up the handleError() function to provide a simple way to report errors after each API call, but you could just as easily subclass the Pandora class and handle it another way, or take another approach.

require_once('path/to/Pandora.php');

use php_pandora_api\Pandora;

$p = new Pandora();

// login to Pandora
if (!$p->login($username, $password)) {
    handleError($p);
}

function handleError(Pandora $p)
{
    echo "Error message: $p->last_error\n";
    echo "Last request: ";
    print_r(json_decode($p->last_request_data));
    echo "Last response: ";
    print_r(json_decode($p->last_response_data));
    exit;
}

Example - get the station list

Call the 'user.getStationList' method to get the list of stations. The easiest way to get a complete list of the data returned from each call is to use print_r() and then structure your output based on what's returned. The example below echoes out the station token and station name; the token is used in other API calls.

// get the station list and show the token and station name
if (!$response = $p->makeRequest('user.getStationList')) {
    handleError($p);
}
// print_r($response); // use print_r() to get the full dataset returned to see what's available
echo "Station list:\n";
foreach($response['stations'] as $station) {
    echo "  $station[stationToken] - $station[stationName]\n";
} 

Using print_r, a single station's output looks something like this (I've replaced some of the values that I don't want to share with ...):

[1] => Array
    (
        [isQuickMix] => 
        [stationId] => ...
        [stationDetailUrl] => ...
        [genre] => Array
            (
                [0] => Jazz
                [1] => Holiday
            )

        [isShared] => 
        [dateCreated] => Array
            (
                [date] => 19
                [day] => 4
                [hours] => 11
                [minutes] => 44
                [month] => 11
                [nanos] => 51000000
                [seconds] => 27
                [time] => 1387482267051
                [timezoneOffset] => 480
                [year] => 113
            )

        [stationToken] => ...
        [stationName] => ...
        [stationSharingUrl] => ...
        [allowRename] => 1
        [allowAddMusic] => 
        [allowDelete] => 1
    )

Example - get user's bookmarks

Although there doesn't appear to be a method to get the user's likes / thumb ups, you can get a list of the bookmarked songs and artists using the 'user.getBookmarks' call:

// get the user's bookmarks
if(!$response = $p->makeRequest('user.getBookmarks')) {
    handleError($p);
}
// print_r($response); // use print_r() to get the full dataset returned to see what's available
echo "Bookmarked songs:\n";
foreach($response['songs'] as $song) {
    echo "  $song[artistName] - $song[albumName] - $song[songName]\n";
}
echo "Bookmarked artists:\n";
foreach($response['artists'] as $artist) {
    echo "  $artist[artistName]\n";
}

print_r() output for a single song:

[26] => Array
    (
        [sampleGain] => -5.77
        [musicToken] => S1773395
        [bookmarkToken] => ...
        [sampleUrl] => ...
        [albumName] => Divergent Spectrum
        [songName] => Lights (Bassnectar Remix)
        [artUrl] => ...
        [dateCreated] => Array
            (
                [date] => 12
                [day] => 6
                [hours] => 20
                [minutes] => 10
                [month] => 0
                [nanos] => 241000000
                [seconds] => 52
                [time] => 1358050252241
                [timezoneOffset] => 480
                [year] => 113
            )

        [artistName] => Ellie Goulding
    )

and for a single artist:

[5] => Array
    (
        [artistName] => Calvin Harris
        [dateCreated] => Array
            (
                [date] => 12
                [day] => 6
                [hours] => 18
                [minutes] => 0
                [month] => 0
                [nanos] => 970000000
                [seconds] => 24
                [time] => 1358042424970
                [timezoneOffset] => 480
                [year] => 113
            )

        [bookmarkToken] => ...
        [artUrl] => ...
        [musicToken] => R247557
    )

Download the example code

As noted at the top of the post, you can download the code here and try it out. Note that I test this sort of stuff using the PHP CLI and not in a web browser, so all the line breaks are \n and there's no HTML formatting.

The example code also includes the 'station.getPlaylist' which gets a few tracks which the app would then play from the radio station. Check out the disclaimer section at the top of this post which includes a link to some documentation of the API for other available methods.

Get public properties that aren't static with PHP ReflectionGet public properties that aren't static with PHP Reflection

Posted May 8th, 2014 in PHP

PHP has a Reflection API adds the ability to reverse-engineer classes, interfaces, functions, methods and extensions. I needed to use it to get all the publicly accessible properties for a class that aren't static, but there doesn't seem to be a way to get this "out of the box".

This post has my solution, but feel free to let me know a better way of doing it in the comments.

tl;dr

$reflection = new ReflectionClass($this);
$public = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$static = $reflection->getProperties(ReflectionProperty::IS_STATIC);
$properties = array_diff($public, $static);

$properties will now contain only the properties that are public and not static. If you are using namespaces, you'll probably need to prefix ReflectionClass and ReflectionProperty with \ so they look like \ReflectionClass and \ReflectionProperty.

Longer answer

The following class "foo" has three public static properties (a, b, c) and three regular public properties (d, e, f). The "reflect" method echoes out all the public properties.

class foo
{
    public static $a;
    public static $b;
    public static $c;

    public $d;
    public $e;
    public $f;

    public function reflect()
    {
        $reflection = new ReflectionClass($this);
        $properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
        foreach($properties as $property) {
            echo $property->name . "\n";
        }
    }

}

Calling the "reflect" method liek this:

$foo = new foo;
$foo->reflect();

will output this:

a
b
c
d
e
f

This includes the static properties, and I couldn't work out any way to exclude them. Again, as noted at the top of this post, please feel free to let me know if there is a way to do it easily when calling the getProperties() method.

In order to get just the public properties that are not static, the only way I could see to do it was to get the IS_STATIC properties and use array_diff() to get only the public ones.

The revised class looks like this:

class foo
{
    public static $a;
    public static $b;
    public static $c;

    public $d;
    public $e;
    public $f;

    public function reflect()
    {
        $reflection = new ReflectionClass($this);
        $public = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
        $static = $reflection->getProperties(ReflectionProperty::IS_STATIC);
        $properties = array_diff($public, $static);
        foreach($properties as $property) {
            echo $property->name . "\n";
        }
    }

}

The output from reflect() now looks like this:

d
e
f

Is there a better way to do this? Please let me know in the comments section.

Using an image for an unordered list with CSSUsing an image for an unordered list with CSS

Posted May 7th, 2014 in HTML and CSS

I needed to put a checkbox in place of the regular circle used for unordered lists and couldn't remember how to do it (it's been a while!) so thought I'd quickly post it here for future reference. This is the simplest cross browser (including IE8) way to do this; I'll post again in the future an alternative doing straight CSS with no images.

tl;dr

.mylist {
    list-style-image: url(... path to image ...)
    ... other styling ...
}

Longer answer with example

Here's an example of what we are trying to achieve, using a blue check in place of where the unordered list circle bullet would normally be:

  • Apple
  • Banana
  • Cherry
  • Date
  • Fig
  • Grapefruit

The HTML for the above looks like this:

<ul class="mylist">
    <li>Apple</li>
    <li>Banana</li>
    <li>Cherry</li>
    <li>Date</li>
    <li>Fig</li>
    <li>Grapefruit</li>
</ul>

And the CSS looks like this:

.mylist {
    list-style-image: url(/assets/examples/check.png);
}

And that's pretty much all there is to it.

You can add additional styling to the <ul> or <li> if you want to play around with margins and padding, as you would normally with ordered and unordered lists.

To move the text further to the right away from the image, set the padding-left of the <li> (e.g. .mylist li { padding-left: 20px; }). Change the padding of the <ul> as a whole to move the whole thing closer to the left or further away. Have a go!

Create keyboard shortcut to export as PDF in Safari on OS XCreate keyboard shortcut to export as PDF in Safari on OS X

Posted May 6th, 2014 in Applications and OSX

I frequently export a whole bunch of web pages as PDF documents in Safari on OS X and found it frustrating to have to use the mouse to click click click to do it, and worked out how to create a keyboard shortcut to do it much faster and simply.

tl;dr

System Prefences -> Keyboard -> Shortcuts -> App Shortcuts -> + -> Change application to "Safari" -> Enter "Export as PDF..." as the menu title (the dots are important) -> enter a keyboard shortcut and click "Add".

The longer answer with screenshots

When you print from any Mac app you'll see the print dialog with the "PDF" drop down box, as shown in the map below. Before I even realised there was an "Export as PDF" menu option in Safari, I was also going into the print dialog to export a heap of reports as PDFs.

exporting as a pdf from the print dialog in safari

There's a much easier way to do it; from the File menu in Safari, there's an "Export as PDF" option. Select that and you'll be prompted where to save the exported file.

export as pdf option in safari

That's still a little annoying when you need to export a lot of pages (like I need to every Monday morning, usually a dozen or so) because there's still a couple of clicks before getting the dialog to save the file.

So let's add a keyboard shortcut to Safari to make it much faster to export as PDF. Go into the System Preferences and click the "Keyboard" option:

selecting keyboard from system preferences

The select Shortcuts, App Shortcuts and click the + button as highlighted with the red boxes in the screenshot below:

add app shortcut

The next screenshot shows the dialog where you add the shortcut. The application drop down box will show "All Applications" by default, so open it up and select Safari. Then enter "Export as PDF..." in the menu title. This needs to match the title in the app exactly, so needs to include the ... at the end. Then enter a keyboard shortcut that isn't already used. Cmd+E worked for me, E for Export. Now click the "Add" button.

enter shortcut settings

Now we're back at the list of application shortcuts. There doesn't appear to be a way to change the application, but you can change both the title (if you cocked it up) or the shortcut keyboard combination (if you need to change it to something else) by clicking once or twice on the title or shortcut.

new shortcut is saved

Now looking back at the Safari File menu, we can see the "Export as PDF" option has the Cmd+E shortcut next to it. If it's not showing, then you've probably typed the title in incorrectly.

export as pdf with shortcut key

There may be other ways to do this, but I've never found it in Safari's preferences, so as far as I am aware it has to be done through the system preferences.