skip to Main Content

If i have an array represented in an ArrayObject like this:

$arrObj = new ArrayObject([
   'foo' => 1,
   'bar' => 2,
]);

Then I can comfortably cast it:

print_r((array)$array);

// output is as expected

But I cannot pass it as an argument that’s typed as an array:

function castTest(array $array)
{
}

castTest($arrObj);

// TypeError: castTest(): Argument #1 ($array) must be of type array, ArrayObject given

Is this intentional? Desirable? Are there any exceptions in PHP that CAN auto-cast objects to arrays?

It works with string objects, for example, with a __toString implemented:

class Str
{
   private string $string;
   public function __construct(string $string)
   {
      $this->string = $string;
   }

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

function castTest(string $string)
{
   echo $string;
}

$str = new Str('hello, world');
castTest($str); // outputs: hello, world

2

Answers


  1. Yes this is intentional. The ArrayObject may behave like an array but isn’t actually an array. PHP enforces strict typing in function arguments. If an argument is defined as an array it only accepts arrays. The behaviour you observed with the __toString method is different because when an object is used in a string context the __toString method is automaticaly called by PHP if it is defined. However, this behavior does not apply to arrays or other non-string types.

    You can convert an ArrayObject to an Array by using getArrayCopy()

    castTest($arrObj->getArrayCopy());
    
    Login or Signup to reply.
  2. This is because it’s a different type and argument type checks are strict. An array is an array type. An ArrayObject is and "object" type (though there is no Object type, it is still a separate type of variable in PHP). This is also because PHP does not have scalar objects. They are objects that represent scalar values behind the scenes. For instance, if you create a string variable, a language with scalar objects would instantiate a String object transparently. This has been suggested to PHP a few times in the past, but not likely to get added soon.

    However, you can allow array objects to be passed along with arrays by altering the argument’s type restriction. PHP 8 introduced union types using the pipe character. You can allow objects able to be accessed like arrays through by adding the ArrayAccess interface:

    $arrObj = new ArrayObject([
       'foo' => 1,
       'bar' => 2,
    ]);
    
    function castTest(array|ArrayAccess $array)
    {
    }
    
    castTest($arrObj); // No problem here
    

    The array|ArrayAccess type restriction means "only allow variables of type array or of object type ArrayAccess".

    If you are using an older version of PHP (7 or earlier), your two options are to cast the object to an array (remember that it will no longer be an object within the function, but a separate array entirely which may have implications depending on how the data is handled), or check for types within the function itself. Here are examples:

    1. Cast object to an array (e.g. make a copy of the object’s values into a new array):
    function castTest(array $array)
    {
    }
    
    castTest((array) $arrObj); // No problem here... ?
    castTest($arrObj->getArrayCopy()); // Same as above, just using a method instead of type casting.
    
    1. Check for type within the function:
    function castTest($array)
    {
        if(!is_array($array) && !($array instanceof ArrayAccess)) {
            throw new InvalidArgumentException(
                'Array or ArrayAccess object type expected, ' .
                (is_object($array) ?
                    ('object of type ' . get_class($array)) :
                    get_type($array)) . ' provided.'
            );
        }
    }
    
    castTest($arrObj); // If something other than an array or object with `ArrayAccess` is passed to the function, an exception is thrown.
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search