Viewed   58 times

I have a lot of functions that either have type hinting for arrays or use is_array() to check the array-ness of a variable.

Now I'm starting to use objects that are iterable. They implement Iterator or IteratorAggregate. Will these be accepted as arrays if they pass through type hinting, or undergo is_array()?

If I have to modify my code, is there a generic sort of is_iterable(), or must I do something like:

if ( is_array($var) OR $var instance_of Iterable OR $var instanceof IteratorAggregate ) { ... }

What other iterable interfaces are out there?

 Answers

1

I think you mean instanceof Iterator, PHP doesn't have an Iterable interface. It does have a Traversable interface though. Iterator and IteratorAggregate both extend Traversable (and AFAIK they are the only ones to do so).

But no, objects implementing Traversable won't pass the is_array() check, nor there is a built-in is_iterable() function. A check you could use is

function is_iterable($var) {
    return (is_array($var) || $var instanceof Traversable);
}

To be clear, all php objects can be iterated with foreach, but only some of them implement Traversable. The presented is_iterable function will therefore not detect all things that foreach can handle.

Tuesday, August 30, 2022
3

It's not included.

If it's not included, do you have Any clue why it was not included when type hinting was added?

With the current array implementation, it would require checking all array elements at runtime, because the array itself contains no type information.

It has actually already been proposed for PHP 5.6 but rejected: RFC "arrayof" - interestingly not because of performance issues which turned out to be neglible, but because there was no agreement in how exactly it should be implemented. There was also the objection that it is incomplete without scalar type hints. If you are interested in the whole discussion, read it in the mailing list archive.

IMHO array type hints would provide most benefit together with typed arrays, and I'd love to see them implemented.

So maybe it's about time for a new RFC and to reopen this discussion.


Partial Workaround:

you can type hint variadic arguments and thus write the signature as

function findUserByAge(int $age, User ...$users) : array

Usage:

findUserByAge(15, ...$userInput);

In this call, the argument $userInput will be "unpacked" into single variables, and in the method itself "packed" back into an array $users. Each item is validated to be of type User. $userInput can also be an iterator, it will be converted to an array.

Unfortunately there is no similar workaround for return types, and you can only use it for the last argument.

Friday, August 19, 2022
 
mmking
 
2

Your problem is that you want to use type hints but you want this class itself to be able to take arguments of its own type.

The type hints PEP (0484) explains that you can use the string version of the type's name as a forward reference. The example there is of a Tree data structure which sounds remarkably similar to this OrgUnit one.

For example, this works:

class OrgUnit(object):

    def __init__(self,
                 an_org_name: str,
                 its_parent_org_unit: 'OrgUnit' = None
                 ):

In Python 3.7, you will be able to activate postponed evaluation of annotations with from __future__ import annotations. This will automatically store annotations as strings instead of evaluating them, so you can do

from __future__ import annotations

class OrgUnit(object):
    def __init__(self,
                 an_org_name: str,
                 its_parent_org_unit: OrgUnit= None
                 ):
        ...

This is scheduled to become the default in Python 4.0.

Saturday, September 10, 2022
 
1

/* @var ClassName $object */ is a non-valid PHPDOC comment and is not parsed in the current version of Web IDE. Use double asterisks to make it work:

/** @var ClassName $object */

Also, you can annotate $array in foreach($array as $var) with /** @var ClassName[] $array */ and $var type will be deduced automatically.

Wednesday, September 28, 2022
 
3

self does not refer to the instance, it refers to the current class. There is no way for an interface to specify that the same instance must be returned - using self in the manner you're attempting would only enforce that the returned instance be of the same class.

That said, return type declarations in PHP must be invariant while what you're attempting is covariant.

Your use of self is equivalent to:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

which is not allowed.


The Return Type Declarations RFC has this to say:

The enforcement of the declared return type during inheritance is invariant; this means that when a sub-type overrides a parent method then the return type of the child must exactly match the parent and may not be omitted. If the parent does not declare a return type then the child is allowed to declare one.

...

This RFC originally proposed covariant return types but was changed to invariant because of a few issues. It is possible to add covariant return types at some point in the future.


For the time being at least the best you can do is:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}
Wednesday, October 19, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :