Skip to content
← Back to projects

Strictus

Strict Typing for local variables in PHP

Strictus

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.

New version available.