Reflection: is_a for type scalar hints?

I’m trying to work out whether a function can be called with a given argument. For example:

function foo(string $a, callable $b) {

}

I can get the parameter using ReflectionMethod::getParameters() which gives me an instance of ReflectionNamedType. There’s a getName() method which returns the type of the hint, e.g. string, int, callable, etc.

What I want to do is this:

if ($type->getName() == 'string' && is_string($value)) return true;
else if ($type->getName() == 'int' && is_int($value)) return true;
else if ($type->getName() == 'callable' && is_callable($value)) return true;

Is there a better way to do this? Ideally I’d like to do something like:

return is_a($type->getName(), $value);

but is_a doesn’t accept scalar inbuilt types.

The best I’ve come up with is this:

return call_user_func('is_' . $type->getName(), $value);

but this feels hacky as hell. Is there a better way to do it?

Maybe I am missing the subtle implications of the script…

Would declaring strict_types catch any function variable type parameters and variable type return values?

It will, but I want to check to see whether a function can be called with a specific argument before calling it, rather than calling the function and seeing if there’s an error.

This may be of interest…

I am creating database tables and populating them without an index.The table is populated from a CSV file extracted from a XLS spreadsheet. This works ok and is very quick using Mysql Load data Infile

I then try to create a primary index and call a tryCatch($query) and the function returns either true or false. If false I then try another tryCatch($query) to delete all duplicates including empty index column fields. When this returns true I can then create the primary index.

But this will only work in strict mode. Normally, scalar type hints allow other types if they can be converted losslessly to a certain degree. Trying to mimic the exact PHP rules for what is allowed for what would make your function much more convoluted. To be complete, you’d need to also check for default values and the nullable type hints in PHP 7.1, meaning: this would grow a lot.

I can’t help wondering what is your use case for this kind of function? To me using this in a normal application flow also feels hacky as hell unless you are using it for source code analysis, generation or some other maintenance tasks.

Not much else to say since I don’t know of a better way of detection than what you invented yourself :thinking:

Wasn’t this meant for some other thread in this forum?

I was replying to Tom’s post:

It will, but I want to check to see whether a function can be called with a specific argument before calling it, rather than calling the function and seeing if there’s an error.

I thought that a Try/catch block could be used. When on the computer I did try various parameters but the strict_type caught all the mismatched parameters.

I agree that I cannot think of any way this would be useful especially because the interpretor catches all invalid parameters. Perhaps a more detailed example would be useful.

It’s for my Dependency Injection Container, Dice. For autowiring, it needs to be able to work out which values should be placed in each constructor argument.

You’re right about that approach being strict, I might have to re-write it slightly.

Just curious, how is autowiring supposed to work for scalar types? If a constructor needs an object then it’s more or less straightforward what needs to be injected and the mechanism can construct the object automatically in most cases. But when a constructor requires an int or string then there are virtually unlimited number of values to pass. How is any automatic mechanism supposed to know what to pass? I can’t think of any way other than guessing unless the values are pre-configured somewhere, in which case it’s not _auto_wiring any more…

Have you looked at how the auto wiring works for the php league container?

http://container.thephpleague.com/

Yes, you’re right. In cases like this it’s a hybrid approach. Consider:

public function __construct(\PDO $pdo, int $id)

Dice takes an array of arguments e.g. [777] and maps it to the constructor filling in the missing pieces. In this case it would use the int 777 for the $id argument and auto-wire $pdo. The problem is, with the constructor above, an array of arguments like this: ['foo'] would break. Due to the way dice works, it needs to determine whether the constructor can be called with a given set of argument as sometimes the array won’t match the constructor, in which case the array is ignored. But, I need to work it out.

Still, my call_user_func('is_' . $type->getName(), $value); is a mostly complete solution. I just wish there was a nicer way.

Try/catch might work, but I don’t really want to call the constructor until I’m sure it will work.

To me this sounds like silent error suppression - if you supply string instead of the (hinted) array then I would expect the application to break with an error message, isn’t that the purpose of type hints? If you say when the argument doesn’t match the constructor then it is ignored - what benefit does it offer? Maybe I don’t understand the reasoning behind this mechanism but I think it would be better to have the DIC attempt to call the constructor and allow a potential fatal error rather than check if the scalar types match.

PHP doesn’t support method overloading so it’s not like you can look for another constructor if the signature of the one you are inspecting doesn’t match the supplied arguments…

Perhaps in some cases, but the problem is that Dice is getting information from two different places. When you create an instance you can do this:

$args = [123];
$dice->create('Foo', $args);

If the constructor of Foo is this:

public function __construct(\PDO $pdo, int $id)

Dice needs to work out which argument 123 should be supplied for. Any that aren’t in the $args array (and are class type hints) are auto-wired. Without getting too much into the internals of Dice, in some situations, the $args array can contain more values than there are arguments in the class.

Normally this isn’t an issue, but when the $args array contains, for example, a \DateTime instance and the constructor looks like this:

public function __construct(\PDO $pdo, int $id = null)

If it’s not nullable, it’s fine, Dice can try to pass the DateTime instance to the class and let PHP handle the error as normal. But if it’s nullable, Dice needs to check to see if there is a valid argument, otherwise pass null.

So as I see it, it is a kind of independence from the fixed order of arguments, to some extent at least. If there is no 1-to-1 mapping between the supplied $args and the arguments in the constructor then things are getting complicated and indeed you need to do those kind of checks. A bit magical behaviour, I’d say. But thanks for explaining.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.