donatj/php-dnf-solver

PHP DNF (Disjunctive Normal Form) Signature Compatibility Solver

v0.3.0 2024-04-17 17:11 UTC

README

Latest Stable Version License ci.yml Coverage Status

PHP DNF (Disjunctive Normal Form) Signature Compatibility Solver - see: https://wiki.php.net/rfc/dnf_types

Requirements

  • php: ^8.1.0 || ^8.2.0 || ^8.3.0

Installing

Install the latest version with:

composer require 'donatj/php-dnf-solver'

Examples

Example Parameter Satisfaction Check

<?php

namespace Examples;

use donatj\PhpDnfSolver\DNF;
use donatj\PhpDnfSolver\Types\UserDefinedType;

require __DIR__ . '/../vendor/autoload.php';

interface A {}
interface B {}
interface C {}
interface D {}

class Foo implements A, B {}
class Bar implements B, C {}
class Baz implements C, D {}

$qux = function ( A|(B&C) $aOrB ) : void {};

$quxParamType = (new \ReflectionFunction($qux))->getParameters()[0]->getType();

$quxDnf = DNF::getFromReflectionType($quxParamType);

var_dump($quxDnf->isSatisfiedBy(
	new UserDefinedType(Foo::class)
)); // true

var_dump($quxDnf->isSatisfiedBy(
	new UserDefinedType(Bar::class)
)); // true

var_dump($quxDnf->isSatisfiedBy(
	new UserDefinedType(Baz::class)
)); // false

Outputs:

bool(true)
bool(true)
bool(false)

Example DNF Building

<?php

namespace Examples;

use donatj\PhpDnfSolver\Types\AndClause;
use donatj\PhpDnfSolver\Types\BuiltInType;
use donatj\PhpDnfSolver\Types\OrClause;
use donatj\PhpDnfSolver\Types\UserDefinedType;

require __DIR__ . '/../vendor/autoload.php';

interface A {}
interface B {}
interface C {}

var_dump((new OrClause(
	new UserDefinedType(A::class),
	new UserDefinedType(B::class),
	new UserDefinedType(C::class)
))->dnf()); // A|B|C

var_dump((new OrClause(
	new UserDefinedType(A::class),
	new AndClause(
		new UserDefinedType(B::class),
		new UserDefinedType(C::class)
	)
))->dnf()); // A|(B&C)

var_dump((new OrClause(
	new AndClause(new UserDefinedType(A::class), new UserDefinedType(B::class)),
	new AndClause(
		new UserDefinedType(B::class),
		new UserDefinedType(C::class)
	),
	new BuiltInType('null'),
))->dnf()); // (A&B)|(B&C)|null

Outputs:

string(32) "Examples\A|Examples\B|Examples\C"
string(34) "Examples\A|(Examples\B&Examples\C)"
string(52) "(Examples\A&Examples\B)|(Examples\B&Examples\C)|null"

Documentation

Class: \donatj\PhpDnfSolver\DNF

Method: DNF::getFromReflectionType

function getFromReflectionType(\ReflectionType $type) : \donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface

Helper to convert a ReflectionType into it's DNF representation

Example sources include
  • ReflectionFunctionAbstract::getParameters()[…]->getType()
  • ReflectionParameter::getType()
  • ReflectionMethod::getReturnType()
  • ReflectionProperty::getType()

Method: DNF::reflectionTypeSatisfiesReflectionType

function reflectionTypeSatisfiesReflectionType(\ReflectionType $satisfyingType, \ReflectionType $satisfiedType) : bool

Helper to quickly check if a ReflectionType satisfies another ReflectionType

Parameters:
  • \ReflectionType $satisfyingType - The type which must be satisfied (e.g. a parameter type)
  • \ReflectionType $satisfiedType - The type which must satisfy the other (e.g. a return type)

Method: DNF::getFromVarType

function getFromVarType(\ReflectionParameter|\ReflectionProperty $parameter) : \donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface|null

Helper to quickly get a DNF representation of a (ReflectionParameter or ReflectionProperty)'s return type

Method: DNF::getFromReturnType

function getFromReturnType(\ReflectionFunctionAbstract $func) : \donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface|null

Helper to quickly get a DNF representation of a ReflectionFunctionAbstract (ReflectionFunction /
ReflectionMethod)'s return type

Class: \donatj\PhpDnfSolver\Exceptions\InvalidArgumentException

Class: \donatj\PhpDnfSolver\Exceptions\LogicException

Class: \donatj\PhpDnfSolver\Types\AndClause

Represents an "and clause" - a set of types which must all be satisfied - e.g. "A&B&C"

Method: AndClause->__construct

function __construct(\donatj\PhpDnfSolver\SingularDnfTypeInterface ...$types)
Parameters:
  • \donatj\PhpDnfSolver\SingularDnfTypeInterface $types - The list of types to be satisfied

Method: AndClause->dnf

