Type determines the way data can be managed in your scripts. You use the string type to display character data, for example, and manipulate such data with string functions. Integers are used in mathematical expressions, booleans are used in test expressions, and so on. these categories are known as primitive types. On a higher level, though, a class defines a type. A ShopProduct object, therefore, belongs to the primitive type object, but it also belongs to the ShopProduct class type. In this section, I will look at types of both kinds in relation to class methods.Method and function definitions do not necessarily require that an argument should be of a particular type. This is both a curse and a blessing. The fact that an argument can be of any type offers you flexibility. You can build methods that respond intelligently to different data types, tailoring functionality to changing circumstances. This flexibility can also cause ambiguity to creep into code when a method body expects an argument to hold one type but gets another.

PHP is a loosely typed language. This means that there is no necessity for a variable to be declared to hold a particular data type. The variable $number could hold the value 2 and the string "two" within the same scope. In strongly typed languages, such as C# or Java, you must declare the type of a variable before assigning a value to it, and, of course, the value must be of the specified type.This does not mean that PHP has no concept of type. Every value that can be assigned to a variable has a type. You can determine the type of a variable’s value using one of PHP’s type-checking functions.

Taking the Hint: Object Types

Just as an argument variable can contain any primitive type, by default it can contain an object of any type. This flexibility has its uses, but can present problems in the context of a method definition. Imagine a method designed to work with a ShopProduct object:

<?php
class ShopProductWriter
{    
    public function write($shopProduct)    
    {        
        $str  = $shopProduct->title . ": ". $shopProduct->getProducer()            . " (" . $shopProduct->price . ")\n";        
        print $str;    
    }
}

You can test this class like this:

<?php
$product1 = new ShopProduct("My Antonia", "Willa", "Cather", 5.99);
$writer = new ShopProductWriter();
$writer->write($product1);

The ShopProductWriter class contains a single method, write(). The write() method accepts a ShopProduct object and uses its properties and methods to construct and print a summary string. I used the name of the argument variable, $shopProduct, as a signal that the method expects a ShopProduct object, but I did not enforce this. That means I could be passed an unexpected object or primitive type and be none the wiser until I begin trying to work with the $shopProduct argument. By that time, my code may already have acted on the assumption that it has been passed a ShopProduct object.

To address this problem, PHP 5 introduced class type declarations (known then as type hints). To add a class type declaration to a method argument, you simply place a class name in front of the method argument you need to constrain. So I can amend the write() method thus:

<?php
    public function write(ShopProduct $shopProduct)
    {        
        // ...    
    }

Now the write() method will only accept the $shopProduct argument if it contains an object of type ShopProduct.

<?php
class Order
{
    // ...
}

$writer = new ShopProductWriter();
$writer->write(new Order());

Because the write() method contains a class type declaration, passing it a order object causes a fatal error


TypeError: Argument 1 passed to ShopProductWriter::write() must be an instance of ShopProduct, instance of Wrong given, called in Runner.php on ...

This saves me from having to test the type of the argument before I work with it. It also makes the method signature much clearer for the client coder. She/He can see the requirements of the write() method at a glance. She/He does not have to worry about some obscure bug arising from a type error because the declaration is rigidly enforced. Even though this automated type checking is a great way of preventing bugs, it is important to understand that type declarations are checked at runtime. This means that a class declaration will only report an error at the moment that an unwanted object is passed to the method.

Armed with scalar type declarations, I can add some constraints to the ShopProductclass:

<?php

class ShopProduct
{    
    public $title;    
    public $producerMainName;    
    public $producerFirstName;    
    public $price = 0;    
    
    public function __construct(        
        string $title,
        string $firstName,        
        string $mainName,        
        float $price    
        ) {
            $this->price = $price;
            $this->title = $title;
            $this->producerMainName = $mainName;        
            $this->producerFirstName = $firstName;

        }
    // ...
}  

With the constructor method shored up in this way, I can be sure that the $title, $firstName, $mainName arguments will always contain string data, and that $price will contain a float. I can demonstrate this by instantiating ShopProduct with the wrong information:

<?php
// will fail
$product = new ShopProduct("title", "first", "main", []);

I attempt to instantiate a ShopProduct object. I pass three strings to the constructor, but I fail at the final one by passing in an empty array instead of the required float. Thanks to type declarations, PHP won’t let me get away with that:


TypeError: Argument 4 passed to ShopProduct::__construct() must be of the type float, array given, called in

By default, PHP will implicitly cast arguments to the required type, where possible. This is an example of the tension between safety and flexibility we encountered earlier. The new implementation of the ShopProduct class, for example, will quietly turn a string into a float for us. So, this instantiation would not fail:

<?php
$product = new ShopProduct("title", "first", "main", "4.22");

Behind the scenes, the string "4.22" becomes the float 4.22

You can make scalar type declarations strict, although only on a file by file basis. Here, I turn on strict type declarations and call outputAddresses() with a string once again:

<?php
declare(strict_types=1);
$manager->outputAddresses("false");

Because I declare strict typing, this call causes a TypeError to be thrown

Note a strict_types declaration applies to the file from which a call is made, and not to the file in which a function or method is implemented. so it’s up to client code to enforce strictness.


This post is part of series:

1 - Object Oriented Programming Concept2 - Classes3 - Objects4 - Methods5 - Constructors6 - Arguments and Types7 - Static Methods and Properties8 - Constant Properties9 - Abstract Classes10 - Interfaces11 - Traits
OOP#OOPPHP#PHP

Comments (0)