$schema * @param array $variables * @return array{total: ?float, breakdown: array, error: ?string} */ public static function calculate( array $schema, array $variables ): array { $calculations = is_array( $schema['calculations'] ?? null ) ? array_values( array_filter( $schema['calculations'], 'is_array' ) ) : []; try { $values = CalculationGraph::evaluateAll( $calculations, $variables ); } catch ( ExpressionException $error ) { return [ 'total' => null, 'breakdown' => [], 'error' => $error->getCodeSlug() ]; } catch ( \Throwable $error ) { return [ 'total' => null, 'breakdown' => [], 'error' => 'calculation_failed' ]; } $breakdown = []; foreach ( $calculations as $calculation ) { if ( true !== ( $calculation['visible'] ?? null ) ) { continue; } $key = (string) ( $calculation['key'] ?? '' ); $breakdown[] = [ 'key' => $key, 'label' => is_string( $calculation['label'] ?? null ) ? $calculation['label'] : '', 'amount' => self::number( $values[ $key ] ?? null ), ]; } return [ 'total' => self::number( $values[ self::TOTAL_KEY ] ?? null ), 'breakdown' => $breakdown, 'error' => null, ]; } /** * @param mixed $value */ private static function number( $value ): ?float { return ( is_int( $value ) || is_float( $value ) ) ? (float) $value : null; } }