PHPackages                             jdwx/strict - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. jdwx/strict

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

jdwx/strict
===========

A PHP module to simplify strict programming.

v1.1.1(4mo ago)14.0k9MITPHPPHP &gt;=8.3CI failing

Since Jun 10Pushed 1mo agoCompare

[ Source](https://github.com/jdwx/strict-php)[ Packagist](https://packagist.org/packages/jdwx/strict)[ RSS](/packages/jdwx-strict/feed)WikiDiscussions main Synced yesterday

READMEChangelog (10)Dependencies (6)Versions (39)Used By (9)

jdwx/strict-php
===============

[](#jdwxstrict-php)

A simple PHP module for writing strict, type-safe code with minimal boilerplate.

Installation
------------

[](#installation)

You can require it directly with Composer:

```
composer require jdwx/strict
```

Or download the source from GitHub:

Requirements
------------

[](#requirements)

This module requires PHP 8.3 or later. It has no other runtime dependencies.

It is designed to work with strict types and static analysis tools like Phan and PHPStan.

Usage
-----

[](#usage)

This module is designed to make it easier to write PHP that works with strict\_mode and strict type checks in static analysis. It allows you to make type expectations explicit without writing a ton of extra code. It's meant to balance always check that functions didn't fail with the YAGNI of preemptively handling errors that may never occur.

This is particularly helpful for quick and dirty code or prototyping.

### Return Value Checking

[](#return-value-checking)

The OK class provides static methods for PHP functions that may fail. The static method calls the underlying PHP function and throws an exception if it fails.

For example, this code is incredibly common:

```
if ( ! preg_match( '/test/', $string ) ) {
    echo "It didn't match!";
} else {
    echo "It matched!";
}
```

But preg\_match() returns 0 if there is no match and false if there is an error.

With this module, you can write:

```
if ( ! OK::preg_match( '/test/', $string ) ) {
    echo "It didn't match!";
} else {
    echo "It matched!";
}
```

In this case, OK::preg\_match() will still return 0 if there is no match but will throw an exception if preg\_match() fails. If that happens, you'll know that you need to insert real error handling code.

It can also reduce boilerplate code. For example, this:

```
$contents = @file_get_contents( '/some/file' );
if ( ! is_string( $contents ) ) {
    throw new RuntimeException( 'file read failed!' );
}
```

can be reduced to:

```
$contents = OK::file_get_contents( '/some/file' );
```

### Type Checking

[](#type-checking)

The TypeIs class provides static methods for ensuring that a value is a given type. This is mostly for the benefit of static analysis, but can help detect unexpected types earlier than function call boundaries (assuming strict\_types is enabled).

There are also some limitations to static analysis. For example, static analysis should raise a warning here at higher strictness levels:

```
i_require_a_string( $string_or_null );
```

The way this is typically handled in our code is:

```
assert( is_string( $string_or_null ) );
i_require_a_string( $string_or_null );
```

This is fast and usually works fine. But there are exceptions. For example, Phan (currently) does not allow narrowing the type of an instance property. So this will still generate a warning:

```
assert( is_string( $this->string_or_null ) );
i_require_a_string( $this->string_or_null );
```

Typically, you have to introduce a locally scoped temporary variable to get around it:

```
$string = $this->string_or_null;
assert( is_string( $string) );
i_require_a_string( $string );
```

The TypeIs class can help:

```
i_require_a_string( TypeIs::string( $this->string_or_null ) );
```

This resolves the static analysis warning from Phan. And it's safe, because if your expectation that `$this->string_or_null` is already a string doesn't hold, an exception will be thrown.

Note that this is *very* different from `strval( $this->string_or_null )` because it is asserting that the value is already a string, not silently converting nulls to an empty string.

This is low overhead but not zero overhead. So it isn't ideal if you wind up using that property 30 times. In that case, it's still better to create the local variable:

```
$string = TypeIs::string( $string_or_null );
i_require_a_string( $string );
i_also_require_a_string( $string );
i_too_require_a_string( $string );
```

### Iterators

[](#iterators)

In PHP, arrays serve as both maps and lists. (We distinguish "maps" as arrays with meaningful string keys and "lists" as arrays with ordinal integer keys.)

One of PHP's lingering "features" from its early years is that numbers stored as strings are converted to numbers when used as array keys. E.g. `$x[ "1" ] = 'one'` and `$x[ 1 ] = 'one'` are equivalent. This causes problems when strict\_types is used:

```
declare( strict_types = 1 );

/** @param array */
function do_a_thing( array $r ) : void {
    foreach ( $r as $k => $v ) {
        i_require_a_string( $k );
    }
}

do_a_thing( [ 'a' => 'Ayyyyy!', '1' => 'one' ] );
```

This code will typically *pass* static analysis and then fail with a TypeError when run. That's not great! The Iter class is designed to handle these situations:

```
declare( strict_types = 1 );

/** @param array */
function do_a_thing( array $r ) : void {
    foreach ( Iter::mapString( $r ) as $k => $v ) {
        i_require_a_string( $k );
    }
}

do_a_thing( [ 'a' => 'Ayyyyy!', '1' => 'one' ] );
```

This avoids the need to remember to have every reference to `$k` use `strval( $k )` or to do `$k = strval( $k )` at the top of the loop. As a bonus, it also ensures that the value is really a string. (If that's undesirable, the Iter::map() and Iter::list() static methods don't perform any type checking of the values returned.)

### Conversion

[](#conversion)

The Convert class provides some simple helper static functions for common cases of parameter handling. For example, it's often desirable to accept a single string in lieu of a list so people don't have to write `do_things( [ 'one_thing' ] )`:

```
/** @param list|string $targets */
function do_things( array|string $targets ) : void {
    if ( ! is_array( $targets ) ) {
        $targets = [ $targets ];
    }
    // ...
    foreach ( $targets as $target ) {
        do_a_thing( $target );
    }
}
```

With the Convert class:

```
/** @param list|string $targets */
function do_things( array|string $targets ) : void {
    foreach ( Convert::listOrString( $targets ) as $target ) {
        do_a_thing( $target );
    }
}
```

Stability
---------

[](#stability)

This is a new module that is not yet widely used internally. The OK class attempts to mimic the PHP internal functions it covers as closely as possible, so it should be at least as stable as those functions. The TypeIs, Iter, and Convert classes are designed to be low-overhead and are providing relatively straightforward functionality that should allow them to be pretty stable, but they are new and may have some rough edges. And as this module is adopted internally, we may determine that it is necessary to change the API.

It has complete test coverage, with a handful of exceptions for some of the PHP function error cases that are difficult to test.

History
-------

[](#history)

This is a new module, originally written in June 2025.

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance85

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~7 days

Recently: every ~0 days

Total

38

Last Release

130d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f71b4b976170f89eac19be48c7381a27c49ed8c9cb5e8e6ff05a2ad7583efa13?d=identicon)[jdwx](/maintainers/jdwx)

---

Top Contributors

[![jdwx](https://avatars.githubusercontent.com/u/2722779?v=4)](https://github.com/jdwx "jdwx (8 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/jdwx-strict/health.svg)

```
[![Health](https://phpackages.com/badges/jdwx-strict/health.svg)](https://phpackages.com/packages/jdwx-strict)
```

###  Alternatives

[elvanto/litemoji

A PHP library simplifying the conversion of unicode, HTML and shortcode emoji.

884.9M12](/packages/elvanto-litemoji)[shubhamjain/php-id3

A MP3 ID3 tags reader in native PHP

434.4k](/packages/shubhamjain-php-id3)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
