PHP Type-Hinting to Primitive Values

PHP type-hinting to primitive values?

In PHP 7 they added the following:

Type declarations allow functions to require that parameters are of a certain type at call time. If the given value is of the incorrect type, then an error is generated: in PHP 5, this will be a recoverable fatal error, while PHP 7 will throw a TypeError exception.

Reference:
http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration


When this answer was asked, PHP 5 was the latest and said the following:

PHP 5 introduces type hinting. Functions are now able to force parameters to be objects (by specifying the name of the class in the function prototype), interfaces, arrays (since PHP 5.1) or callable (since PHP 5.4). However, if NULL is used as the default parameter value, it will be allowed as an argument for any later call.

If class or interface is specified as type hint then all its children or implementations are allowed too.

Type hints cannot be used with scalar types such as int or string. Resources and Traits are not allowed either.

Reference: http://php.net/manual/en/language.oop5.typehinting.php

Why is traditional type-hinting not allowed in PHP?

PHP's loosely typed, where your "primitive" types are automatically type-juggled based on the context in which they're used. Type-hinting wouldn't really change that, since a string could be used as an int, or vice versa. Type-hinting would only really be helpful for complex types like arrays and objects, which can't be cleanly juggled as ints, strings, or other primitives.

To put it another way, since PHP has no concept of specific types, you couldn't require an int somewhere because it doesn't know what an int really is. On the other hand, an object is of a certain type, since a MyClass is not interchangeable with a MyOtherClass.


Just for reference, here's what happens when you try to convert between such types (not an exhaustive list):

Converting to Object (ref)

"If an object is converted to an object, it is not modified. If a value of any other type is converted to an object, a new instance of the stdClass built-in class is created. If the value was NULL, the new instance will be empty. Arrays convert to an object with properties named by keys, and corresponding values. For any other value, a member variable named scalar will contain the value."

Object to int/float (ref)

undefined behavior

Object to boolean (ref)

in PHP5, always TRUE

Object to string (ref)

The object's __toString() magic method will be called, if applicable.

Object to array (ref)

"If an object is converted to an array, the result is an array whose elements are the object's properties. The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible; private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name. These prepended values have null bytes on either side. This can result in some unexpected behaviour."

Array to int/float (ref)

undefined behavior

Array to boolean (ref)

If the array is empty (i.e., no elements), it evaluates to FALSE -- otherwise, TRUE.

Array to string (ref)

the string "Array"; use print_r() or var_dump() to print the contents of an array

Array to object (ref)

"Arrays convert to an object with properties named by keys, and corresponding values."

How do typehint a parameter of a function in php

In modern PHP you can either provide a list of all possible types:

// Tweak type list your exact needs
public function getValue(bool $typed = false): bool|DateTime|null

... or use mixed if the method can indeed return anything:

public function getValue(bool $typed = false): mixed

In older versions, you can only use the @return tag in the docblock:

/**
* @param bool $typed
* @return mixed
* @throws Exception
*/

I understand PHPStan will be happy with all options.

Parent object type hinting in PHP

Is this possible?

Yes, what you've descrived is exactly what type hinting is for. Here is the documentation.

How to specify the type of the parameter in PHP

There are no direct scalar type hint possibilities PHP. But you can emulate it by checking the type inside the function and trigger an appropriate error:

function retrieveCount( $count )
{
if( !is_int( $count ) )
{
// I believe E_USER_WARNING is the appropriate error level
// equivalent to what PHP issues itself on type hint errors
trigger_error(
'Argument 1 passed to retrieveCount() must be an integer',
E_USER_WARNING
);
}

return ++$count;
}

Use wrapper classes like in JAVA, when type hinting

