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:
| Property | Type | Meaning |
|---|---|---|
originalValue | float | The value passed to the constructor. |
originalUnit | UnitOfMeasurement | The unit the value was created in. |
nativeValue | float | The value normalized to the native unit. |
nativeUnit | UnitOfMeasurement | The 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 (m², m³) 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().