Tester.php

<?php

namespace Lia\Test;

class Tester extends \Tlf\Tester {

    /**
     * Turn an object with protected methods into an anonymous sub-class with pseudo-public methods. Uses `__call()` on the subclass to make this work.
     *
     * @param $class_name string name of a class with protected methods that does NOT implement `__call()`
     * @param $args args to pass to the constructor
     * @return an anonymous subclass of the object with `__call()` implememnted to call protected methods.
     */
    public function unprotect(string $class_name, ...$args): object {
        //$class = new \ReflectionClass($object);
        //$class_name = $class->getName();
        $sub_class = 'pub_'.uniqid().'_'.str_replace('\\','_',$class_name);
        $sub_class_code = 
            <<<PHP
                class {$sub_class} extends {$class_name} {
                    public function __call(string \$method, array \$args){
                        return \$this->\$method(...\$args);
                    }
                }
            PHP;
        eval($sub_class_code);
    
        $obj = new $sub_class(...$args);
        return $obj;
    }


    /**
     * Call a protected method on an object.
     *
     * @param $object object to invoke the protected method on
     * @param $method string method to call
     * @param ...$args mixed args to pass to the object's method
     *
     * @return mixed return value from calling the method
     */
    public function call_protected(object $object, string $method, ...$args): mixed {
        $class = new \ReflectionClass($object);
        $class_name = $class->getName();
        $ref_method = $class->getMethod($method);
        $ref_method->setAccessible(true);
        return $ref_method->invoke($object,...$args);

    }

}