Skip to main content

Architecture

The library is built from three components: an abstract base class, a unit value object, and a trait for SI prefix support.

PhysicalQuantity

PhysicalQuantity (src/PhysicalQuantity.php) is the abstract base class that every quantity type extends — Length, Mass, Temperature, Area, Volume, Force, Energy, Power, Pressure, Time, Velocity, Acceleration, Current, Voltage, Resistance, and the rest.

It holds four readonly properties:

PropertyTypeMeaning
originalValuefloatThe value passed to the constructor.
originalUnitUnitOfMeasurementThe unit the value was created in.
nativeValuefloatThe value normalized to the native unit.
nativeUnitUnitOfMeasurementThe type's native unit (factor 1.0).

When an instance is constructed, the base class calls the abstract initialise() method that each subclass implements. initialise() registers the units available for that quantity. The first unit registered becomes the native unit. The constructor then converts the supplied value to the native unit and stores it as nativeValue.

All conversions (toUnit()), comparisons (compareTo() and friends), and arithmetic operate on nativeValue, so the unit a value was created in does not change the result.

Instances are immutable. Operations such as add() or multiply() return new instances rather than modifying the receiver.

UnitOfMeasurement

UnitOfMeasurement (src/UnitOfMeasurement.php) describes a single unit. It stores a name, a conversion factor relative to the native unit, and a list of aliases.

The conversion factor and aliases are exposed through property hooks as read-only virtual properties, so they are accessed as $unit->conversionFactor and $unit->aliases rather than through getter methods.

See Unit of measurement for details.

HasSIUnits

HasSIUnits (src/HasSIUnits.php) is a trait that adds metric prefix conversion. A quantity uses the trait and overrides getSIBaseUnit() to declare its base unit symbol (for example m, g, s, N). The trait can then convert between any prefixed form of that base unit (km, cm, mm, …) without each prefix being registered as a separate UnitOfMeasurement.

See SI units for details, including how power notation (, ) is handled.

Conversion lookup order

When converting a value, the base class first tries to find a registered unit whose name or alias matches. If no registered unit matches and the quantity uses HasSIUnits, it falls back to SI prefix resolution. If neither succeeds, an InvalidArgumentException is thrown.

This ordering applies both when constructing an instance (converting the input to the native unit) and when calling toUnit().