amount. * @param int $count Number of items. * @return string */ public static function per( array $amounts, $count ) { $amounts = array_filter( $amounts ); if ( 1 !== count( $amounts ) || $count < 1 ) { return '—'; } return self::money( reset( $amounts ) / $count, (string) key( $amounts ) ); } /** * Return on ad spend: revenue divided by cost, e.g. "3.2×". Needs both in the same currency. * * @param array $revenue Currency => amount. * @param array $cost Currency => amount. * @return string */ public static function roas( array $revenue, array $cost ) { $revenue = array_filter( $revenue ); $cost = array_filter( $cost ); if ( 1 !== count( $cost ) || array_diff( array_keys( $revenue ), array_keys( $cost ) ) ) { return '—'; } $value = ( $revenue[ key( $cost ) ] ?? 0 ) / reset( $cost ); return number_format_i18n( $value, $value < 10 ? 1 : 0 ) . '×'; } /** * Share of the revenue that is profit, e.g. "32%". Needs both in one currency. * * @param array $profit Currency => amount. * @param array $revenue Currency => amount. * @return string */ public static function margin( array $profit, array $revenue ) { $revenue = array_filter( $revenue ); if ( 1 !== count( $revenue ) || array_diff( array_keys( array_filter( $profit ) ), array_keys( $revenue ) ) ) { return '—'; } $value = ( $profit[ key( $revenue ) ] ?? 0 ) / reset( $revenue ) * 100; return number_format_i18n( $value, abs( $value ) < 10 ? 1 : 0 ) . '%'; } /** * Amount left after a cost, per currency, e.g. profit after ad spend. * * @param array $amounts Currency => amount. * @param array $cost Currency => amount. * @return string */ public static function after( array $amounts, array $cost ) { $left = $amounts; foreach ( $cost as $currency => $amount ) { $left[ $currency ] = ( $left[ $currency ] ?? 0 ) - $amount; } return self::money_list( $left ); } /** * Formats an amount with its currency. * * @param float $amount Amount. * @param string $currency ISO 4217 code. * @return string */ public static function money( $amount, $currency = '' ) { $currency = '' !== $currency ? $currency : Settings::get( 'currency' ); // Whole amounts, as most deals are, read better without the cents. $whole = abs( (float) $amount - round( (float) $amount ) ) < 0.005; if ( class_exists( '\NumberFormatter' ) ) { $formatter = new \NumberFormatter( get_user_locale(), \NumberFormatter::CURRENCY ); if ( $whole ) { $formatter->setAttribute( \NumberFormatter::FRACTION_DIGITS, 0 ); } $formatted = $formatter->formatCurrency( (float) $amount, $currency ); if ( false !== $formatted ) { return $formatted; } } return number_format_i18n( (float) $amount, $whole ? 0 : 2 ) . ' ' . $currency; } /** * Amounts in several currencies, e.g. "8.390,50 € + 120,00 $". * * @param array $amounts Currency => amount. * @return string */ public static function money_list( array $amounts ) { $amounts = array_filter( $amounts ); if ( ! $amounts ) { return '—'; } $default = Settings::get( 'currency' ); uksort( $amounts, static function ( $a, $b ) use ( $default ) { $order = ( $b === $default ) <=> ( $a === $default ); return 0 !== $order ? $order : strcmp( $a, $b ); } ); $list = ''; foreach ( $amounts as $currency => $amount ) { $amount = (float) $amount; // "120,00 $ − 40,00 €" rather than "120,00 $ + -40,00 €". if ( '' === $list ) { $list = self::money( $amount, $currency ); } else { $list .= ( $amount < 0 ? ' − ' : ' + ' ) . self::money( abs( $amount ), $currency ); } } return $list; } /** * Duration such as "45 min", "3 h 20 min" or "2 days". * * @param int $seconds Seconds. * @return string */ public static function duration( $seconds ) { $seconds = max( 0, (int) $seconds ); if ( $seconds < HOUR_IN_SECONDS ) { /* translators: %s: number of minutes. */ return sprintf( __( '%s min', 'dealroot' ), number_format_i18n( max( 1, (int) round( $seconds / MINUTE_IN_SECONDS ) ) ) ); } if ( $seconds < DAY_IN_SECONDS ) { $hours = intdiv( $seconds, HOUR_IN_SECONDS ); $minutes = (int) round( ( $seconds % HOUR_IN_SECONDS ) / MINUTE_IN_SECONDS ); /* translators: 1: hours, 2: minutes. */ return $minutes ? sprintf( __( '%1$s h %2$s min', 'dealroot' ), number_format_i18n( $hours ), number_format_i18n( $minutes ) ) /* translators: %s: number of hours. */ : sprintf( __( '%s h', 'dealroot' ), number_format_i18n( $hours ) ); } $days = (int) round( $seconds / DAY_IN_SECONDS ); /* translators: %s: number of days. */ return sprintf( _n( '%s day', '%s days', $days, 'dealroot' ), number_format_i18n( $days ) ); } /** * Percentage with one decimal. * * @param int $part Part. * @param int $total Total. * @return string */ public static function percent( $part, $total ) { if ( $total < 1 ) { return '—'; } $value = $part / $total * 100; return number_format_i18n( $value, abs( $value - round( $value ) ) < 0.05 ? 0 : 1 ) . '%'; } }