cuonghuynh
2/24/2017 - 6:19 AM

Distance.php

<?php

/**
 * Class Distance
 */
class Distance
{
    /**
     * @var array
     */
    public static $units = [
        'meters' => [
            'suffix' => 'm',
            'unit' => 1.00
        ],
        'kilometers' => [
            'suffix' => 'km',
            'unit' => 0.001
        ],
        'miles' => [    //statute miles
            'suffix' => 'mi.',
            'unit' => 0.000621371
        ],
        'nautical_miles' => [
            'suffix' => 'nmi',
            'unit' => 0.000539956
        ]
    ];

    /**
     * @var float
     */
    private $value;
    /**
     * @var string
     */
    private $unit;

    /**
     * Distance constructor.
     *
     * @param float $value
     * @param string $unit
     *
     * @throws \InvalidArgumentException
     */
    public function __construct(float $value, $unit = 'meters')
    {
        $this->value = $value;
        $this->setUnit($unit);
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * @param mixed $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * @return string
     */
    public function getUnit()
    {
        return $this->unit;
    }

    /**
     * @param string $unit
     *
     * @throws \InvalidArgumentException
     */
    public function setUnit(string $unit)
    {
        if (!array_key_exists($unit, self::$units)) {
            throw new \InvalidArgumentException('The given Unit is not supported.');
        }
        $this->unit = $unit;
    }

    /**
     * @return bool
     */
    public function isMeters()
    {
        return $this->unit === 'meters';
    }

    /**
     * @return bool
     */
    public function isKilometers()
    {
        return $this->unit === 'kilometers';
    }

    /**
     * @return bool
     */
    public function isMiles()
    {
        return $this->unit === 'miles';
    }

    /**
     * @return bool
     */
    public function isNauticalMiles()
    {
        return $this->unit === 'nautical_miles';
    }

    /**
     * @param $distance
     *
     * @return static
     * @throws \InvalidArgumentException
     */
    public static function fromMeters($distance)
    {
        return new static($distance, 'meters');
    }

    /**
     * @param $distance
     *
     * @return static
     * @throws \InvalidArgumentException
     */
    public static function fromKilometers($distance)
    {
        return new static($distance, 'kilometers');
    }

    /**
     * @param $distance
     *
     * @return static
     * @throws \InvalidArgumentException
     */
    public static function fromMiles($distance)
    {
        return new static($distance, 'miles');
    }

    /**
     * @param $distance
     *
     * @return static
     * @throws \InvalidArgumentException
     */
    public static function fromNauticalMiles($distance)
    {
        return new static($distance, 'nautical_miles');
    }

    /**
     * @param string $unit
     *
     * @return float
     * @throws \InvalidArgumentException
     */
    public function getMeasurement(string $unit = '')
    {
        $unit = $this->unit ?: $unit;
        if (!array_key_exists($unit, self::$units)) {
            throw new \InvalidArgumentException('The measurement '.$unit.' not found.');
        }
        return self::$units[$unit]['unit'];
    }

    /**
     * @return string
     */
    public function getSuffix()
    {
        return self::$units[$this->unit]['suffix'];
    }

    /**
     * @param string $unit
     *
     * @return float
     * @throws \InvalidArgumentException
     */
    public function asUnit(string $unit)
    {
        $from = $this->getMeasurement($this->unit);
        $to = $this->getMeasurement($unit);

        return $this->value * $to * (1/$from);
    }

    /**
     * @param string $unit
     *
     * @return static
     * @throws \InvalidArgumentException
     */
    public function convertTo(string $unit)
    {
        $distance = $this->asUnit($unit);

        return new static($distance, $unit);
    }

    /**
     * @return Distance
     * @throws \InvalidArgumentException
     */
    public function toMeters()
    {
        return $this->convertTo('meters');
    }

    /**
     * @return Distance
     * @throws \InvalidArgumentException
     */
    public function toKilometers()
    {
        return $this->convertTo('kilometers');
    }

    /**
     * @return Distance
     * @throws \InvalidArgumentException
     */
    public function toMiles()
    {
        return $this->convertTo('miles');
    }

    /**
     * @return Distance
     * @throws \InvalidArgumentException
     */
    public function toNauticalMiles()
    {
        return $this->convertTo('nautical_miles');
    }

    /**
     * @return string
     */
    public function toString()
    {
        return (string)number_format($this->value, 2, ',', '.');
    }

    /**
     * @return string
     */
    public function toStringWithSuffix()
    {
        return sprintf('%s %s', $this->toString(), $this->getSuffix());
    }

    /**
     * @inheritDoc
     */
    public function __toString()
    {
        return $this->toString();
    }
}