mistralys / column-widths-calculator
PHP column widths calculator utility.
Installs: 1 766
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 3
Forks: 0
Open Issues: 0
pkg:composer/mistralys/column-widths-calculator
Requires
- php: >=8.4
- mistralys/application-utils: >=3.2
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: >=2.1.39
- phpunit/phpunit: >=13.0.5
README
Small utility that can be used to convert arbitrary numeric values to column widths. Given a list of column names and values, it will intelligently convert the values to percentages, and fill empty columns with meaningful values.
Features:
- Fills empty values with meaningful values
- Converts any values to percentages, using proportional calculations
- Ensures that the total always matches 100% exactly
Installation
Using composer, simply require the package.
Via command line:
composer require mistralys/column-widths-calculator
Via composer.json:
"require" :
{
"mistralys/column-widths-calculator": "^1.0"
}
Usage
Use the factory method to instantiate the calculator:
$columns = array( 'Col1' => 20, 'Col2' => 0, 'Col3' => 40 ); $calc = Calculator::create($columns); $converted = $calc->getValues();
This will return an array with the same keys, and the missing column value filled out:
array( 'Col1' => 20, 'Col2' => 40, 'Col3' => 40 );
Fluent interface
All configuration methods return the Calculator instance, allowing calls to be chained:
$calc = Calculator::create($columns) ->setMaxTotal(200) ->setMinWidth(10) ->setFloatValues(); $converted = $calc->getValues();
Switching between integers and floats
By default, the calculator will produce integer values, even if all internal calculations are float based for precision. This can be turned off to retrieve float values:
$calc = Calculator::create($columns); $calc->setFloatValues();
In this case, no rounding is done at all - you will have to manually handle any rounding you wish to apply.
To check whether the calculator is currently operating in integer mode, use isIntegerMode():
$calc = Calculator::create($columns); $calc->isIntegerMode(); // true (default) $calc->setFloatValues(); $calc->isIntegerMode(); // false
Handling minimum widths
When leaving columns empty to be automatically filled, there may not be enough width left for the columns to fill. Consider this configuration of columns:
Col1 = 80
Col2 = 20
Col3 = 0
By default, the calculator will guarantee that every column has a minimum width of 1. As a result, the list would be adjusted like this, to allow the third column to have a width:
Col1 = 79
Col2 = 20
Col3 = 1
The surplus is subtracted from all non-empty columns, proportionally to their size (without going below the minimum width).
Setting the minimum size
The minimum size can be set like this:
$calc = Calculator::create($columns); $calc->setMinWidth(20);
This will ensure that all columns have a minimum size of 20%.
Example:
Col1 = 80
Col2 = 20
Col3 = 0
Would give the following result:
Col1 = 60
Col2 = 20
Col3 = 20
NOTE: The maximum possible value for the minimum size depends on the amount of columns. Trying to set the minimum size to 40% for 3 columns for example, will throw \InvalidArgumentException with code Calculator::ERROR_INVALID_MIN_WIDTH (61501), because 40 × 3 exceeds 100. Use getMaxMinWidth() to query the safe upper bound before calling setMinWidth().
NOTE: Calling setMinWidth() on a Calculator created with an empty column array (Calculator::create([])) throws \InvalidArgumentException with code Calculator::ERROR_EMPTY_COLUMN_ARRAY (61502). Use getMaxMinWidth() to query the maximum safe value before calling setMinWidth() — it returns 0.0 on an empty array.
Arbitrary numbering
The calculator can work with any number system, and converts the values proportionally.
Consider the following values:
Col1 = 1400
Col2 = 900
Col3 = 700
This will be converted to the following values:
Col1 = 38
Col2 = 33
Col3 = 30
= 100%
Missing values
As long as there are more than one column with values, missing values will be filled with a value based on an average of the existing values, to get realistic results.
For example, the values:
Col1 = 1400
Col2 = 900
Col3 = 0
Col4 = 0
Will be converted to this:
Col1 = 30
Col2 = 19
Col3 = 25
Col4 = 26
= 100%
Changing the target value
By default, the Calculator assumes you want to work with percentages, and has the target value locked to 100. However, it is possible to adjust this value:
$calc = Calculator::create($columns); $calc->setMaxTotal(1000);
In the example above, the column widths will be calculated to reach a total of 1000, instead of the default 100.
Edge cases
Negative values
Column values that are negative are treated identically to 0 — they are considered missing and will be filled automatically, exactly like an empty column:
$calc = Calculator::create(['A' => -10, 'B' => 50, 'C' => 0]); $result = $calc->getValues(); // 'A' receives a positive width, same as if it were 0
Empty column array
Passing an empty array to Calculator::create([]) does not throw immediately. However, several methods throw \InvalidArgumentException with code Calculator::ERROR_EMPTY_COLUMN_ARRAY (61502) when called on a calculator with no columns:
getValues()— guard fires at the top of the calculation pipeline.setMinWidth(float $width)— guard fires before the internal width-limit check, preventing aDivisionByZeroError.
// Both will throw \InvalidArgumentException (code 61502): $calc = Calculator::create([]); $calc->setMinWidth(5); // throws $calc->getValues(); // throws
getMaxMinWidth() is safe to call on an empty-array calculator — it returns 0.0 without performing any division:
$max = Calculator::create([])->getMaxMinWidth(); // 0.0, no exception
To handle this defensively:
try { $result = Calculator::create([])->getValues(); } catch (\InvalidArgumentException $e) { if ($e->getCode() === Calculator::ERROR_EMPTY_COLUMN_ARRAY) { // No columns were provided } }
Always ensure at least one column is present before calling setMinWidth() or getValues().
Converting to absolute pixel values
For convenience, the getPixelValues() method can convert the column percentages to absolute pixel values, given the maximum target width.
The following example converts the column widths to reach a width of 600px:
$cols = array( 'Col1' => 40, 'Col2' => 40, 'Col3' => 20 ); $calc = Calculator::create($columns); $pixelWidths = $calc->getPixelValues(600);
The resulting widths will be:
Col1 = 207
Col2 = 207
Col3 = 186
= 600
Development
Running the test suite
composer test
Additional convenience aliases:
| Command | Purpose |
|---|---|
composer test |
Run the full PHPUnit test suite |
composer test-suite <name> |
Run a specific PHPUnit test suite by name |
composer test-filter <pattern> |
Run tests matching a name pattern |
composer test-group <group> |
Run tests belonging to a PHPUnit group |
Static analysis
The project uses PHPStan at level 9 (maximum strictness), configured via docs/config/phpstan.neon.
Run the analyser:
composer analyze
Or directly:
vendor/bin/phpstan analyse --configuration docs/config/phpstan.neon
To save the analysis report to phpstan-result.txt locally:
composer analyze-save
Note:
analyze-savesuppresses the non-zero exit code (|| true) so that the report file is always written. This alias is not suitable for CI pipelines — usecomposer analyzethere instead.
To clear the PHPStan result cache:
composer analyze-clear
Code style
The project uses PHP-CS-Fixer for automated code style enforcement, configured via .php-cs-fixer.php. The ruleset is PSR-12 with Allman-style braces for function and OOP constructs (class, method, anonymous function declaration bodies).
Check whether any files need reformatting (dry-run, no changes written):
composer cs-check
Apply automatic fixes:
composer cs-fix
Note: PHP-CS-Fixer may emit a non-fatal version-mismatch warning when the runtime PHP version differs from the project target (8.4). This does not affect correctness; the exit code remains 0. The generated cache file (
.php-cs-fixer.cache) is excluded from version control via.gitignore.