Type inferring examples
Here we have some snippets of PHP code, and what types are inferred afterwards.
lca(int, double) = double
$a = 4;
$a += 5.5;
// $a :: double
lca(int, string) = mixed
$a = 4;
$a += '5.5';
// $a :: mixed
int + mixed = mixed
$a = 4;
$b = 5 + '5.5';
$c = $a + $b;
// $a :: int
// $b :: mixed => $c :: mixed
Concatenation is string
$a = 4;
$b = 5 + '5.5';
$c = $a . $b;
// $a :: int, $b :: mixed
// but $c :: string
All elements of an array have one type — array<T>
// array<string>
$a = ['1', '2', (string)3];
// array<double>
$num = floatval($_GET['val']);
$a = [1.5, -100];
$a[] = $num;
// array<array<int>>
$a = [ [1,2], [3,4,5=>5], ['key' => (int)'7']];
// array<array<double>>
$a = [];
$a[$i1][$i2] = 5.4;
Mixing types inside an array — array<mixed>
// array<mixed>
$a = [1, 2, '3'];
// array<mixed>
$a = [1,2];
$a[] = $_GET['val'];
// array<array<mixed>>
$a = [ [1,2], [3,4,5=>5], ['key' => '7']];
// array<mixed>
return [$count, $items_arr];
// array<mixed>
['string', [1,2]];
// array<mixed>, as $config is probably array<mixed>
$options = ['id' => true, 'num' => true, 'idx' => $config['idx']];
// array<mixed>, as $options is probably not array<bool>
$def = ['id' => true] + $options;
Mixing primitive types — mixed
// mixed
$a = isset($input['v']) ? $input['v'] : 0;
// mixed
$a = [1,2,3];
if (...) {
$a = 4;
echo $a;
}
// mixed
if (...) {
$a = 'str';
...
} else {
$a = [];
}
echo $a;
// mixed
$val = $config['key'];
// get() returns mixed[] => $count :: mixed, $items :: mixed
function get() { ... return [$count, $items]; }
list($count, $items) = get();
// default value int, called with bool => $arg :: mixed
function f3($arg = 42) { }
f3(true);
Array key — mixed
$arr = [...];
foreach ($arr as $key => $v) {
// $key :: mixed (since keys are int|string in PHP)
// $arr :: array<T> => $v :: T
}
T | null (or ?T, shorter form) (nullability doesn't convert to mixed)
// ?int
$v = ... ? 0 : null;
// ?array<int>
$a = null;
if (...) {
$a = fReturningArrayInt();
}
// mixed, as null|mixed = mixed
$a = null;
if (...) {
$a = fReturningMixed();
}
// returns ?bool
function f2($id) {
return $id == 0 ? null : $id > 0;
}
T | false (falseability doesn't convert to mixed)
// int|false
$v = ... ? 0 : false;
// string|false, as substr() can return false
$s = substr("string", 0, 1);
// int|false, as strpos() can return false
$pos = strpos($haystack, $needle, $offset);
// int|false|null
$pos = 1 ? strpos(...) : null;
// (int|false)[]
$arr = [1, 2, false, getInt(), (int)$_GET['id']];
// $user_id :: int|false
function log($user_id = false) {}
log(42);
Empty arrays have Unknown inside
// $options :: array<Unknown>, as it is always empty from all invocations
function log($user_id, $options = []) {
// this is an error! $v will be Unknown type
$v = $options['use_newlines'];
// foreach on always-empty array is also an error
}
log(1);
log(2);
log(3, []);
array_* functions leave types as is
// array<int>
$a2 = array_values(['a' => 1, 'b' => 2]);
// array<string>
$a = array_reverse(['c','b','a']);
// $v :: string, $a remains array<string>
$v = array_shift($a);
// array<double>
$a = array_fill(0, 10, (float)$input['v']);
References change argument types
function assignTo(&$target, $value) {
$target = $value;
}
$t1 = 0;
assignTo($t1, 100);
$t2 = '0';
assignTo($t2, '100');
// everything is mixed:
// 1) $value is int|string => $value :: mixed
// 2) $t1 and $t2 by ref assigned mixed => $t1 :: mixed, $t2 :: mixed
// 3) $t1 and $t2 passed to $target => $target :: mixed
KPHP has many types except primitives: instances, tuples, shapes, futures…
Type inferring concepts are the same for all of them.