====== PHP RFC: Scalar Pseudo-type ======
* Version: 1.0
* Date: 2017-12-24 (use today's date here)
* Author: Richard Fussenegger, php@fleshgrinder.com
* Status: Under Discussion
* First Published at: http://wiki.php.net/rfc/scalar-pseudo-type
===== Introduction =====
Some procedures are capable of accepting or returning any type of the ones that are considered [[https://php.net/is-scalar|scalar]]. PHP does not have support for union types and the only way to communicate such support to callers or receivers is via documentation. This RFC proposes the addition of a special ''scalar'' pseudo-type that covers all types that are considered ''scalar'' – namely ''bool'', ''float'', ''int'', and ''string'' – to be valid for both parameter and return type constraints.
===== Proposal =====
This RFC proposes a new ''scalar'' pseudo-type. This type is analogous to ''callable'', ''iterable'', and ''object'', accepting multiple types instead of one single type.
''scalar'' accepts ''bool'', ''float'', ''int'', and ''string''. All of these types can be safely coerced to a ''string'' and be printed.
''scalar'' can be used as a parameter type constraint to require that a procedure is called with any of the types that are considered scalar.
function f(scalar $param) {
echo "{$param}\n";
}
''scalar'' can be combined with the nullable constraint to broaden the amount of types that are accepted.
function f(?scalar $param) {
echo "{$param}\n";
}
''scalar'' can also be used as a return type constraint to indicate that a procedure will return any of ''bool'', ''float'', ''int'', or ''string''. The combination with the nullable constraint is supported here as well.
function f(): scalar {
return 42;
}
function f(): ?scalar {
return null;
}
Parameters that are constrained to ''scalar'' may use a ''bool'', ''float'', ''int'', ''null'', or ''string'' as default value.
function f0(scalar $p = null) {}
function f1(scalar $p = true) {}
function f2(scalar $p = 4.2) {}
function f3(scalar $p = 42) {}
function f4(scalar $p = 'str') {}
Classes extending or implementing a supertype that has a ''scalar'' compatible parameter type constraint may broaden that constraint to be ''scalar'' (contravariance).
interface A {
function f1(bool $p);
function f2(float $p);
function f3(int $p);
function f4(string $p);
}
interface B implements A {
function f1(scalar $p);
function f2(scalar $p);
function f3(scalar $p);
function f4(scalar $p);
}
Classes extending or implementing a supertype that has a ''scalar'' return type constraint may narrow that constraint down to a compatible type (covariance).
interface A { function f(): scalar; }
interface B extends A { function f(): bool; }
interface C extends A { function f(): float; }
interface D extends A { function f(): int; }
interface E extends A { function f(): string; }
The function [[https://php.net/is-scalar|is_scalar]] to determine whether a value is ''scalar'' or not already exist in PHP since a long time and must not be added.
====== Weak Mode ======
Objects with a magic ''toString'' method are accepted and treated as strings in weak mode. The behavior is 1:1 the same as if the type constraint would have been ''string'' in the first place for objects. This ensures perfect consistency and adheres to the principle of least astonishment.
====== Examples ======
======= Scalar Parameters =======
PHP core already contains a multitude of procedures that could be constrained to ''scalar'' instead of ''mixed''.
function strpos(string $haystack, scalar $needle, //...
function printf(string $format, scalar ...$args): string {}
// ... and many, many, many more ...
However, it is also useful in userland.
interface Parser {
/** @return static */
function parse(scalar $input);
}
namespace Userland\Database\MySQL;
class ReadConnection {
private $mysqli;
function fetch(string $query, ?scalar ...$args): ResultSet {
$types = '';
foreach ($args as $arg) {
if (is_float($arg)) {
$types .= 'd';
} elseif (is_bool($arg) || is_int($arg)) {
$types .= 'i';
} else {
$types .= 's';
}
}
//...
$stmt->bind_params($types, ...$params);
//...
}
}
In other words, it allows one to implement type safe method overloading over a well-defined set of types. It does not cover all possibilities just one common one. (Covering all possibilities is not possible anyways and would require union types.)
======= Scalar Returns =======
The return type constraint is less commonly useful than the one for parameters, however, it is specifically of interest while designing supertypes for others and to work around the magic ''toString'' method that can only return values of type ''string'' (and is incompatible with exceptions).
interface Convertible {
function into(): scalar;
}
final class Longitude implements Convertible {
/** @var float */
private $deg;
//...
function into(): float {
return $this->deg;
}
}
// In combination with our previous DB example.
$db->fetch('SELECT * FROM t WHERE lng = ?', (new Longitude(42.42))->into());
===== Backward Incompatible Changes =====
''scalar'' is implemented as reserved name, therefore a class, interface, or trait named ''scalar'' cannot be declared.
===== Proposed PHP Version(s) =====
7.3.0
===== Future Scope =====
* Addition of an interface that allows objects to be considered ''scalar''.
===== Proposed Voting Choices =====
This proposal requires a 2/3 majority to be accepted.
===== Patches and Tests =====
* [[https://github.com/php/php-src/pull/2987|GitHub PR #2987]]