Type Hinting for Properties in PHP 7

Type hinting for properties in PHP 7?

PHP 7.4 will support typed properties like so:

class Person
{
public string $name;
public DateTimeImmutable $dateOfBirth;
}

PHP 7.3 and earlier do not support this, but there are some alternatives.

You can make a private property which is accessible only through getters and setters which have type declarations:

class Person
{
private $name;
public function getName(): string {
return $this->name;
}
public function setName(string $newName) {
$this->name = $newName;
}
}

You can also make a public property and use a docblock to provide type information to people reading the code and using an IDE, but this provides no runtime type-checking:

class Person
{
/**
* @var string
*/
public $name;
}

And indeed, you can combine getters and setters and a docblock.

If you're more adventurous, you could make a fake property with the __get, __set, __isset and __unset magic methods, and check the types yourself. I'm not sure if I'd recommend it, though.

Type hinting in PHP 7 - array of objects

It's not included.

If it's not included, do you have Any clue why it was not included when type hinting was added?

With the current array implementation, it would require checking all array elements at runtime, because the array itself contains no type information.

It has actually already been proposed for PHP 5.6 but rejected: RFC "arrayof" - interestingly not because of performance issues which turned out to be neglible, but because there was no agreement in how exactly it should be implemented. There was also the objection that it is incomplete without scalar type hints. If you are interested in the whole discussion, read it in the mailing list archive.

IMHO array type hints would provide most benefit together with typed arrays, and I'd love to see them implemented.

So maybe it's about time for a new RFC and to reopen this discussion.


Partial Workaround:

you can type hint variadic arguments and thus write the signature as

function findUserByAge(int $age, User ...$users) : array

Usage:

findUserByAge(15, ...$userInput);

In this call, the argument $userInput will be "unpacked" into single variables, and in the method itself "packed" back into an array $users. Each item is validated to be of type User. $userInput can also be an iterator, it will be converted to an array.

Unfortunately there is no similar workaround for return types, and you can only use it for the last argument.

Nullable string type hinting gives syntax error in PHP 7.2

Typed properties were not included until PHP 7.4.

Is type callable supported with typed properties?

Proposal in provided link https://wiki.php.net/rfc/typed-properties has status declined.

The proposal implemented in php7.4 is here https://wiki.php.net/rfc/typed_properties_v2 and there's an explanation about callable:

The callable type is not supported, because its behavior is context
dependent The following example illustrates the issue:

class Test {
public callable $cb;

public function __construct() {
// $this->cb is callable here
$this->cb = [$this, 'method'];
}

private function method() {}
}

$obj = new Test;
// $obj->cb is NOT callable here
($obj->cb)();

This means that it is possible to write a legal value to a property
and then proceed to read an illegal value from the same property. This
fundamental problem of the callable pseudo-type is laid out in much
more detail in the consistent callables RFC.

The recommended workaround is to instead use the Closure type, in
conjunction with Closure::fromCallable(). This ensures that the
callable will remain callable independent of scope. For a discussion
of alternative ways to handle the callable issue, see the Alternatives
section.

List of all implemented proposals for php7.4 is here https://wiki.php.net/rfc#php_74.

PHP 7.4 Typed properties iteration

If you want to allow a typed attribute to be nullable you can simply add a ? before the type and give NULL as default value like that:

class DragonBallClass 
{
public ?string $goku = NULL;
public string $bulma = 'Bulma';
public string $vegeta = 'Vegeta';
}

In this case NULL is a perfectly legitimate value (and not a dummy value).

demo


Also without using ?, you can always merge the class properties with the object properties lists:

class DragonBallClass 
{
public string $goku;
public string $bulma = 'Bulma';
public string $vegeta = 'Vegeta';
}

$dragonBall = new DragonBallClass();

$classProperties = get_class_vars(get_class($dragonBall));
$objectProperties = get_object_vars($dragonBall);

var_dump(array_merge($classProperties, $objectProperties));

// array(3) {
// ["goku"]=>
// NULL
// ["bulma"]=>
// string(5) "Bulma"
// ["vegeta"]=>
// string(6) "Vegeta"
// }

What is the typed property for Resource

Short answer:

You can't. Sorry.

Some background:

There's an RFC here discussing the gap, but it hasn't moved since 2015: https://wiki.php.net/rfc/resource_typehint

The original RFC for scalar type hints specified that

No type declaration for resources is added, as this would prevent moving from resources to objects for existing extensions, which some have already done (e.g. GMP).

There's a comment on bug 71518 making the point that type-hinting for a resource really isn't very useful, since you'd still be able to pass in a file handle, or a GD resource, or a CURL handle, none of which do remotely the same thing. The bug itself has been suspended, so it doesn't look like this is going to be addressed any time soon.

A user-land compromise could be to write a thin object-wrapper around whichever resource type you need, and type-hint against that class instead. You still won't get typed property support for the resource on your new object, but it makes the issue less visible to the rest of your application.

Type hinting for properties in PHP 7?

PHP 7.4 will support typed properties like so:

class Person
{
public string $name;
public DateTimeImmutable $dateOfBirth;
}

PHP 7.3 and earlier do not support this, but there are some alternatives.

You can make a private property which is accessible only through getters and setters which have type declarations:

class Person
{
private $name;
public function getName(): string {
return $this->name;
}
public function setName(string $newName) {
$this->name = $newName;
}
}

You can also make a public property and use a docblock to provide type information to people reading the code and using an IDE, but this provides no runtime type-checking:

class Person
{
/**
* @var string
*/
public $name;
}

And indeed, you can combine getters and setters and a docblock.

If you're more adventurous, you could make a fake property with the __get, __set, __isset and __unset magic methods, and check the types yourself. I'm not sure if I'd recommend it, though.

Why I am suddenly getting a Typed property must not be accessed before initialization error when introducing properties type hints?

Since PHP 7.4 introduces type-hinting for properties, it is particularly important to provide valid values for all properties, so that all properties have values that match their declared types.

A property that has never been assigned doesn't have a null value, but it is on an undefined state, which will never match any declared type. undefined !== null.

For the code above, if you did:

$f = new Foo(1);
$f->getVal();

You would get:

Fatal error: Uncaught Error: Typed property Foo::$val must not be accessed before initialization

Since $val is neither string nor nullwhen accessing it.

The way to get around this is to assign values to all your properties that match the declared types. You can do this either as default values for the property or during construction, depending on your preference and the type of the property.

For example, for the above one could do:

class Foo {

private int $id;
private ?string $val = null; // <-- declaring default null value for the property
private Collection $collection;
private DateTimeInterface $createdAt;
private ?DateTimeInterface $updatedAt;

public function __construct(int $id) {
// and on the constructor we set the default values for all the other
// properties, so now the instance is on a valid state
$this->id = $id;
$this->createdAt = new DateTimeImmutable();
$this->updatedAt = new DateTimeImmutable();

$this->collection = new ArrayCollection();
}

Now all properties would have a valid value and the the instance would be on a valid state.

This can hit particularly often when you are relying on values that come from the DB for entity values. E.g. auto-generated IDs, or creation and/or updated values; which often are left as a DB concern.

For auto-generated IDs, the recommended way forward is to change the type declaration to:

private ?int $id = null

For all the rest, just choose an appropriate value for the property's type.

Type hinting in class variables

PHP 7.3 and below does not support typed properties. You could only define a variable as below:

class Sandbox {
private $connection;

However, to help editors understand your code, you may use a @var tag to document the expected type of the property:

class Sandbox {
/** @var Connectors\ISandboxConnector */
private $connection;

Update

PHP 7.4.0

Thanks @Manuel for mentioning the new update, PHP 7.4 now introduces typed properties according to PHP RFC: Typed Properties 2.0.

Property type declarations support all type declarations supported by PHP, with the exception of void and callable. Any class or interface name, stdClass, scalar and compound types, references to parent and own objects are also supported.

class Sandbox {
public int $id;
public string $name;
private Connectors\ISandboxConnector $connection;
}

Note: keep an eye on side effects such as uninitialised state and inheritance strict rules.



Related Topics



Leave a reply



Submit