Olle Härstedt
Olle Härstedt

Reputation: 4020

How much type-inference can you possibly cram out of vanilla PHP?

I want to make a type-inferring linter for a subset of PHP, but am unsure about how much type information you can get out of it without having to enforce phpdoc annotations or similar. Consider this example:

function a() {
  b(10); // This is wrong, but we don't know the type of b()
}

function b($c) {
   print($c . " hallo"); // Only allow concatenations with strings
}

Without the in OCaml present and for mutual recursion, things have to be defined in order of use. The way to get around it is to make a two-stage type-checker, where the first stage checks interfaces, but type-inferring needs to go through the body of the functions.

One way to get around this is to enforce phpdoc docblocks:

function a() {
  b(10); // Type error: b() expects a string
}

/**
 * @param string $c
 */
function b($c) {
   print($c . " hallo");
}

Enforcing docblocks to be used in type-checking feels... wrong. Is there any way around this? Of course the scalar type-hints v5, using declare(strict_types=1), would allow scalar type-hints in function signatures, but that's too far into the future.

Edit: I'm stupid, b() could of course be inferred from it's usage in a(), so we would have:

function a() {
  b(10); // b() inferred to int -> void
}

function b($c) {
   print($c . " hallo"); // Wrong, $c used as a string
}

Upvotes: 3

Views: 775

Answers (1)

crowebird
crowebird

Reputation: 2586

You could create classes that represent the scalar values you want to type hint.

class String {
  protected $string;

  public function __construct($value) {
    if (!is_string($value)) {
      throw new Exception('Not a string');
    }
    $this->string = $value;
  }

  public function __toString() {
    return $this->string;
  }
}

Then in your function declaration:

function b(String $c) {
  ...
}

b(new String('Some String'));

Upvotes: 3

Related Questions