parent
3fbb9bc3c8
commit
254cc88a42
@ -1,31 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types = 1); |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Robert Strutts |
|
||||||
* @copyright (c) 2026, Robert Strutts |
|
||||||
* @license MIT |
|
||||||
*/ |
|
||||||
namespace IOcornerstone\Framework; |
|
||||||
|
|
||||||
class Dollars |
|
||||||
{ |
|
||||||
private function moneyAsFloat(Number $money): float { |
|
||||||
// Remove any existing currency symbols and thousands separators |
|
||||||
$t = preg_replace('/[^\d.-]/', '', $money); |
|
||||||
return (float)$t; |
|
||||||
} |
|
||||||
|
|
||||||
public function formatAsUSD(Number $money): string { |
|
||||||
return '$' . number_format($this->moneyAsFloat($money), 2, '.', ','); |
|
||||||
} |
|
||||||
|
|
||||||
public function intl_USD(Number $money): string|false { |
|
||||||
if (class_exists('NumberFormatter')) { |
|
||||||
$formatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); |
|
||||||
return $formatter->formatCurrency($this->moneyAsFloat($money), 'USD'); |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,31 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework\Http\App; |
||||||
|
|
||||||
|
class MiddlewareHandler |
||||||
|
{ |
||||||
|
public function run(mixed $request, array $middleware, callable $destination): mixed |
||||||
|
{ |
||||||
|
$pipeline = array_reduce( |
||||||
|
array_reverse($middleware), |
||||||
|
function ($next, string $middlewareClass) { |
||||||
|
return function ($request) use ($middlewareClass, $next) { |
||||||
|
$instance = new $middlewareClass(); |
||||||
|
|
||||||
|
return $instance->handle($request, $next); |
||||||
|
}; |
||||||
|
}, |
||||||
|
$destination |
||||||
|
); |
||||||
|
|
||||||
|
return $pipeline($request); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework\Http\App; |
||||||
|
|
||||||
|
/** |
||||||
|
* Description of MiddlewareInterface |
||||||
|
* |
||||||
|
* @author Robert Strutts |
||||||
|
*/ |
||||||
|
interface MiddlewareInterface |
||||||
|
{ |
||||||
|
public function handle(mixed $request, callable $next): mixed; |
||||||
|
} |
||||||
@ -0,0 +1,236 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework; |
||||||
|
|
||||||
|
use NumberFormatter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Description of MoneyFormatter |
||||||
|
* Requries php8.5-intl |
||||||
|
* @TODO install intl |
||||||
|
* sudo apt install php8.5-intl |
||||||
|
* @author Robert Strutts |
||||||
|
*/ |
||||||
|
class MoneyFormatter |
||||||
|
{ |
||||||
|
private const CURRENCIES = [ |
||||||
|
'USD' => ['symbol' => '$', 'locale' => 'en_US', 'name' => 'US Dollar'], |
||||||
|
'CAD' => ['symbol' => '$', 'locale' => 'en_CA', 'name' => 'Canadian Dollar'], |
||||||
|
'EUR' => ['symbol' => '€', 'locale' => 'de_DE', 'name' => 'Euro'], |
||||||
|
'GBP' => ['symbol' => '£', 'locale' => 'en_GB', 'name' => 'British Pound'], |
||||||
|
'JPY' => ['symbol' => '¥', 'locale' => 'ja_JP', 'name' => 'Japanese Yen'], |
||||||
|
'CNY' => ['symbol' => '¥', 'locale' => 'zh_CN', 'name' => 'Chinese Yuan'], |
||||||
|
'AUD' => ['symbol' => '$', 'locale' => 'en_AU', 'name' => 'Australian Dollar'], |
||||||
|
'NZD' => ['symbol' => '$', 'locale' => 'en_NZ', 'name' => 'New Zealand Dollar'], |
||||||
|
'CHF' => ['symbol' => 'CHF','locale' => 'de_CH', 'name' => 'Swiss Franc'], |
||||||
|
'SEK' => ['symbol' => 'kr', 'locale' => 'sv_SE', 'name' => 'Swedish Krona'], |
||||||
|
'NOK' => ['symbol' => 'kr', 'locale' => 'nb_NO', 'name' => 'Norwegian Krone'], |
||||||
|
'DKK' => ['symbol' => 'kr', 'locale' => 'da_DK', 'name' => 'Danish Krone'], |
||||||
|
'PLN' => ['symbol' => 'zł', 'locale' => 'pl_PL', 'name' => 'Polish Zloty'], |
||||||
|
'CZK' => ['symbol' => 'Kč', 'locale' => 'cs_CZ', 'name' => 'Czech Koruna'], |
||||||
|
'HUF' => ['symbol' => 'Ft', 'locale' => 'hu_HU', 'name' => 'Hungarian Forint'], |
||||||
|
'RON' => ['symbol' => 'lei','locale' => 'ro_RO', 'name' => 'Romanian Leu'], |
||||||
|
'BGN' => ['symbol' => 'лв', 'locale' => 'bg_BG', 'name' => 'Bulgarian Lev'], |
||||||
|
'TRY' => ['symbol' => '₺', 'locale' => 'tr_TR', 'name' => 'Turkish Lira'], |
||||||
|
'RUB' => ['symbol' => '₽', 'locale' => 'ru_RU', 'name' => 'Russian Ruble'], |
||||||
|
'UAH' => ['symbol' => '₴', 'locale' => 'uk_UA', 'name' => 'Ukrainian Hryvnia'], |
||||||
|
'INR' => ['symbol' => '₹', 'locale' => 'hi_IN', 'name' => 'Indian Rupee'], |
||||||
|
'PKR' => ['symbol' => '₨', 'locale' => 'ur_PK', 'name' => 'Pakistani Rupee'], |
||||||
|
'BDT' => ['symbol' => '৳', 'locale' => 'bn_BD', 'name' => 'Bangladeshi Taka'], |
||||||
|
'LKR' => ['symbol' => 'Rs', 'locale' => 'si_LK', 'name' => 'Sri Lankan Rupee'], |
||||||
|
'NPR' => ['symbol' => '₨', 'locale' => 'ne_NP', 'name' => 'Nepalese Rupee'], |
||||||
|
'THB' => ['symbol' => '฿', 'locale' => 'th_TH', 'name' => 'Thai Baht'], |
||||||
|
'VND' => ['symbol' => '₫', 'locale' => 'vi_VN', 'name' => 'Vietnamese Dong'], |
||||||
|
'MYR' => ['symbol' => 'RM', 'locale' => 'ms_MY', 'name' => 'Malaysian Ringgit'], |
||||||
|
'SGD' => ['symbol' => '$', 'locale' => 'en_SG', 'name' => 'Singapore Dollar'], |
||||||
|
'IDR' => ['symbol' => 'Rp', 'locale' => 'id_ID', 'name' => 'Indonesian Rupiah'], |
||||||
|
'PHP' => ['symbol' => '₱', 'locale' => 'en_PH', 'name' => 'Philippine Peso'], |
||||||
|
'KRW' => ['symbol' => '₩', 'locale' => 'ko_KR', 'name' => 'South Korean Won'], |
||||||
|
'HKD' => ['symbol' => '$', 'locale' => 'zh_HK', 'name' => 'Hong Kong Dollar'], |
||||||
|
'TWD' => ['symbol' => 'NT$','locale' => 'zh_TW', 'name' => 'New Taiwan Dollar'], |
||||||
|
'AED' => ['symbol' => 'د.إ','locale' => 'ar_AE', 'name' => 'UAE Dirham'], |
||||||
|
'SAR' => ['symbol' => '﷼', 'locale' => 'ar_SA', 'name' => 'Saudi Riyal'], |
||||||
|
'QAR' => ['symbol' => '﷼', 'locale' => 'ar_QA', 'name' => 'Qatari Riyal'], |
||||||
|
'KWD' => ['symbol' => 'د.ك','locale' => 'ar_KW', 'name' => 'Kuwaiti Dinar'], |
||||||
|
'BHD' => ['symbol' => '.د.ب','locale' => 'ar_BH', 'name' => 'Bahraini Dinar'], |
||||||
|
'OMR' => ['symbol' => '﷼', 'locale' => 'ar_OM', 'name' => 'Omani Rial'], |
||||||
|
'ILS' => ['symbol' => '₪', 'locale' => 'he_IL', 'name' => 'Israeli New Shekel'], |
||||||
|
'ZAR' => ['symbol' => 'R', 'locale' => 'en_ZA', 'name' => 'South African Rand'], |
||||||
|
'NGN' => ['symbol' => '₦', 'locale' => 'en_NG', 'name' => 'Nigerian Naira'], |
||||||
|
'EGP' => ['symbol' => 'E£', 'locale' => 'ar_EG', 'name' => 'Egyptian Pound'], |
||||||
|
'KES' => ['symbol' => 'KSh','locale' => 'sw_KE', 'name' => 'Kenyan Shilling'], |
||||||
|
'GHS' => ['symbol' => '₵', 'locale' => 'en_GH', 'name' => 'Ghanaian Cedi'], |
||||||
|
'BRL' => ['symbol' => 'R$', 'locale' => 'pt_BR', 'name' => 'Brazilian Real'], |
||||||
|
'ARS' => ['symbol' => '$', 'locale' => 'es_AR', 'name' => 'Argentine Peso'], |
||||||
|
'CLP' => ['symbol' => '$', 'locale' => 'es_CL', 'name' => 'Chilean Peso'], |
||||||
|
'COP' => ['symbol' => '$', 'locale' => 'es_CO', 'name' => 'Colombian Peso'], |
||||||
|
'MXN' => ['symbol' => '$', 'locale' => 'es_MX', 'name' => 'Mexican Peso'], |
||||||
|
'PEN' => ['symbol' => 'S/', 'locale' => 'es_PE', 'name' => 'Peruvian Sol'], |
||||||
|
'UYU' => ['symbol' => '$U', 'locale' => 'es_UY', 'name' => 'Uruguayan Peso'], |
||||||
|
]; |
||||||
|
|
||||||
|
/** |
||||||
|
* SQL for money: |
||||||
|
* balance BIGINT NOT NULL DEFAULT 0, |
||||||
|
* currency CHAR(3) NOT NULL DEFAULT 'USD' |
||||||
|
*/ |
||||||
|
|
||||||
|
public static function getBalance( |
||||||
|
float $input, |
||||||
|
string $currency = 'USD', |
||||||
|
string $locale = 'en_US' |
||||||
|
): string |
||||||
|
{ |
||||||
|
$locale = self::getLocaleFromCode($currency, $locale); |
||||||
|
|
||||||
|
$formatter = new NumberFormatter( |
||||||
|
$locale, |
||||||
|
NumberFormatter::CURRENCY |
||||||
|
); |
||||||
|
|
||||||
|
$amount = $formatter->formatCurrency( |
||||||
|
abs($input), |
||||||
|
$currency |
||||||
|
); |
||||||
|
|
||||||
|
if ($input < 0.00) { |
||||||
|
return "-{$amount}"; |
||||||
|
} |
||||||
|
|
||||||
|
if ($input > 0.01) { |
||||||
|
return "+{$amount}"; |
||||||
|
} |
||||||
|
|
||||||
|
return $amount; |
||||||
|
} |
||||||
|
|
||||||
|
public static function getColorClass(float $input): string |
||||||
|
{ |
||||||
|
if ($input < 0.00) { |
||||||
|
return " money-negitive "; |
||||||
|
} else if ($input > 0.01) { |
||||||
|
return " money-positive "; |
||||||
|
} else { |
||||||
|
return " money-zero "; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* To store into the Database as whole integer |
||||||
|
* This is For Migrations ONLY!!! |
||||||
|
* Please use parseInputToMinorUnits on USER INPUT. |
||||||
|
*/ |
||||||
|
public static function amountToMinorUnits( |
||||||
|
float $amount, |
||||||
|
string $currency = 'USD', |
||||||
|
string $locale = 'en_US' |
||||||
|
): int |
||||||
|
{ |
||||||
|
$locale = self::getLocaleFromCode($currency, $locale); |
||||||
|
|
||||||
|
$formatter = new NumberFormatter( |
||||||
|
$locale, |
||||||
|
NumberFormatter::CURRENCY |
||||||
|
); |
||||||
|
|
||||||
|
$formatter->setTextAttribute( |
||||||
|
NumberFormatter::CURRENCY_CODE, |
||||||
|
$currency |
||||||
|
); |
||||||
|
|
||||||
|
$digits = $formatter->getAttribute( |
||||||
|
NumberFormatter::FRACTION_DIGITS |
||||||
|
); |
||||||
|
|
||||||
|
return (int) round( $amount * (10 ** $digits)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* To get back from the Database into Decimal Numbers for Output |
||||||
|
*/ |
||||||
|
public static function minorUnitsToAmount( |
||||||
|
int $amount, |
||||||
|
string $currency = 'USD', |
||||||
|
string $locale = 'en_US' |
||||||
|
): float |
||||||
|
{ |
||||||
|
$locale = self::getLocaleFromCode($currency, $locale); |
||||||
|
|
||||||
|
$formatter = new NumberFormatter( |
||||||
|
$locale, |
||||||
|
NumberFormatter::CURRENCY |
||||||
|
); |
||||||
|
|
||||||
|
$formatter->setTextAttribute( |
||||||
|
NumberFormatter::CURRENCY_CODE, |
||||||
|
$currency |
||||||
|
); |
||||||
|
|
||||||
|
$digits = $formatter->getAttribute( |
||||||
|
NumberFormatter::FRACTION_DIGITS |
||||||
|
); |
||||||
|
|
||||||
|
return $amount / (10 ** $digits); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parse User Input Safely, to be saved into Database |
||||||
|
*/ |
||||||
|
public static function parseInputToMinorUnits( |
||||||
|
string $input, |
||||||
|
string $currency = 'USD', |
||||||
|
string $locale = 'en_US' |
||||||
|
): int |
||||||
|
{ |
||||||
|
$locale = self::getLocaleFromCode($currency, $locale); |
||||||
|
|
||||||
|
return self::amountToMinorUnits((float) $input, $currency, $locale); |
||||||
|
} |
||||||
|
|
||||||
|
public static function convertToUsd( |
||||||
|
int $minorUnits, |
||||||
|
float $exchangeRate, |
||||||
|
string $currency, |
||||||
|
): int { |
||||||
|
return (int) round($minorUnits * $exchangeRate); |
||||||
|
} |
||||||
|
|
||||||
|
public static function getCurrencyCodes(string $unit = "USD"): string |
||||||
|
{ |
||||||
|
$ret = ""; |
||||||
|
foreach (self::CURRENCIES as $code => $currency) { |
||||||
|
$ret .= sprintf( |
||||||
|
'<option value="%s" %s>%s (%s)</option>', |
||||||
|
$code, |
||||||
|
($code === $unit) ? "selected" : "", |
||||||
|
htmlspecialchars($currency['name']), |
||||||
|
$code |
||||||
|
); |
||||||
|
} |
||||||
|
return $ret; |
||||||
|
} |
||||||
|
|
||||||
|
public static function getLocaleFromCode(string $currencyCode = "USD", string $locale = "en_US"): ?string |
||||||
|
{ |
||||||
|
if ($currencyCode === "USD" && $locale === "en_US") { |
||||||
|
return $locale; |
||||||
|
} |
||||||
|
if ($currencyCode !== "USD" && ! empty($locale) && $locale !== "en_US") { |
||||||
|
return $locale; |
||||||
|
} |
||||||
|
foreach (self::CURRENCIES as $code => $currency) { |
||||||
|
if ($code === $currencyCode) { |
||||||
|
return $currency['locale'] ?? null; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue