kolaybi/unit-converter

A PHP 8.4+ unit conversion package compatible with UN/CEFACT Recommendation 20

Maintainers

Package info

github.com/kolaybi/unit-converter

pkg:composer/kolaybi/unit-converter

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-03-04 01:13 UTC

README

A PHP 8.4+ framework-agnostic package for converting between 2,216 units of measurement based on UN/CEFACT Recommendation 20, Recommendation 21 (package types), and the GS1 unit conversion model.

Uses arbitrary-precision math (bcmath) with the GS1 multiplier + offset formula to handle both linear conversions (mass, length, volume, etc.) and non-linear ones (temperature).

Requirements

  • PHP 8.4+
  • ext-bcmath

Installation

composer require kolaybi/unit-converter

Quick Start

use KolayBi\UnitConverter\Converter;
use KolayBi\UnitConverter\Units\Mass;
use KolayBi\UnitConverter\Units\Temperature;

// Using enum cases
$result = Converter::convert(10)->from(Mass::Kilogram)->to(Mass::Pound);
echo $result->round(2); // "22.05"

// Using UN/CEFACT codes
$result = Converter::convert(1)->from('KGM')->to('LBR');
echo $result->toFloat(); // 2.20462262...

// Using symbols
$result = Converter::convert(100)->from('°C')->to('°F');
echo $result->round(1); // "212.0"

// Using labels
$result = Converter::convert(1)->from('kilogram')->to('gram');
echo $result->value; // "1000.00000000000000000000"

// Default value is 1 — handy for conversion factors
$result = Converter::convert()->from(Mass::Kilogram)->to(Mass::Pound);
echo $result->round(6); // "2.204623"

API

Converter::convert(int|float|string $value = 1): PendingConversion

Entry point. Accepts any numeric value. Defaults to 1, useful for looking up conversion factors.

PendingConversion::from(Unit|string $unit): self

Set the source unit. Accepts an enum case, UN/CEFACT code, symbol, or label.

PendingConversion::to(Unit|string $unit): ConversionResult

Convert to the target unit and return the result.

Converter::unit(string $input): Unit

Resolve a unit by UN/CEFACT code, symbol, or label. Returns the Unit enum case.

$unit = Converter::unit('kg');
$unit->code();   // 'KGM'
$unit->symbol(); // 'kg'
$unit->label();  // 'kilogram'

PendingConversion::toAll(): array<string, string>

Convert to all other units in the same category. Returns an array keyed by UN/CEFACT code.

ConversionResult

$result->value;              // Full-precision string
$result->toFloat();          // PHP float
$result->round(2);           // "22.05"
$result->format(2, ',', '.'); // "1.234,57"
(string) $result;            // Same as ->value
$result->from;               // Source Unit enum
$result->to;                 // Target Unit enum

Unit Categories

58 categories covering 2,216 units (780 convertible + 1,436 non-convertible):

Category Units Category Units
Absorbed Dose 4 Kinematic Viscosity 7
Absorbed Dose Rate 11 Length 34
Acceleration 11 Luminous Intensity 3
Amount of Substance 4 Magnetic Flux 3
Angle 9 Magnetic Flux Density 6
Angular Impulse 3 Magnetic Vector Potential 3
Angular Velocity 4 Mass 34
Area 20 Memory Capacity 15
Capacitance 7 Molar Concentration 5
Charge 13 Molar Mass 2
Conductance 7 Molar Thermodynamic Energy 2
Counting 14 Molar Volume 4
Current 9 Package Type ⊘ 406
Data Rate 12 Packaging ⊘ 52
Density 28 Power 44
Dimensionless Concentration 24 Pressure 42
Dynamic Viscosity 19 Radioactivity 9
Effective Dose 4 Resistance 8
Effective Dose Rate 13 Signal Rate 3
Energy 34 Specific Volume 7
Energy Density 7 Speed 23
Exposure 2 Temperature 4
Flow Rate 93 Textile Density 3
Force 15 Time 18
Frequency 10 Torque 16
Illuminance 6 Trade ⊘ 978
Impulse 6 Voltage 6
Inductance 6 Volume 66
Irradiance 14 Wavenumber 4

⊘ = non-convertible (code resolution only)

Package Types (Rec 21)

406 package types from UNECE Recommendation 21 (Rev. 12) are available via the PackageType enum.

Per Rec 20 guidance, each 2-character Rec 21 code is prefixed with X to form a 3-character unit-of-measure code (reserved range X00XZZ). The native Rec 21 code is also available as an alias.

use KolayBi\UnitConverter\Converter;
use KolayBi\UnitConverter\Units\PackageType;

// Using X-prefixed code
$unit = Converter::unit('XBG');  // PackageType::Bag
$unit->code();                   // 'XBG'
$unit->rec21Code();              // 'BG'
$unit->label();                  // 'bag'

// Using label
$unit = Converter::unit('aerosol'); // PackageType::Aerosol (code: XAE)

// Using enum case directly
$unit = PackageType::DrumSteel;     // code: X1A

Resolution priority: When a Rec 21 native code conflicts with a Rec 20 code (23 shared codes like BG, DR, TU), Rec 20 takes priority. Use the X-prefixed code to explicitly target the Rec 21 entry.

Unit Resolution

Units can be resolved by:

  • Enum case: Mass::Kilogram
  • UN/CEFACT code: 'KGM' (case-sensitive)
  • Symbol: 'kg' (case-insensitive)
  • Label: 'kilogram' (case-insensitive)
// All equivalent
Converter::convert(1)->from(Mass::Kilogram)->to(Mass::Pound);
Converter::convert(1)->from('KGM')->to('LBR');
Converter::convert(1)->from('kg')->to('lb');
Converter::convert(1)->from('kilogram')->to('pound');

Error Handling

use KolayBi\UnitConverter\Exceptions\IncompatibleUnitsException;
use KolayBi\UnitConverter\Exceptions\NonConvertibleUnitException;
use KolayBi\UnitConverter\Exceptions\UnitNotFoundException;

// Incompatible categories
Converter::convert(1)->from(Mass::Kilogram)->to(Temperature::Celsius);
// throws IncompatibleUnitsException

// Non-convertible units (packaging)
Converter::convert(1)->from(Packaging::Box)->to(Packaging::Bag);
// throws NonConvertibleUnitException

// Unknown unit string
Converter::convert(1)->from('not_a_real_unit')->to('abc');
// throws UnitNotFoundException

How It Works

All convertible units use the GS1 multiplier + offset model relative to a base unit per category:

base_value = input_value × multiplier + offset
output_value = (base_value - target_offset) ÷ target_multiplier

For most categories (mass, length, volume, etc.), the offset is 0, making it a simple ratio. Temperature uses non-zero offsets to handle Celsius (offset 273.15) and Fahrenheit (multiplier 5/9, offset 255.37).

All math uses bcmath with a scale of 20 decimal places.

Code Generation

The convertible unit enums are generated from the GS1 datatable:

php bin/generate-enums.php [path-to-json]

Development

composer install
vendor/bin/phpunit           # Run tests
vendor/bin/phpstan analyse   # Static analysis (level 9)
vendor/bin/pint              # Code style

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

License

Please see License File for more information.