Home  >  Article  >  Backend Development  >  Typed Properties in PHP 7.4

Typed Properties in PHP 7.4

藏色散人
藏色散人Original
2019-11-30 13:32:114046browse

Type attributes were added in PHP 7.4 and major improvements were made to PHP's type system. These changes are completely optional and do not break previous versions.

In this article we will take a deeper look at this feature, but first let us summarize the most important points:

● They are available since PHP 7.4

●● They are only available within classes and require access modifiers: public, protected, or private; or var.

● All types are allowed except void and callable

their What actually happens is this:

class Foo
{
    public int $a;
    public ?string $b = 'foo';
    private Foo $prop;
    protected static string $static = 'default';
}

#Uninitialized

Before looking at the interesting stuff, there is an important aspect of type properties to discuss first.

Despite what you first see, the following code is valid:

class Foo
{
    public int $bar;
}
$foo = new Foo;

Even if the value of $bar is not an integer, after making an object Foo, PHP will just throw When an error occurs, $bar is accessed:

var_dump($foo->bar);
Fatal error: Uncaught Error: Typed property Foo::$bar 
must not be accessed before initialization

As you can see from the error message, there is a new "variable state": uninitialized.

If $bar has no type, its value will be null. But types can be null, so there's no way to tell if a property of type null was set, or simply forgotten about. That's why "uninitialized" was added.

Four things to remember about uninitialized:

● Uninitialized properties cannot be read, and doing so will result in a fatal error.

● Because uninitialized status is checked when accessing properties, it is possible to create objects with uninitialized properties, even if their type is not nullable.

● You can write to an uninitialized property first and then read it.

● Using unset on a typed property will make it uninitialized, while unsetting an untyped property will make it null.

In particular, note that the following code is valid, in which non-initialized, non-nullable properties are set after constructing the object

class Foo
{
    public int $a;
}
$foo = new Foo;
$foo->a = 1;

Although the uninitialized, non-nullable property is only checked when reading the property value Initialize state, but do type validation when writing property values. This means you can ensure that any invalid type does not end up as a property value.

#Default Values ​​and Constructors

Let’s take a closer look at how to initialize a typed value. For scalar types, a default value can be provided:

class Foo
{
    public int $bar = 4;
    
    public ?string $baz = null;
    
    public array $list = [1, 2, 3];
}

Note that you can only use null as the default value if the type is actually empty. This seems obvious, but there is some old behavior with parameter defaults, which allowed the following:

function passNull(int $i = null)
{ /* … */ }
passNull(null);

Fortunately, type attributes do not allow this confusing behavior.

Also note that object or class types cannot have default values. You should use constructors to set their default values.

The obvious place to initialize a typed value is of course the constructor:

class Foo{
    private int $a;
    public function __construct(int $a)
    {
        $this->a = $a;
    }
}

But also remember what I mentioned earlier: writing uninitialized properties outside the constructor is valid. As long as nothing is read from the property, the uninitialized check is not performed.

#TYPES OF TYPES

So what exactly can be entered and how? I already mentioned that typed properties are only valid within classes (for now), they need An access modifier or the var keyword precedes them.

For available types, almost all types can be used except void and callable.

Because void means no value, it makes sense that it cannot be used for typed values. callable is slightly different.

It can be seen that "callable" in PHP can be written like this:

But please also remember what I mentioned earlier: writing uninitialized properties outside the constructor is valid. As long as nothing is read from the property, the uninitialized check is not performed.

See, a "callable" in PHP can be written like this:

$callable = [$this, 'method'];

Suppose you have the following (invalid) code:

class Foo
{
    public callable $callable;
    
    public function __construct(callable $callable)
    { /* … */ }
}
class Bar
{
    public Foo $foo;
    
    public function __construct()
    {
        $this->foo = new Foo([$this, 'method'])
    }
    
    private function method()
    { /* … */ }
}
$bar = new Bar;
($bar->foo->callable)();

In this example, $callable references private Bar::method, but is called in the context of Foo. Due to this issue, it was decided not to add callable support.

This is no big deal, though, because Closure is a valid type and it will remember the $this context in which it was constructed.

By the way, here is the list of all available types:

● bool

● int

● float

● string

● array

● iterable

● object

● ? (nullable)

● self & parent

● Classes & interfaces

# Forced typing and strict typing

PHP is a dynamic language that we love and hate, it will cast types whenever possible. Assuming you pass a string where an integer is expected, PHP will try to automatically convert the string:

function coerce(int $i)
{ /* … */ }
coerce('1'); // 1

The same principle applies to type attributes.

The code below is valid and converts "1" to 1.

class Bar
{
    public int $i;
}
$bar = new Bar;
$bar->i = '1'; // 1

If you don't like this behavior, you can disable it by declaring strict typing:

declare(strict_types=1);
$bar = new Bar;
$bar->i = '1'; // 1
Fatal error: Uncaught TypeError: 
Typed property Bar::$i must be int, string used

#Type differences and inheritance

Even PHP 7.4 introduced improved type differences, but type properties remain unchanged.

This means the following is not valid:

class A {}
class B extends A {}
class Foo
{
    public A $prop;
}
class Bar extends Foo
{
    public B $prop;
}
Fatal error: Type of Bar::$prop must be A (as in class Foo)

If the above example doesn't seem important, you should look at the following:

class Foo
{
    public self $prop;
}
class Bar extends Foo
{
    public self $prop;
}

Before running the code, PHP will Behind the scenes replaces self with the concrete class it refers to.

这意味着在本例中会抛出相同的错误。处理它的唯一方法,是执行以下操作:

class Foo
{
    public Foo $prop;
}
class Bar extends Foo
{
    public Foo $prop;
}

说到继承,您可能会发现很难找到任何好的用例来覆盖继承属性的类型。

虽然我同意这种观点,但值得注意的是,可以更改继承属性的类型,但前提是访问修饰符也从private更改为protected或public。

以下代码有效:

class Foo{
    private int $prop;
}
class Bar extends Foo
{
    public string $prop;
}

但是,不允许将类型从可为空的类型更改为不可为空或反向的类型。

class Foo
{
    public int $a;
    public ?int $b;
}
class Bar extends Foo
{
    public ?int $a;
    public int $b;
}
Fatal error: Type of Bar::$a must be int (as in class Foo)

翻译:https://stitcher.io/blog/typed-properties-in-php-74

The above is the detailed content of Typed Properties in PHP 7.4. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn