Adding new quantities
A new quantity type is a class that extends PhysicalQuantity and implements
initialise() to register its units.
Basic quantity
<?php
declare(strict_types=1);
namespace Renfordt\UnitLib;
class Length extends PhysicalQuantity
{
use HasSIUnits;
protected function initialise(): void
{
// The first unit registered becomes the native unit (factor 1.0).
$meter = new UnitOfMeasurement('m', 1);
$meter->addAlias('meter');
$meter->addAlias('meters');
$this->addUnit($meter);
// Other units give their conversion factor to the native unit.
$foot = new UnitOfMeasurement('ft', 0.3048); // 1 ft = 0.3048 m
$foot->addAlias('foot');
$foot->addAlias('feet');
$this->addUnit($foot);
}
}
Key rules:
- The first unit added in
initialise()is the native unit and must have a conversion factor of1.0. - A unit's conversion factor expresses how many native units it equals.
- Add aliases for common spellings and abbreviations with
addAlias().
SI prefix support
To accept metric prefixes, use the HasSIUnits trait and override getSIBaseUnit() to
return the base unit symbol. The default base unit is m, so override it whenever the
native symbol differs:
class Mass extends PhysicalQuantity
{
use HasSIUnits;
protected function getSIBaseUnit(): string
{
return 'g';
}
protected function initialise(): void
{
$gram = new UnitOfMeasurement('g', 1);
$this->addUnit($gram);
// ...
}
}
With the trait in place, prefixed units such as kg, mg, and μg resolve
automatically without being registered. See SI units.
Offset-based conversions
If the quantity's scales involve an offset (not just a ratio), override
convertToNativeUnit() and toUnit(). The constructor calls convertToNativeUnit()
when it is defined, and toUnit() converts back from the native unit. Temperature is
the reference implementation:
protected function convertToNativeUnit(float $value, string $unitName): float
{
return match ($unitName) {
'K', 'kelvin', 'Kelvin' => $value,
'°C', 'C', 'celsius', 'Celsius' => $value + 273.15,
// ...
};
}
See Temperature for the full pattern.
Derived quantities and factory methods
If the new quantity is the product or quotient of existing ones, add it to the relevant
match arms in PhysicalQuantity::multiply() or divide() so those operations return
it. A static factory method is a convenient wrapper around the operation:
public static function fromLengthAndTime(Length $length, Time $time): self
{
/** @var self */
return $length->divide($time);
}
Tests
Add a test class with the #[CoversClass(ClassName::class)] attribute. The PHPUnit
configuration is strict and fails on warnings, risky tests, and deprecations, so every
test class must declare the class it covers.
To make the type available to PhysicalQuantity::parse() auto-detection, add it to the
$quantityClasses list in PhysicalQuantity::parse().
Checklist
- Extend
PhysicalQuantity. - Implement
initialise(); register the native unit first with factor1.0. - Use
HasSIUnitsand overridegetSIBaseUnit()if metric prefixes apply. - Override
convertToNativeUnit()/toUnit()for offset-based scales. - Add aliases for common unit names.
- Wire up derived-unit operations and factory methods if applicable.
- Add a test class with
#[CoversClass]. - Register the class in
parse()for auto-detection.