Sizzling Code Sizzling Code - 5 months ago 12
PHP Question

Can We Restrict PHP Variables to accept only certain type of values

i am wondering is it possible to restrict php variable to accept only certain type of variable it is defined to accept.

e-g if we see in C#

public int variable = 20;
public string variable2 = "Hello World";


so what would it be like in php if i want to restrict type for variable.

public int $variable = 20;


so that if i try to assign string to integer variable i get the error.

public int $varaible = "Hello World"; //As type is integer, it should throw error.


is there such thing defining types in PHP for variables before assigning values to it??

Answer

TL;DR Not directly, no. PHP is not strictly-typed. There are, however, a few workarounds that may work for you in the context of function parameters or properties of classes.

Long answer: PHP is not a strictly-typed language, but loosely-typed. You can give a variable any value you want, regardless of how it was initialized. So, you can't simply type something like int $myVar and expect $myVar = "foo"; to throw an error. But PHP does offer a few handy features to get you to the same end when dealing with function parameters or properties of a class.

Option 1: Type hints

You can use a "type hint" for function parameters:

class SomeClass 
{
    /* code here */
}

function foo(SomeClass $data)
{
    /* code here */
}

foo() will only accept parameters of type SomeClass. Passing it, say, an int will throw a fatal error. This doesn't work in PHP < 7 if the parameters are intended to be base types, like int, string, etc., so you can't do function foo(int $data) {...}. That said, there are a few libraries out there that attempt to force it to work at the expense of a little speed. Also, PHP 7 adds a lot of support for this kind of thing, as does the Hack language based on PHP.

Pros:

  • Easy
  • Intuitive

Cons:

  • Only works for program-defined classes
  • Unavailable for base types

Option 2: Getters and Setters

You can also use getters and setters, like so:

class SomeClass 
{
    private $foo = 0;

    function setFoo($val = 0)
    {
        // force it to be an int
        if (is_integer($val) {
            $this->foo = $val;
        } else {
            // throw an error, raise an exception, or otherwise respond
        }
    }
}

Pros:

  • Relatively easy
  • Relatively intuitive

Cons:

  • Only works in program-defined classes
  • Unavailable for base types
  • Requires lots of code

Option 3: Magic Methods

This method is my favorite, but also the most complicated. Use the __set() magic method to deal with class properties.

class MyClass {
    private $type = 0; // we will force this to be an int
    private $string = ''; // we will force this to be a string
    private $arr = array(); // we will force this to be an array
    private $percent = 0; // we will force this to be a float in the range 0..100

    function __set($name, $value) {
        switch ($name) {
            case "type":
                $valid = is_integer($value);
                break;
            case "string":
                $valid = is_string($value);
                break;
            case "arr":
                $valid = is_array($value);
                break;
            case "percent":
                $valid = is_float($value) && $value >= 0 && $value <= 100;
                break;
            default:
                $valid = true; // allow all other attempts to set values (or make this false to deny them)
        }

        if ($valid) {
            $this->{$name} = $value;

            // just for demonstration
            echo "pass: Set \$this->$name = ";
            var_dump($value);
        } else {
            // throw an error, raise an exception, or otherwise respond

            // just for demonstration
            echo "FAIL: Cannot set \$this->$name = ";
            var_dump($value);
        }
    }
}

$myObject = new MyClass();
$myObject->type = 1; // okay
$myObject->type = "123"; // fail
$myObject->string = 1; // fail
$myObject->string = "123"; // okay
$myObject->arr = 1; // fail
$myObject->arr = "123"; // fail
$myObject->arr = array("123"); // okay
$myObject->percent = 25.6; // okay
$myObject->percent = "123"; // fail
$myObject->percent = array("123"); // fail
$myObject->percent = 123456; // fail

Pros:

  • Relatively easy
  • Intuitive
  • Extremely powerful: one setter to rule them all

Cons:

  • Only works in program-defined classes
  • Unavailable for base types
  • Requires lots of switching or if/else logic
  • Can cause problems with IDEs not auto-completing property types correctly

Here's a demo of this approach.

Closing Thoughts

Finally, if you're using an IDE like PHPStorm, don't forget about PHPDoc type hints:

/* @var integer */
$foo = 0; // will result in warnings if the IDE is configured properly and you try to do something like substr($foo, 1, 4);

And if you really want to go hard core, you can do strong typing using Hack, at the expense of making your code less portable and less compatible (for now) with major IDEs.

Of course, none of these is a substitute for explicitly validating user input and thoroughly testing the application's response to unexpected input types.