arnapou/ensure

Library - Simple library to make safer php code for SA tools.

v2.4 2024-04-28 19:39 UTC

This package is auto-updated.

Last update: 2024-04-28 17:39:32 UTC


README

pipeline coverage

This library is a simple tool to have a safer php code.

We want to 'ensure' or 'enforce' the type check for quality reasons and/or for Static Analysis tools like PHPStan or Psalm.

Installation

composer require arnapou/ensure

packagist 👉️ arnapou/ensure

Ensure

In the example below, we guaranty the fact that a non-constrained int or string are constrained into the class.

use Arnapou\Ensure\Ensure;
use Arnapou\Ensure\Expected;

class MyObject
{
    /** @var positive-int */
    public int $id;

    /** @var non-empty-string */
    public string $name;

    /** 
     * @throws Expected 
     */
    public function __construct(int $id, string $name) 
    {
        $this->id = Ensure::positiveInt($id);
        $this->name = Ensure::nonEmptyString($name);
    }
}

Enforce

In the example below, which can be from a legacy code, we have a phpdoc type-hinting in the construct. But it is not "hard" enough from a php point of view, and it may be too risky to change the mixed type-hinting.

You can guaranty the type hinting inside the class while staying lax on type-check by using Enforce. It will cast the value when it is enough "safe" to do it :

  • Examples of valid/auto-casted int : "1234", "1.2e3", 3.0, true
  • Examples of invalid int : "foo", "1.234e2", 3.14, []
use Arnapou\Ensure\Enforce;
use Arnapou\Ensure\Expected;

class MyObject
{
    /** @var positive-int */
    public int $id;

    /** @var non-empty-string */
    public string $name;

    /** 
     * @param int    $id
     * @param string $name
     *
     * @throws Expected 
     */
    public function __construct(mixed $id, mixed $name) 
    {
        $this->id = Enforce::positiveInt($id);
        $this->name = Enforce::nonEmptyString($name);
    }
}

Message customization or translation

You can do that implementing your own MessageFactoryInterface and then set it as default for Expected class.

class MyMessageFactory implements MessageFactoryInterface
{
    public function createExpectedMessage(string $expected, int $code, mixed $value, string $propertyName, mixed ...$parameters): string
    {
        // Build the message here.
    }
}

// Change the default message factory.
\Arnapou\Ensure\Expected::setMessageFactory(new MyMessageFactory());

ℹ️ The default MessageFactory implementation is final, you cannot extend it, but you can reuse it with composition if necessary :

class MyMessageFactory implements MessageFactoryInterface
{
    private \Arnapou\Ensure\MessageFactory\MessageFactory $internal;

    public function __construct(){
        $this->internal = new \Arnapou\Ensure\MessageFactory\MessageFactory();
    }

    public function createExpectedMessage(string $expected, int $code, mixed $value, string $propertyName, mixed ...$parameters): string
    {
        if (/* any logic you need */) {
            // Your customization here
        }
        
        // Default on the default message factory
        return $this->internal->createExpectedMessage($expected, $code, $value, $propertyName, ...$parameters);
    }
}

// Change the default message factory.
\Arnapou\Ensure\Expected::setMessageFactory(new MyMessageFactory());

Changelog versions

StartTag, BranchPhp
25/11/20232.x, main8.3
23/08/20231.x8.2