Since PHP 5, PHP allows type hinting using classes (forcing a function/method's parameter to be an instance of a classs).

So you can create an int class that takes a PHP integer in constructor (or parsing an integer if you allow a string containing an integer, such as in the example below), and expect it in a function's parameter.

The int class

<?php

class int
{

protected $value;

public function __construct($value)
{
if (!preg_match("/^(0|[-]?[1-9][0-9])*$/", "$value"))
{
throw new \Exception("{$value} is not a valid integer.");
}
$this->value = $value;
}

public function __toString()
{
return '' . $this->value;
}

}

Demo

$test = new int(42);
myFunc($test);

function myFunc(int $a) {
echo "The number is: $a\n";
}

Result

KolyMac:test ninsuo$ php types.php 
The number is: 42
KolyMac:test ninsuo$

But you should be careful about side effects.

Your int instance will evaluate to true if you're using it inside an expression (such as, $test + 1), instead of 42 in our case. You should use the "$test" + 1 expression to get 43, as the __toString is only called when trying to cast your object to a string.

Note: you don't need to wrap the array type, as you can natively type-hint it on function/method's parameters.

The float class

<?php

class float
{

protected $value;

public function __construct($value)
{
if (!preg_match("/^(0|[-]?[1-9][0-9]*[\.]?[0-9]*)$/", "$value"))
{
throw new \Exception("{$value} is not a valid floating number.");
}
$this->value = $value;
}

public function __toString()
{
return $this->value;
}

}

The string class

<?php

class string
{

protected $value;

public function __construct($value)
{
if (is_array($value) || is_resource($value) || (is_object($value) && (!method_exists($value, '__toString'))))
{
throw new \Exception("{$value} is not a valid string or can't be converted to string.");
}
$this->value = $value;
}

public function __toString()
{
return $this->value;
}

}

The bool class

class bool
{

protected $value;

public function __construct($value)
{
if (!strcasecmp('true', $value) && !strcasecmp('false', $value)
&& !in_array($value, array(0, 1, false, true)))
{
throw new \Exception("{$value} is not a valid boolean.");
}
$this->value = $value;
}

public function __toString()
{
return $this->value;
}

}

The object class

class object
{

protected $value;

public function __construct($value)
{
if (!is_object($value))
{
throw new \Exception("{$value} is not a valid object.");
}
$this->value = $value;
}

public function __toString()
{
return $this->value; // your object itself should implement __tostring`
}

}

PHP Should all functions check their parameter types first?

You can see this is a somewhat debatable topic. This is my take:

Type Hinting

Use type hinting when possible. Type hints are not possible in PHP for primitive types, so yes, you should check to ensure you've received valid arguments. If you have not, your function can throw an Exception or return some default value like null or false.

Defensive Programming

The idea of writing testable code is that failures are not silent or mysterious. There's no reason to avoid explicit argument validation: be verbose and your code is more clear and usable.

On top of validating your arguments, you can implement an error handler to catch the edge cases. But you should be validating most arguments, especially if they have an effect on persistent data (like your database).

Murphy's Law is in full effect, therefore you must contend with as many predictable bugs as you can. An invalid argument is an easily predictable bug -- failure to validate it is a timebomb in your code. Calling is_string, for example, is easy and diffuses the bomb.

Boxing

Another consideration is to "box" your variables. This leads to very verbose code, but it does have the advantage of allowing type hints for primitives.

I've never seen anyone actually do this though their entire codebase, but it is out there. There are SPL classes available for primative types, so you'd wind up like this:

function stringThing (\SplString $myString) { ... }

stringThing(new \SplString('This is my string'));

SplTypes enforce the primitive type and throw exceptions when it is misused. From the documentation:

$string = new SplString("Testing");
try {
$string = array(); // <----------this will throw an exception
} catch (UnexpectedValueException $uve) {
echo $uve->getMessage() . PHP_EOL;
}

SplTypes is a PECL extension, and not always a part of a standard PHP install, so check your extensions before using it. The extension is also considered experimental, though it has been in around for some time now.

You can also create your own box fairly simply:

class myStringBox {
private $string = '';
public function __construct($string=null) {
if ($string)
$this->set($string);
}
public function set($val) {
if (!is_string($string)) throw new \InvalidArgumentException();
$this->string= $val;
}
public function __toString() {
return $this->string;
}
public function trim() { return trim($this->string); } // extend with functions?
}

... but this has a major functional difference in that you cannot directly set a new string value like this:

$stringBox = new myStringBox('hello world! ');
echo $stringBox; // "hello world![space]"
echo $stringBox->trim(); // "hello world!"

$stringBox = 'A new string';
echo $stringBox->trim(); // Error: Call to a member function trim() on a non-object

Instead, you have to use a setter method:

$stringBox = new myStringBox('hello world! ');
echo $stringBox; // "hello world![space]"
echo $stringBox->trim(); // "hello world!"

$stringBox->set('A new world');
echo $stringBox->trim(); // "A new world"

This all leads us back to type hinting, which is probably the most efficient way to NOT have to validate your arguments.

Related Reading

  • Spl types - http://www.php.net/manual/en/book.spl-types.php
  • Type hinting in PHP - http://php.net/manual/en/language.oop5.typehinting.php
  • Defensive programming - http://en.wikipedia.org/wiki/Defensive_programming

is there a double type in PHP

Update:

Regarding type hinting:

Type Hints can only be of the object and array (since PHP 5.1) type. Traditional type hinting with int and string isn't supported.

So I don't know, but probably you get the error because only array and object types are supported.


I am not exactly sure what you want, but there is only float:

Floating point numbers (also known as "floats", "doubles", or "real numbers") can be specified using any of the following syntaxes:

<?php
$a = 1.234;
$b = 1.2e3;
$c = 7E-10;
?>

and there you find also:

Converting to float

For information on converting strings
to float , see String conversion to numbers. For values of other types,
the conversion is performed by
converting the value to integer first
and then to float . See Converting to integer for more information. As of
PHP 5, a notice is thrown if an object
is converted to float .

Typehinting for filehandle as argument in function

Unfortunately, you cannot use the primitive type resource as a type hint. The latest type hint system change to PHP, scalar type hints, only added int, float, string, and bool.

The error you quote has come up before as confusing, especially in the context of the scalar type hints. Let me template it for you:

Argument [N] passed to [Function] must be an instance of [Class], [Type] given

The confusion arises because PHP allows classes to have the same name as documented primitives (bool, float, etc.) for all primitives before PHP 7, and for some in PHP 7 and later
. So when you say stream $handle, PHP is expecting $handle to be of class stream. Likewise resource $handle expects $handle to be of class resource.

If you want to type hint resources, I suggest using an \SplFileObject:

$handle = new \SplFileObject('myfile.csv', 'r');
function parseHandle(\SplFileObject $handle) { ... }

This is not the best thing in the world, as \SplFileObject has a few quirks, but at the end of the day, if you want to type hint it in PHP, you must either have an array, a scalar, or a class.



Related Topics



Leave a reply



Submit