Strictus
Strict Typing for local variables in PHP
Strictus
Strict Typing for local variables in PHP
#Introduction
Strictus brings strict typing for local variables into PHP.
With Strictus, you can control the types of local variables using different patterns.
💣 The problem:
PHP has no support for strongly typed Local Variables.
Here is an illustrative example of a basic mistake:
<?php //Rule: Active discount of 10% or 25% for orders from $50 $total = 82.50; $discount = 0.10; //float if ($total >= 50) { $discount = '25%'; //replacing a float value with string value 🤦🏻♂️ } $total = $total - ($total * $discount); //💥 Error: A float cannot be multiplied by a string
In the code above, nothing prevents overriding the float value of $discount a string value, causing a bug.
👍 The solution:
Let's rewrite the previous example using Strictus and strongly typed variables:
<?php //Rule: Active discount of 10% or 25% for orders from $50 use Strictus\Strictus; $total = Strictus::float(82.50); $discount = Strictus::float(0.10); if ($total() >= 50) { $discount(0.25); //updates the $discount value } $total($total() - ($total() * $discount())); echo $total(); //61.875
In the code above, the variable $discount is an instance of StrictusFloat::class and it only accepts float values.
An Exception StrictusTypeException is thrown when we try to assign anything that is not of type float, like a string for example.
See this example:
<?php use Strictus\Strictus; $discount = Strictus::float(0.10); $discount('25%'); //A StrictusTypeException stops the code execution
#Installation
You can install the package via composer:
composer require strictus/strictus
Requires: PHP 8.1+
#Usage
There are a few different patterns you can use to work Strictus.
#Creating Your Variables
To create a variable, simply call Strictus::*method*(), replacing the *method* with the type you want to enforce.
For example:
<?php use Strictus\Strictus; //creates a string $name = Strictus::string('Wendell'); //creates a nullable string $comment = Strictus::nullableString(null); //creates an int $score = Strictus::int(100); //creates a boolean $isActive = Strictus::boolean(true); //creates an array $authors = Strictus::array(['Wendell', 'Christopher']); //creates an object $person = Strictus::object((object) ['name' => 'Wendell', 'country' => 'BR']); //instantiates a class $calculator = Strictus::instance(CalculatorClass::class, new CalculatorClass()); //instantiates an enum $role = Strictus::enum(Role::class, Role::CONTRIBUTOR); class CalculatorClass { //... } enum Role { case CONTRIBUTOR; }
💡 Check out all the available variable methods.
#Getting Variable Value
To retrieve the variable value, just call it like a function:
echo $name(); //Wendell echo $score() - 10; //90 if ($isActive()) { //do your logic here } echo implode($authors(), ';'); //Wendell;Christopher
Alternatively, you can use the $variable like a Value Object:
$name = Strictus::string('Christopher'); //creates a string echo $name->value; //Christopher
#Update Variable Value
To update the variable value, call it like a function passing the new value as the argument:
$score = Strictus::int(100); $score($score() - 20); //updates $score echo $score(); //80
Alternatively, you can use the $variable like a Value Object:
$score = Strictus::int(100); $score->value = 0; echo $score(); //0
#Variable methods
You can use the following methods to create single type variables:
| Type | Nullable | Method |
|---|---|---|
| String | No | Strictus::string($value) |
| String | Yes | Strictus::string($value, true) |
| String | Yes | Strictus::nullableString($value) |
| Integer | No | Strictus::int($value) |
| Integer | Yes | Strictus::int($value, true) |
| Integer | Yes | Strictus::nullableInt($value) |
| Float | No | Strictus::float($value) |
| Float | Yes | Strictus::float($value, true) |
| Float | Yes | Strictus::nullableFloat($value, true) |
| Boolean | No | Strictus::boolean($value) |
| Boolean | Yes | Strictus::boolean($value, true) |
| Boolean | Yes | Strictus::nullableBoolean($value) |
| Array | No | Strictus::array($value) |
| Array | Yes | Strictus::array($value, true) |
| Array | Yes | Strictus::nullableArray($value) |
| Object | No | Strictus::object($value) |
| Object | Yes | Strictus::object($value, true) |
| Object | Yes | Strictus::nullableObject($value) |
| Class Type | No | Strictus::instance($instanceType, $value) |
| Class Type | Yes | Strictus::instance($instanceType, $value, true) |
| Class Type | Yes | Strictus::nullableInstance($instanceType, $value) |
| Enum Type | No | Strictus::enum($enumType, $value) |
| Enum Type | Yes | Strictus::enum($enumType, $value, true) |
| Enum Type | Yes | Strictus::nullableEnum($enumType, $value) |
#Union Types
Strictus also supports union types:
use Strictus\Enums\Type; $unionTypesVariable = Strictus::union([Type::INT, Type::STRING], 'foo'); echo $unionTypesVariable->value; //foo echo $unionTypesVariable(); //foo // Update variable $unionTypesVariable->value = 100; echo $unionTypesVariable->value; //100 // Thrown an exception if the value is wrong union types $unionTypesVariable->value = false; //StrictusTypeException
#Immutable Variables
If you want to create immutable variables, you can use the ->immutable() method. If you try to assign a new value
to an Immutable Variable, an Strictus\Exceptions\ImmutableStrictusException exception will be thrown:
$immutableScore = Strictus::int(100)->immutable(); $immutableScore(50); //ImmutableStrictusException $immutableScore->value = 50; //ImmutableStrictusException
#Error Handling
If you try to assign a value that doesn't match the type of the created variable, an
Strictus\Exceptions\StrictusTypeException exception will be thrown:
$score = Strictus::int(100); $score('one hundred'); //StrictusTypeException $score->value = false; //StrictusTypeException
If you try to assign a new value to an Immutable Variable, an
Strictus\Exceptions\ImmutableStrictusException exception will be thrown:
$immutableScore = Strictus::int(100)->immutable(); $immutableScore(50); //ImmutableStrictusException $immutableScore->value = 50; //ImmutableStrictusException
#Motivation
Following a discussion on Twitter between Christopher Miller and Wendell Adriel around the lack of strongly typed local variables for PHP we quickly decided a package was the right approach whilst we could get an RFC into the core.
#Credits
#Contributing
We welcome contributions!
Please visit the Contributing Guide to learn more about contributing to Strictus.