Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Should all functions check their parameter types first?

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?

like image 505
Nick Shears Avatar asked Nov 20 '13 14:11

Nick Shears


People also ask

Does PHP have type checking?

Description ¶ Returns the type of the PHP variable value . For type checking, use is_* functions.

What is default argument in PHP precedence?

The default arguments must be constant expressions. They cannot be variables or function calls. PHP allows you to use a scalar value, an array, and null as the default arguments.

Are functions first class in PHP?

PHP supports first-class functions, meaning that a function can be assigned to a variable. Both user-defined and built-in functions can be referenced by a variable and invoked dynamically.

How can you get the type of arguments passed to a function?

Passing Arguments There are two ways to pass arguments to a function: by reference or by value. Modifying an argument that's passed by reference is reflected globally, but modifying an argument that's passed by value is reflected only inside the function.


1 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

  • 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
like image 128
Chris Baker Avatar answered Nov 15 '22 06:11

Chris Baker