elliotwms/Abacus

View on GitHub
src/Abacus.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Abacus;

/**
 * Class Abacus
 * @package Abacus
 */
class Abacus
{

    /**
     * Create a new Abacus object
     *
     * @param float $value
     * @param Currency|string|null $currency
     */
    public function __construct($value = null, $currency = "USD")
    {
        // Assign the value
        $this->value = $value;

        // Assign the currency
        if (is_a($currency, "Abacus\\Currency")) {
            // Existing (or custom) Currency object
            $this->currency = $currency;
        } else {
            // New Currency object
            $this->currency = new Currency($currency);
        }

    }

    /**
     * Format Abacus object
     *
     * Add the currency symbol to the front and output formatted with decimal
     * and thousands markers
     *
     * @return string
     */
    public function format()
    {
        return $this->currency->symbol . number_format($this->value, $this->currency->decimal_digits, $this->currency->decimal_separator, $this->currency->thousands_separator);
    }

    /**
     * Typecast to string
     *
     * Should return the value in its most basic form for humans to do with
     * as they please
     *
     * @return string
     */
    public function __toString()
    {
        return (string)$this->value;
    }

    /**
     * Subtract from a currency
     *
     * Subtract an Abacus object or a float from an Abacus object. The
     * exact opposite of the ->add() function, but with a cheeky
     * reversed sign
     *
     * @param Abacus|float $value
     * @param string|null $currency
     * @return Abacus
     */
    public function sub($value, $currency = null)
    {
        // Cheeky cheeky
        if (is_a($value, "Abacus\\Abacus")) {
            return $this->add(-$value->value, $currency);
        }
        return $this->add(-$value, $currency);
    }

    /**
     * Add two currencies together.
     *
     * Either add a float to an Abacus object, or add another Abacus object
     * of any currency to the current abacus object. Either way, this results
     * in an Abacus object of the original currency
     *
     * @param Abacus|float $value Monetary value of the number added
     * @param Currency|string|null $currency Currency of the number added
     * @return $this
     */
    public function add($value, $currency = null)
    {
        if (is_a($value, "Abacus\\Abacus")) {
            // If adding an Abacus object
            // Convert the object to the current object's currency and
            // add it to the current object

            $result = $this->value + $value->toCurrency($this->currency)->value;

            // Return a new Abacus object
            return new Abacus($result, $this->currency);
        } else {
            // If we're dealing with a different currency
            if (isset($currency)) {
                $value = new Abacus($value, $currency);
            } else {
                $value = new Abacus($value, $this->currency);
            }
            // Call Add properly with some recursive magic
            return $this->add($value);
        }
    }

    /**
     * Convert to a currency
     *
     * Convert the Abacus object from one currency to another.
     *
     * @param Currency|string $currency
     *
     * @return Abacus $this
     */
    public function toCurrency($currency)
    {
        // If we're not using a custom Currency model
        if (!is_a($currency, "Abacus\\Currency")) {
            // Make a new Currency model of the target
            $currency = new Currency($currency);
        }

        // New currency = Current / rate * new rate
        $value = $this->value / $this->currency->rate * $currency->rate;

        // Return the new Abacus object
        return new Abacus($value, $currency);
    }

    /**
     * To Cents
     *
     * Produce a cent value of the result for handing off to
     * payment processors and integer nerds
     *
     * @return int
     */
    public function toCents()
    {
        return round($this->value * 100, 0, PHP_ROUND_HALF_DOWN);
    }

}