Search by

stevegrunwell / typesnitch

Utility for improving type coverage

Maintainers

Package info

codeberg.org/stevegrunwell/typesnitch

Issues

pkg:composer/stevegrunwell/typesnitch

Transparency log

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

v0.1.0 2026-09-04 22:00 UTC

This package is auto-updated.

Last update: 2026-09-04 21:19:54 UTC


README

TypeSnitch is a utility designed to help you gradually strengthen types in your PHP applications.

Imagine you have a function that looks something like this:

function sendMessage($user, $message)
{
    if (!$user) {
        return false;
    }

    return MessageFactory::make()
        ->setRecipient($user)
        ->setMessage($message)
        ->send();
}

The sendMessage() method accepts two arguments, but neither have type-hints. Is $user meant to be an ID, email address, or User instance? Is $message a string, array of strings, or something else?

This is a common problem in legacy codebases, and one that can be difficult to remedy: if we assume that $message is a string, how can we be sure adding a type-hint won't cause some corner of the application that passes an array of strings to blow up?

This is the problem TypeSnitch sets out to solve: instrumenting code to make assertions about types, allowing you to confidently add types to your code.

The Process

In its simplest form, using TypeSnitch looks like this:

First, you'll add type assertions throughout your codebase. You can think of these as a "proper type-hints coming soon!" billboard in your code. These assertions look like this:

use TypeSnitch\Type\Type;
use TypeSnitch\TypeSnitch;

function sendMessage($user, $message)
{
    TypeSnitch::assertType($user, User::class);
    TypeSnitch::assertType($message, Type::string);

    // ...
}

Once these assertions are in-place, the code will log a message any time the assertion fails, letting you know that there's still at least one instance where your code is being called with the wrong types.

Once your code has been orchestrated, wait for some amount of time. How long you wait will vary based on how much traffic your application handles, but it should be long enough that all paths through the orchestrated code will be executed (probably a week or two).

⚠️ If you have scheduled jobs that run infrequently (billing, archiving old data, etc.) it's probably best to make sure they have run successfully before moving forward!

Once you're sure that methods are being called with the expected types, you can confidently add proper type-hints and remove the assertions:

- function sendMessage($user, $message)
+ function sendMessage(User $user, string $message)
  {
-     TypeSnitch::assertType($user, User::class);
-     TypeSnitch::assertType($message, Type::string);

      // ...
  }

Congratulations, you have improved the type-safety of your application!

Installation

TypeSnitch can be installed via Composer:

composer require stevegrunwell/typesnitch

Please note that this library requires that you provide your own PSR-3 compliant logger; 99% of the time, this will probably be Monolog, but TypeSnitch should work with any implementation of Psr\Log\LoggerInterface.

If you don't already have a logger installed, you can install Monolog with the following:

composer require monolog/monolog

Next, you'll need to inject your logger into TypeSnitch. Where this happens will vary based on your framework or application layout, but the idea is to inject the logger as early in the bootstrap process as possible.

In this example, we'll create a simple Monolog instance and inject it in a simple index.php file (often the entry point to an application):

use Monolog\Level;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use TypeSnitch\TypeSnitch;

// Load our Composer autoloader.
require_once __DIR__ . '/vendor/autoload.php';

// Build a basic logger. This will vary based on your needs.
$logger = new Logger('typesnitch');
$logger->pushHandler(new StreamHandler('path/to/your.log', Level::Info));

// This is the important bit: tell TypeSnitch about the logger!
TypeSnitch::setLogger($logger);

Usage

The primary use of TypeSnitch is the TypeSnitch::assertType() method, which accepts up to four arguments: the subject that we're inspecting, the expected type, and then optionally a custom error message and an array of additional context to include in the log.

A simple example:

use TypeSnitch\TypeSnitch;

TypeSnitch::assertType($subject, 'boolean');

If $subject is a boolean (e.g. true or false), the method will return true and nothing will be logged. However, if $subject is not a boolean value, the method will return false and an INFO-level message will be logged to the logger instance that has been configured.

TypeSnitch features several built-in aliases (e.g. "bool" and "boolean" are treated as equivalent), but also exposes an enumerated list of types via TypeSnitch\Types:

use TypeSnitch\Types\Type;
use TypeSnitch\TypeSnitch;

TypeSnitch::assertType($subject, Type::Boolean);

The log message itself can be modified via the third argument, and additional context can be included via the fourth. Assuming your PSR-3 implementation adheres to the specification, messages can utilize context keys as placeholders:

use TypeSnitch\Types\Type;
use TypeSnitch\TypeSnitch;

TypeSnitch::assertType(
    $subject,
    Type::Boolean,
    'Ruh-roh, we expected to see a value with type {expected}, but saw {actual} in {method} instead!',
    [
        'method' => __METHOD__,
    ]
);

The previous example would generate a log message that looks like this:

Ruh-roh, we expected to see a value with type bool, but saw string instead in User::isAdmin() instead!

When using TypeSnitch::assertType(), the following context keys are automatically defined for you:

KeyDescriptionExamples
actualThe actual type (via gettype())string, boolean, etc.
expectedThe expected typestring, ?int, etc.
valueA string representation of the value that was being inspected (via var_export())'Hello, world!', true, etc.

For additional details on PSR-3 placeholders, please review this article by Larry Garfield.

Custom assertions

If you need more flexibility, you may also look at the TypeSnitch::assertThat() method, which accepts a callable that returns a boolean.

use TypeSnitch\TypeSnitch;

TypeSnitch::assertThat(fn () => in_array($some_var, ['option1', 'option2']));

The assertThat() method also accepts an optional second argument, which will customize the message that is logged (the default is simply "Failed assertion."):

use TypeSnitch\TypeSnitch;

TypeSnitch::assertThat(fn () => false, 'Oh no, false is false!'));

Additionally, assertThat() accepts an optional $context array as its third argument, which operates just like assertType():

use TypeSnitch\TypeSnitch;

TypeSnitch::assertThat(
    fn () => $val >= 0,
    'Failed asserting {value} is greater than or equal to zero',
    [
        'value' => $val,
    ]
);

However, please note that TypeSnitch::assertThat() does not include any additional context by default!

Frequently Asked Questions

How much overhead will TypeSnitch add to my application?

TypeSnitch is intentionally very lightweight and is meant to be a short-term refactoring aid. Logs are only written when an assertion fails, so the overhead of running TypeSnitch in production should be negligible.

Why is this library still compatible with PHP 7.4?

While PHP 7.4 has long-since reached End of Life (EOL) status, there are plenty of production applications that have yet to be upgraded to PHP 8.x. In fact, TypeSnitch was conceived while working on such an upgrade.

In order to aid those teams working on upgrades, version 1.x of this library includes support for PHP 7.4. May it help you in your upgrade efforts! 😉

License

TypeSnitch is available under the terms of the MIT License, a copy of which is included in this library.