Home » Php » PHP Should all functions check their parameter types first?

PHP Should all functions check their parameter types first?

Posted by: admin July 12, 2020 Leave a comment

Questions:

I’ve built up a collection of string functions that do various things and I’ve noticed that I don’t actually have any internal function checks that make sure the variable is a string before something is done with it.

So on a few occasions during development I’ve accidentally passed something other than a string resulting in an error.

Now, I’m wondering if this is this something I should be doing all the time. First checking to make sure the correct type of data has been sent/check for things that could potentially go wrong first, log them in some way, then if all is okay, do something with it.

Is this something I should be sticking to?

How to&Answers:

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

Answer:

Currently, PHP doesn’t have type hinting for scalars, this was planned for PHP 5.4 but was removed due to the dynamic nature of PHP variables.

A PHP RFC request is now open and it’s a planned feature for PHP 6
https://wiki.php.net/rfc/scalar_type_hinting_with_cast

Some workarounds are available:

The set_error_handler method:

Described at: http://edorian.github.io/2010-03-30-typehints-hack-for-literal-values-in-php/

function typehint($level, $message) {
    if($level == E_RECOVERABLE_ERROR && preg_match(
        '/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/', $message, $match)
    ) { 
        if($match[4] == $match[5]) {
            return true;
        }
    } 
    return false;
}
set_error_handler("typehint");

PHPTypeSafe method

Another workaround is PHPTypeSafe. This is an old library for 5.3, but a definite slowdown on the PHP execution:

class Foo
{
    public static function bar(string $msg, int $counter)
    {
        echo "$counter. run: $msg\n\n";
    }
}

$postCount = 0;

Foo::bar('test', ++$postCount);

echo "The next run will result in an an ErrorException: \n\n";

Foo::bar(false, ++$postCount);

You can read the full example at:
https://github.com/max-horvath/PHPTypeSafe/blob/master/src/examples/php/com/maxhorvath/phptypesafe/SimpleExample.php


SPL Types method

This is the most standard and recomended method, the problem is that you must wrap your variables with objects. This is part of the SPL extension: http://www.php.net/manual/en/book.spl-types.php

This is a little example:

function myFunction(SplInt $int) {
    //foo
}

$int = new SplInt(94);
myFunction($int);

You can find a more extensive info about SPL Types at Chris answer


For the sake of completeness: Some time ago, Ms. Sara Golemon, one of the best PHP contributors, tried to make autoboxing for scalar variables similar to SPL Types. It’s a little extension in a very alpha state:

https://github.com/sgolemon/objectifier

Regards

Answer:

In the absense of real type hinting for scalar variables, you can use doc-comments for it. If you use a decent IDE, you’ll get reasonable type warnings from the IDE; even if PHP itself doesn’t enforce them, that’s usually enough to stop you making any howlers.

For example:

/**
 * @param int custID
 */
function loadCustomer($custID) {
    ....
}

Given the above, a decent IDE will give you code completion/type hints for the argument as if it was constrained as an integer. It won’t completely stop you from passing a string, by mistake, but if it knows that you’re passing the wrong type, it will warn you.

Answer:

No, except for edge cases, you do not.

I suggest you use http://php.net/manual/en/class.errorexception.php instead of the default PHP error handler. ErrorException will stop script execution even in case of notice.

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
set_error_handler("exception_error_handler");

I prefer this approach because it allows to catch all unxepected cases in the long term and write the appropriate condition to handle them.

Answer:

I’d suggest accepting all user input into your functions and casting the parameters to strings (to be sure) wherever an error would occur otherwise. There’s really no reason to not, for example, call one of the functions with an int and have it treated using its string representation.

function foo($bar) {
  $str = (string)$bar;
  return strlen($str);
}

This will even work on classes that implement the __toString() method..

Answer:

use is_string() validation for function params