function dnf() : string

Return the canonical string representation of the DNF representation of this type

Method: AndClause->isSatisfiedBy

function isSatisfiedBy(\donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface $value) : bool

Tests if this type is satisfied by the given type

For example, if this type is "A|(B&C)" and the given type matches just "A", this method returns true.
If the given type matches just "B", this method returns false.
If the given type matches "B&C", this method returns true.

Method: AndClause->count

function count() : int

Returns the number of types in this DNF type

Method: AndClause->getTypes

function getTypes() : array
Returns:
  • \donatj\PhpDnfSolver\SingularDnfTypeInterface[]

Class: \donatj\PhpDnfSolver\Types\BuiltInType

Represents a "built in type" as defined by ReflectionNamedType::isBuiltin()

This includes:

  • int
  • float
  • string
  • bool
  • array
  • iterable

Method: BuiltInType->__construct

function __construct(string $name)
Parameters:
  • string $name - The name of the built-in type

Method: BuiltInType->dnf

function dnf() : string

Return the canonical string representation of the DNF representation of this type

Method: BuiltInType->getTypeName

function getTypeName() : string

Returns the fully qualified type name of this type

Method: BuiltInType->isSatisfiedBy

function isSatisfiedBy(\donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface $value) : bool

Tests if this type is satisfied by the given type

For example, if this type is "A|(B&C)" and the given type matches just "A", this method returns true.
If the given type matches just "B", this method returns false.
If the given type matches "B&C", this method returns true.

Method: BuiltInType->count

function count() : int

Always 1 for singular types

Returns the number of types in this DNF type

Class: \donatj\PhpDnfSolver\Types\CallableType

Represents a "callable" type

This includes:

  • callable
  • Closure
  • Invokable classes

Method: CallableType->dnf

function dnf() : string

Return the canonical string representation of the DNF representation of this type

Method: CallableType->isSatisfiedBy

function isSatisfiedBy(\donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface $value) : bool

Tests if this type is satisfied by the given type

For example, if this type is "A|(B&C)" and the given type matches just "A", this method returns true.
If the given type matches just "B", this method returns false.
If the given type matches "B&C", this method returns true.

Method: CallableType->getTypeName

function getTypeName() : string

Returns the fully qualified type name of this type

Method: CallableType->count

function count() : int

Always 1 for singular types

Returns the number of types in this DNF type

Class: \donatj\PhpDnfSolver\Types\OrClause

Represents a "or" clause - a set of types where any one of them must be satisfied - e.g. "A|B|(C&D)"

Method: OrClause->__construct

function __construct(\donatj\PhpDnfSolver\Types\AndClause|\donatj\PhpDnfSolver\SingularDnfTypeInterface ...$types)
Parameters:
  • \donatj\PhpDnfSolver\Types\AndClause | \donatj\PhpDnfSolver\SingularDnfTypeInterface $types - The list of types to be satisfied. Does not accept an OrClause as DNF defines that as invalid.

Method: OrClause->dnf

function dnf() : string

Return the canonical string representation of the DNF representation of this type

Method: OrClause->isSatisfiedBy

function isSatisfiedBy(\donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface $value) : bool

Tests if this type is satisfied by the given type

For example, if this type is "A|(B&C)" and the given type matches just "A", this method returns true.
If the given type matches just "B", this method returns false.
If the given type matches "B&C", this method returns true.

Method: OrClause->count

function count() : int

Returns the number of types in this DNF type

Method: OrClause->getTypes

function getTypes() : array
Returns:
  • \donatj\PhpDnfSolver\Types\AndClause[]

Class: \donatj\PhpDnfSolver\Types\UserDefinedType

Represents a "user defined type" - a class, interface, or trait, etc.

<?php
namespace donatj\PhpDnfSolver\Types;

class UserDefinedType {
	public $className;
}

Method: UserDefinedType->__construct

function __construct(string $className)
Parameters:
  • class-string $className - The name of the class, interface, or trait to be satisfied

Throws: \donatj\PhpDnfSolver\Exceptions\InvalidArgumentException - if the user defined type does not exist after triggering registered autoloaders

Method: UserDefinedType->dnf

function dnf() : string

Return the canonical string representation of the DNF representation of this type

Method: UserDefinedType->getTypeName

function getTypeName() : string

Returns the fully qualified type name of this type

Returns:
  • class-string

Method: UserDefinedType->isSatisfiedBy

function isSatisfiedBy(\donatj\PhpDnfSolver\SingularDnfTypeInterface|\donatj\PhpDnfSolver\NestedDnfTypeInterface $value) : bool

Tests if this type is satisfied by the given type

For example, if this type is "A|(B&C)" and the given type matches just "A", this method returns true.
If the given type matches just "B", this method returns false.
If the given type matches "B&C", this method returns true.

Method: UserDefinedType->count

function count() : int

Always 1 for singular types

Returns the number of types in this DNF type