Search code examples
phpcastingintegeroctal

Reliable integer typecasting


I'm working on a data validator package for PHP (mostly as an exercise in some of the new additions in 5.3 such as namespaces). I'm planning to have a main validator class which uses plugin classes to do the actual validation.

The first validation plugin I'm building is just a simple one for integers. The source is below:

namespace validator\validatorplugins;

class Integer implements ValidatorPlugin
{
    private
        $field  = NULL;

    public function cast ()
    {
        return ($this -> field  = (int) ($this -> field * 1));
    }

    public function isValid ()
    {
        if (!$isValid = is_int ($this -> field * 1))
        {
            $this -> field  = NULL;
        }
        return ($isValid);
    }

    public function __construct ($field)
    {
        $this -> field  = $field;
    }

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

I'm using the following tests of the above class:

$a  = new Integer (0xDEADBEEF);
var_dump ($a -> isValid ());
var_dump ($a -> cast ());
echo ($a . "\n\n");

$b  = new Integer ('0xDEADBEEF');
var_dump ($b -> isValid ());
var_dump ($b -> cast ());
echo ($b . "\n\n");

$c  = new Integer (0777);
var_dump ($c -> isValid ());
var_dump ($c -> cast ());
echo ($c . "\n\n");

$d  = new Integer ('0777');
var_dump ($d -> isValid ());
var_dump ($d -> cast ());
echo ($d . "\n\n");

From the above test cases I'm getting the following results:

bool(true) int(3735928559) 3735928559

bool(true) int(3735928559) 3735928559

bool(true) int(511) 511

bool(true) int(777) 777

As you can see, I've run into an issue with a string that encodes an octal number. It should evaluate to 511 (0777 in base 10), but I'm actually getting 777 instead.

As I might one day want to use this class to validate forms, and as forms are always treated as arrays of strings by PHP, you can see that processing octal numbers that happen to be strings with this plugin is problematic.

I've tried various approaches to integer validation (using is_int always returns false for strings, multiplying by 1 and is_numeric will not catch floats, typecasting and intval produce an unwanted result when dealing with octal numbers). Is there some other approach I could use that would work for base 10 (decimal), base 16 (hex) and base 8 (octal)?


Solution

  • intval has an optional argument $base:

    intval('0777', 8); // 511
    intval('0777', 10); // 777
    intval('0777'); // 777
    intval(0777); // 511
    

    So you could do something like this:

    $i = '0777';
    
    $base = 10;
    if (strtolower(substr($i, 0, 2)) === '0x') $base = 16;
    else if (substr($i, 0, 1) === '0') $base = 8;
    
    intval($i, $base);