      1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116:

    <?php

    namespace WPGMZA;

    /**
     * This class represents a latitude and longitude coordinate pair, provides type consistency for latitude and longitude, and some utility functions
     */
    class LatLng
    {
        /**
         * @const A regular expression to match a coordinate pair from a string. The latitdue and longitude will be in matches 1 and 3 respectively.
         */
        const REGEXP = '/^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/';

        private $_lat;
        private $_lng;

        /**
         * Constructor
         * @param mixed $arg Can be a string matching LatLng::REGEXP, or an object/array with lat and lng set.
         * @param mixed $lng Where $arg is a number, it will be treated as a float, you should supply a number to $lng too.
         */
        public function __construct($arg=null, $lng=null)
        {
            $this->_lat = 0;
            $this->_lng = 0;

            if($arg === null && $lng === null)
                return;

            if($lng === null)
            {
                if(is_string($arg))
                {
                    $str = trim($arg, '() ');

                    if(!preg_match(LatLng::REGEXP, $str, $m))
                        throw new \Exception('Invalid LatLng string');

                    $arg = array(
                        'lat' => $m[1],
                        'lng' => $m[3]
                    );
                }

                if(!is_object($arg) && !is_array($arg))
                    throw new \Exception('Argument must be a LatLng literal');

                $obj = (object)$arg;

                if(!property_exists($obj, 'lat') || !property_exists($obj, 'lng'))
                    throw new \Exception('Argument must define lat and lng');

                $this->lat = $obj->lat;
                $this->lng = $obj->lng;
            }
            else
            {
                $this->lat = $arg;
                $this->lng = $lng;
            }
        }

        /**
         * Gets the specified property.
         */
        public function __get($name)
        {
            switch($name)
            {
                case "lat":
                    return $this->_lat;
                    break;

                case "lng":
                    return $this->_lng;
                    break;

                default:
                    return $this->{$name};
                    break;
            }
        }

        /**
         * Sets the specified property.
         * @throws \Exception The value is not numeric (int, float or a numeric string)
         */
        public function __set($name, $value)
        {
            switch($name)
            {
                case "lat":
                case "lng":
                    if(!is_numeric($value))
                        throw new \Exception('Value must be numeric');

                    $this->{"_$name"} = floatval($value);

                    break;

                default:
                    $this->{$name} = $value;
                    break;
            }
        }

        /**
         * Returns this coordinate pair as a comma separated string.
         */
        public function __toString()
        {
            return "{$this->_lat}, {$this->_lng}";
        }
    }