from http://www.phpclasses.org/package/7147-PHP-Perform-calculations-with-geographic-coordinates.html

<?php
/**
 * Copyright (c) 2011 by Menno Bieringa - http://www.mennobieringa.nl
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the project's author nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
class GeoTools {
    const EARTH_RADIUS = 6378100;// radius of earth in meters
 
    public static function distanceBetweenCoords($lat1, $lon1, $lat2, $lon2) {
        $sinLatD = sin(deg2rad($lat1 - $lat2)/2);
        $sinLonD = sin(deg2rad($lon1 - $lon2)/2);
        $cosLat1 = cos(deg2rad($lat1));
        $cosLat2 = cos(deg2rad($lat2));
        $a = pow($sinLatD,2)+$cosLat1*$cosLat2*pow($sinLonD,2);
        if ($a<0) $a = -1*$a;
        $c = 2*atan2(sqrt($a), sqrt(1-$a));
        return self::EARTH_RADIUS*$c;
    }
 
    public static function bearingBetweenCoords($lat1, $lon1, $lat2, $lon2) {
        $lat1 = deg2rad($lat1);
        $lat2 = deg2rad($lat2);
        $dLon = deg2rad($lon2-$lon1);
 
        $dPhi = log(tan($lat2/2+pi()/4)/tan($lat1/2+pi()/4));
        if (abs($dLon) > pi()) $dLon = $dLon>0 ? -(2*pi()-$dLon) : (2*pi()+$dLon);
        $brng = atan2($dLon, $dPhi);
        return (rad2deg($brng)+360) % 360;
    }
 
    public static function mirrorBearing($iBearing, $sAxis='y') {
        if (strtolower($sAxis) == 'y') {
            if ($iBearing>180) {
                $iBearing = ($iBearing - 180) * -1 + 180;
            } else if ($iBearing<180) {
                $iBearing = 360 - $iBearing;
            }        
        } else {
            if ($iBearing<=180) {
                $iBearing = 180-$iBearing;
            } else if ($iBearing>180) {
                $iBearing = (360 - $iBearing) + 180;
            }
        }
        return $iBearing;
    }
 
    public static function calcLatLong($lat, $long, $distance, $bearing) {
        // Funny hack for somehow this calculation always came out mirrored over the latitude.
        $bearing = self::mirrorBearing($bearing);
 
        $radian = 180/pi();
        $b = $bearing / $radian;
        $long = $long / $radian;
        $lat = $lat / $radian;
        $f = 1/298.257;
        $e = 0.08181922;
 
        $r = self::EARTH_RADIUS * (1 - $e * $e) / pow( (1 - $e*$e * pow(sin($lat),2)), 1.5);    
        $psi = $distance/$r;
        $phi = pi()/2 - $lat;
        $arccos = cos($psi) * cos($phi) + sin($psi) * sin($phi) * cos($b);
        $latA = (pi()/2 - acos($arccos)) * $radian;
 
        $arcsin = sin($b) * sin($psi) / sin($phi);
        $longA = ($long - asin($arcsin)) * $radian;
        return array('lon' => $longA, 'lat' => $latA);
    }
}