Žilvinas Ringelė
Žilvinas Ringelė

Reputation: 104

Type hinting ClassName in php

I would like to type hint class name in method parameter, in this code:

    public function addScore($scoreClassName): self
    {
        $this->score = new $scoreClassName($this->score);
        return $this;
    }

$scoreClassName should be class name of a class which implements certain Interface. Something like:

    public function addScore(CalculatesScore::class $scoreClassName): self
    {
        $this->score = new $scoreClassName($this->score);
        return $this;
    }

Is there any way to do it? If not, could you suggest a workaround?

EDIT: Best solution i found so far to my question

    public function addScore(string $scoreClassName): self
    {
        $implementedInterfaces = class_implements($scoreClassName);
        if (!in_array(CalculatesScore::class, $implementedInterfaces)) 
        {
            throw new \TypeError($this->getTypeErrorMessage($scoreClassName));
        }
        $this->score = new $scoreClassName($this->score);

        return $this;
    }

Upvotes: 5

Views: 4588

Answers (2)

Cave Johnson
Cave Johnson

Reputation: 6788

If you want to type hint a class string solely for the purpose of passing that information to the IDE (to get usage search and autocomplete to work), and your IDE uses Psalm, you can use the class-string type hint in a docblock comment. So for your code it would look like this:

/**
 * @param class-string<CalculatesScore> $scoreClassName
 * @return $this
 */
public function addScore($scoreClassName): self
{
    $this->score = new $scoreClassName($this->score);
    return $this;
}

Upvotes: 3

GordonM
GordonM

Reputation: 31780

You can't type-hint on a specific string. You can, however, set a default string value.

To ensure you've instantiated a class of the correct type you'd have to do a check in the method body itself.

public function addScore(string $scoreClassName = CalculatesScore::class): self
{
    $score = new $scoreClassName($this->score);
    if (!$score instanceof CalculatesScore) 
    {
        throw new InvalidArgumentException("Invalid class score type: $scoreClassName");
    }
    $this->score = $score;
    return $this;
}

I'd argue that this isn't really the right way to do it, you should probably be using dependency injection instead, and instantiate the score class prior to passing it to this class. It makes the dependency more explicit and simpler to control. The actual code is also a lot simpler.

public function addScore(CalculatesScore $scoreClass): self
{
    $this->score = $scoreClass;
    return $this;
}

Upvotes: 3

Related Questions