A PHPStan plugin to enforce pure functional programming in PHP.
Add the repository to your composer.json:
{
"repositories": [
{
"type": "vcs",
"url": "git@github.com:NormanAlbert91/functional-php.git"
}
]
}Then install the package:
composer require --dev na/functional-php:dev-mainIf you use phpstan/extension-installer, the plugin is automatically registered. Otherwise, add to your phpstan.neon:
includes:
- vendor/na/functional-php/extension.neonVariables cannot be reassigned.
$x = 1;
$x = 2; // Error: Variable $x is being reassigned
$x += 1; // Error: Variable $x is being reassignedNo for, while, or do-while loops. Use array_map, array_filter, array_reduce instead.
for ($i = 0; $i < 10; $i++) {} // Error
while ($condition) {} // Error
do {} while ($condition); // Error
foreach ($items as $item) {} // OK - foreach is allowed
array_map(fn($x) => $x * 2, $items); // OKNo functions that mutate arrays in-place.
sort($array); // Error: Use [...$array] with usort()
array_push($array, $value); // Error: Use [...$array, $value]
array_pop($array); // Error: Use array_slice()
shuffle($array); // Error
$sorted = [...$array];
usort($sorted, fn($a, $b) => $a <=> $b); // OK - copy first
$new = [...$array, $value]; // OK - spread operatorNo global variables, superglobals, or static function variables.
global $config; // Error
$_GET['id']; // Error
$_SESSION['user']; // Error
function counter() {
static $count = 0; // Error: Hidden state
}All functions must return a value. No void or never return types.
function doSomething(): void {} // Error: Pure functions must return a value
function log($msg) {} // Error: Must have a return type
function add(int $a, int $b): int {
return $a + $b; // OK
}No direct output. Return values instead.
echo 'Hello'; // Error
print 'World'; // Error
var_dump($data); // Error
exit(1); // Error
die('error'); // Error
return 'Hello World'; // OKNo pass-by-reference parameters. Return new values instead.
function increment(int &$value) {} // Error: Reference parameters allow mutation
function increment(int $value): int {
return $value + 1; // OK
}No static properties. They are global mutable state.
class Singleton {
private static ?self $instance = null; // Error
}
Counter::$count; // Error: Accessing static propertiesNo functions with non-deterministic output. Inject dependencies instead.
// Random - inject a random generator
rand(1, 100); // Error
random_int(1, 100); // Error
// Time - inject a Clock interface
time(); // Error
new DateTime(); // Error
new DateTimeImmutable(); // Error
// Filesystem - inject a filesystem interface
file_get_contents('file.txt'); // Error
file_exists('file.txt'); // Error
// Network - inject an HTTP client
curl_exec($ch); // Error
// Process - inject a process runner
exec('ls'); // Error
shell_exec('ls'); // ErrorAll class properties must be readonly.
class User {
public string $name; // Error: Must be readonly
}
class User {
public readonly string $name; // OK
}
readonly class User {
public string $name; // OK - readonly class
}This plugin enforces the core principles of functional programming:
- Immutability - Data never changes after creation
- Pure Functions - Same input always produces same output, no side effects
- Determinism - No hidden dependencies on external state
- Explicit Dependencies - All dependencies passed as parameters