<?php

declare(strict_types=1);

namespace GroundLevel\Testing\Traits;

use ReflectionClass;
use ReflectionMethod;

/**
 * Class trait with utility functions to enable calling private/protected methods.
 */
trait InvokesMethods
{
    /**
     * Invokes a protected/private method with the given arguments.
     *
     * @param  string|object $object     An instance of the class or the class name.
     * @param  string        $methodName The name of the method.
     * @param  array         $args       Indexed array of arguments to be used
     *                                  when invoking the method.
     * @return mixed Returns the return value of the invoked method.
     */
    protected function invoke($object, string $methodName, array $args = [])
    {
        $method = $this->getMethod(
            is_string($object) ? $object : get_class($object),
            $methodName
        );
        return $method->invokeArgs(
            $method->isStatic() ? null : $object,
            $args
        );
    }

    /**
     * Retrieves an accessible method for the given private/protected method.
     *
     * @param  string $className  The name of the class.
     * @param  string $methodName The name of the method.
     * @return \ReflectionMethod
     */
    protected function getMethod(string $className, string $methodName): ReflectionMethod
    {
        $class  = new ReflectionClass($className);
        $method = $class->getMethod($methodName);
        $method->setAccessible(true);
        return $method;
    }
}
