Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 219
PerfilExample
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 18
7310.00
0.00% covered (danger)
0.00%
0 / 219
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 7
 getFileSystemControllerPath
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 getFileSystemCodePath
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 getControllerURL
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 getCodeURL
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 makeExampleSubDir
0.00% covered (danger)
0.00%
0 / 1
56.00
0.00% covered (danger)
0.00%
0 / 7
 directoryPath
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 3
 getFileName
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 endingSlash
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 PerfilExample_class
0.00% covered (danger)
0.00%
0 / 1
992.00
0.00% covered (danger)
0.00%
0 / 91
 callStrings
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 32
 parametersCall
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 8
 parameterValue
0.00% covered (danger)
0.00%
0 / 1
90.00
0.00% covered (danger)
0.00%
0 / 17
 getControllerCode
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 5
 saveFileIfNotExists
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 testTemplate
0.00% covered (danger)
0.00%
0 / 1
156.00
0.00% covered (danger)
0.00%
0 / 26
 testMethodTemplate
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 testClassHeader
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
<?php
namespace ia\DocumentIt;
//@EXPAND pre-fill module, functions in file, may be file with no functions nor vars nor defines ie has a class, trait, interface
//@EXPAND a completa xxx_example_code.php nota ver <li id="method_name"> de no estar append it
//@MEJORA cuando un parĂ¡metro tiene un default usar el default o no ponerlo en la llamada
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionMethod;
class PerfilExample {
    private $className;
    private $classVariable;
    private $examplesDir;
    private $examplesHome;
    private $exampleControllerFileName;
    private $exampleCodeFileName;
    private $shortName;
    /**
     * PerfilExample constructor.
     * @param string $className
     * @param string $exampleDir
     * @param string $exampleHome
     */
    public function __construct($className, $exampleDir, $exampleHome) {
        $this->className = $className;
        $this->examplesDir = $this->endingSlash($exampleDir);
        $this->examplesHome = $this->endingSlash($exampleHome);
        $this->exampleControllerFileName = $this->getFileName('', $className, '_example.php');
        $this->exampleCodeFileName = $this->getFileName('code/', $className, '_example_code.php');
        $this->makeExampleSubDir();
    }
    public function getFileSystemControllerPath() {
        return $this->examplesDir . $this->exampleControllerFileName;
    }
    public function getFileSystemCodePath() {
        return $this->examplesDir . $this->exampleCodeFileName;
    }
    private function getControllerURL() {
        return $this->examplesHome . $this->exampleControllerFileName;
    }
    private function getCodeURL() {
        return $this->examplesHome . $this->exampleCodeFileName;
    }
    private function makeExampleSubDir() {
        $controllerSubDir = $this->directoryPath($this->getFileSystemControllerPath());
        if($controllerSubDir !== '' && $controllerSubDir !== '/' && !file_exists($controllerSubDir)) {
            mkdir($controllerSubDir, 0755, true);
        }
        $codeSubDir = $this->directoryPath($this->getFileSystemCodePath());
        if($codeSubDir !== '' && $codeSubDir !== '/' && !file_exists($codeSubDir)) {
            mkdir($codeSubDir, 0755, true);
        }
    }
    public function directoryPath($fileName) {
        $path = explode('/', $fileName);
        array_pop($path);
        return implode('/', $path);
    }
    private function getFileName($prefix, $className, $suffix) {
        if(substr($className,0,1) === '/') {
            $className = substr($className, 1);
        }
        return $prefix . str_replace('\\', '/', $className).$suffix;
    }
    /**
     * Ensure path ends with /
     * @param string $path
     * @return string
     */
    private function endingSlash($path) {
        if(substr($path, -1) !== '/') {
            $path .= '/';
        }
        return $path;
    }
    /**
     * @return string
     */
    public function PerfilExample_class() {
        if(empty($this->className) || $this->className[0] === '/' || $this->className[0] === '\\') {
            return "Invalid namespace\\className";
        }
        if(stripos($this->className, '/') || stripos($this->className, '.\\')) {
            return "Invalid namespace\\className  / ./ and .\\  not allowed";
        }
        try {
            $ref = new ReflectionClass($this->className);
        } catch (ReflectionException $e) {
            return "\Reflection error, invalid \$className = $this->className";
        }
        $this->shortName = $ref->getShortName();
        $this->classVariable = lcFirst($ref->getShortName());
        $namespace = $ref->getNamespaceName();
        if(empty($namespace)) {
            $namespace = '';
        }
        $controllerCode =  $this->getControllerCode();
        $this->saveFileIfNotExists($this->getFileSystemControllerPath(), $controllerCode);
        $construct = [
            'parametersList' => '',
            'callPrototype' => '',
            'callCommentOut' =>'',
            'call' => '',
            'callEcho' => '',
            'returnType' => '',
            'parametersPrototype' => '',
            'parametersValues' => [],
            'createNew' => true,
            'docBlock' => '',
        ];
        $ret = "\r\n";
        $instantiateClass = false;
        foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            $functionDocBlock = $method->getDocComment();
            $callers =  $this->callStrings($method, $functionDocBlock);
            if($method->getName() === '__construct') {
                $construct = $callers;
                $construct['docBlock'] = $functionDocBlock;
                $instantiateClass = true;
                continue;
            }
            if(!$method->isStatic()) {
                $instantiateClass = true;
            }
            switch($callers['returnType']) {
                case 'int':
                case 'string':
                case 'float':
                case 'float|string':
                case 'string|float':
                case 'int|string':
                case 'string|int':
                case 'int|float':
                case 'float|int':
                case 'string|bool':
                case 'bool|string':
                    $example = <<< METHODEXAMPLE
    /// display ///
    echo <<< DOCUMENT_EXAMPLE
        <li>$callers[callEcho] -&gt;&nbsp;
DOCUMENT_EXAMPLE;
    /// run ///
        $callers[callCommentOut] echo $callers[call];
METHODEXAMPLE;
                    break;
                case 'bool':
                    $example = <<< METHODEXAMPLE
    /// display ///
    echo <<< DOCUMENT_EXAMPLE
        <li>$callers[callEcho] -&gt;&nbsp;
DOCUMENT_EXAMPLE;
    ///run ///
    $callers[callCommentOut] echo ($callers[call] ? 'true' : 'false');
METHODEXAMPLE;
                    break;
                case 'void':
    $example = <<< METHODEXAMPLE
    /// display ///
    echo <<< DOCUMENT_EXAMPLE
        <li>$callers[callEcho]"
DOCUMENT_EXAMPLE;
    /// run ///
        $callers[callCommentOut] echo $callers[call];
METHODEXAMPLE;
                    break;
             case 'Generator':
             case '\Generator':
$example = <<< METHODEXAMPLE
    /// display ///
    echo <<< DOCUMENT_EXAMPLE
        <li>echo "&lt;ul&gt;";
            foreach($callers[callEcho] as \\\$k => \\\$v)
                echo "&lt;li&gt;\\\$k := ". print_r(\\\$v, true)."&lt;/li&gt;";
            echo "/&lt;ul&gt;";
DOCUMENT_EXAMPLE;
    /// run ///
    echo "<ul>"; foreach($callers[call] as \$k => \$v) echo "<li>\$k :=  ". print_r(\$v, true)."</li>"; echo "</ul>";
METHODEXAMPLE;
                    break;
             default:
                if(empty($callCommentOut)) {
$example = <<< METHODEXAMPLE
    /// display ///
    echo <<< DOCUMENT_EXAMPLE
        <li>$callers[callEcho]
        -&gt;&nbsp; </b><br><pre>
DOCUMENT_EXAMPLE;
    /// run ///
        print_r(
        $callers[call]
        );
        echo "</pre>";
METHODEXAMPLE;
                    } else {
$example = <<< METHODEXAMPLE
    /// display ///
    echo <<< DOCUMENT_EXAMPLE
        <li>$callers[callEcho]"
            -&gt;&nbsp; </b><br><pre>
DOCUMENT_EXAMPLE;
    /// run ///
        // print_r($callers[call]);
        echo "</pre>";
METHODEXAMPLE;
                }
            }
            $prototype = <<< PROTOTYPEXAMPLE
///////// $callers[callEcho] //////////////////////////////////////////////////
echo <<< FUNCTIONDEF
<li id='u_$method->name'><b>$callers[callPrototype]</b>
FUNCTIONDEF;
echo "<pre class='docBlock'>".DocumentIt::methodDocBlockProtected("$this->className", "$method->name")."</pre>";
echo "<ol class='usage'>";
PROTOTYPEXAMPLE;
            $ret .=  "\r\n$prototype\r\n$example\r\n\techo '</ol>';\r\n";
        }
        $ret .=  "\r\n\r\n/* PerfilExample adds new methods here */\r\n\r\necho '</ul>';";
        $date = date('Y-m');
        $header = "
/**
 *    Usage examples for $this->className
 *    @version 1.0
 *    @date $date
*/\r\n\r\nuse ia\\DocumentIt\\DocumentIt;\r\n";
        $shortName = $ref->getShortName();
        if(!empty($namespace)) {
            $header = "use $namespace\\$shortName;\r\n";
        }
        $header .= "use ia\DocumentIt\DocumentIt;\r\n";
        $header .=  "\r\necho '<h3>Disclaimer: Auto generated file.
                Please help us setting parameters to useful values and extending the examples.
        </h3>';\r\n\r\ntry {\r\n\r\n";
        if($instantiateClass) {
            if (!empty($construct['createNew'])) {
                $docBlock = str_replace('$', "\\$", $construct['docBlock']);
                $proto = "\\$" . $this->classVariable . " = new $shortName($construct[parametersPrototype]);";
                $parametersValues = empty($construct['parametersValues']) ? '' : implode(', ', $construct['parametersValues']);
                $showCreate = "\\$" . $this->classVariable . " = new $shortName($parametersValues);";
                $header .= <<< PROTOTYPE
            echo <<< CONSTRUCTOR_PROTOTYPE
            <pre>
$docBlock
/*
 $proto
*/
$showCreate
</pre>
CONSTRUCTOR_PROTOTYPE;
PROTOTYPE;
                $header .= "\r\n" . '$' . $this->classVariable . " = new $shortName($parametersValues);\r\n\r\n";
            }
        }
        $header .=  "\r\necho '<ul class=\"usage\">';\r\n";
        $code = $header . $ret . "\r\n\r\n" .
            '} catch(Exception $exception) { echo "<pre class=\'errorBlock\'>$exception</pre>"; }'."\r\n";
        $this->saveFileIfNotExists($this->getFileSystemCodePath(), $code);
        return
            "<hr><h2 id='controler' class='fileName'>" . $this->getControllerURL() .
            "</h2><hr><code style='margin:2em'>" . htmlentities("<?php\r\n".$controllerCode)."</code>" .
            "<hr><h2 id='byFunction' class='fileName'>" . $this->getCodeURL() . "</h2><hr><code style='margin:2em'>".
            htmlentities($code)."</code>";
    }
    /**
     * @param ReflectionMethod|ReflectionFunction $method
     * @param string $functionDocBlock
     * @return array
     */
    private function callStrings($method, $functionDocBlock) {
        $variables = [];
        foreach($method->getParameters() as  $p) {
            $variables[] = '$'.$p->getName()    ;
        }
        $parametersList = implode(', ', $variables);
        $parameters = DocumentIt::parameters($method, $functionDocBlock);
        $parametersPrototype = str_replace(['$'], ["\\$"], implode(', ', $parameters) );
        $parametersValues =  $this->parametersCall($method, $functionDocBlock);
        $returnType = $method->hasReturnType() ? $method->getReturnType() :
            DocumentIt::deduceReturnTypeFromDockBlock($functionDocBlock);
        if($method->isStatic()) {
            $callWithVariables = $call = $callPrototype = $callEcho = "$this->shortName::".$method->name;
            $createNew = false;
        } else {
            $callWithVariables = $call = '$'.$this->classVariable . '->' . $method->name;
            $callPrototype = $callEcho = str_replace('$', "\\$", $call);
            $createNew = true;
        }
        $callPrototype .= "($parametersPrototype) <span>: $returnType</span>";
        $call .= "(" . implode(', ', $parametersValues).')';
        $callWithVariables .="($parametersList)";
        $callEcho .= str_replace(['$'], ["\\$"], "(" . implode(', ', $parametersValues).');');
        return [
            'parameterCount' => count($variables),
            'callWithVariables' => $callWithVariables,
            'parametersList' => $parametersList,
            'callPrototype' => $callPrototype,
            'callCommentOut' => strpos($callPrototype, '&') === false ? '' : '// ',
            'call' => $call,
            'callEcho' => $callEcho,
            'returnType' => $returnType,
            'parametersPrototype' => $parametersPrototype,
            'parametersValues' => $parametersValues,
            'createNew' => $createNew,
            'docBlock' => $functionDocBlock,
            'parameters' => $parameters,
        ];
    }
    private function parametersCall($method, $functionDocBlock) {
        $parametersCall = [];
        try {
            foreach (DocumentIt::parametersInfo($method, $functionDocBlock) as $p) {
                if ($p['isOptional']) {
                    continue;
                }
                $parametersCall[] =  $this->parameterValue($p['type']);
            }
        } catch (ReflectionException $e) {
            $parametersCall[] = '<span class="error">Parameter ReflectionException</span>';
        }
        return $parametersCall;
    }
    private function parameterValue($p) {
        if(stripos($p, 'array') !== false) {
            return "['a' => '1', 'b' => '2', 'c' => 3]";
        }
        if(stripos($p, 'int') !== false) {
            return 3;
        }
        if(stripos($p, 'float') !== false) {
            return 2.7172;
        }
        if(stripos($p, 'string') !== false) {
            return "'palabra'";
        }
        if(stripos($p, 'bool') !== false) {
            return "true";
        }
        if(stripos($p, 'DateTimeImmutable') !== false) {
            return "new DateTimeImmutable()";
        }
        if(stripos($p, 'DateTime') !== false) {
            return "new DateTime()";
        }
        if(stripos($p, 'IaMysqli') !== false) {
            return "\$gSqlClass";
        }
        return "null /* =$p= */";
    }
    private function getControllerCode() {
        $slashes_number =  count(explode('\\',$this->className));
        $includePath = $slashes_number < 2 ? '' : str_repeat('../', $slashes_number - 1  );
        return "
    \$ia_example = [
        'title' => '$this->className', // fully qualified className
        'summary' => '', // class summary
        'full_example' => '', // full usage example, relative path from ia_examples, blank '' not shown.
        'example_file' => '$this->exampleCodeFileName', // method by method example, blank '' not shown.
    ];
     include( __DIR__ . '/{$includePath}template/ia_example_class.php');
    ";
    }
    private function saveFileIfNotExists($filePath, $code) {
        if(!file_exists($filePath)) {
            file_put_contents($filePath, "<?php\r\n$code");
        }
    }
    /** test file example */
    public function testTemplate() {
        if(empty($this->className) || $this->className[0] === '/' || $this->className[0] === '\\') {
            return "Invalid namespace\\className";
        }
        if(stripos($this->className, '/') || stripos($this->className, '.\\')) {
            return "Invalid namespace\\className  / ./ and .\\  not allowed";
        }
        try {
            $ref = new ReflectionClass($this->className);
        } catch (ReflectionException $e) {
            return "\Reflection error, invalid \$className = $this->className";
        }
        $this->shortName = $ref->getShortName();
        $this->classVariable = lcFirst($ref->getShortName());
        $construct = [
            'callWithVariables' => '',
            'callPrototype' => '',
            'callCommentOut' =>'',
            'call' => '',
            'callEcho' => '',
            'returnType' => '',
            'parametersPrototype' => '',
            'parametersValues' => [],
            'parameters' => [],
            'parametersList' => '',
            'createNew' => true,
            'docBlock' => '',
        ];
        foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            $functionDocBlock = $method->getDocComment();
            $callers =  $this->callStrings($method, $functionDocBlock);
            if($method->getName() === '__construct') {
                /** @var array $construct */
                $construct = $callers;
                $construct['docBlock'] = $functionDocBlock;
                continue;
            }
        }
        $ret = "\r\n";
        foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            if($method->getName() === '__construct') {
                continue;
            }
            $functionDocBlock = $method->getDocComment();
            $callers =  $this->callStrings($method, $functionDocBlock);
            $instantiateClassStatement = $method->isStatic() ? '' : "\$$this->classVariable = new $this->shortName($construct[parametersList]);" ;
            $ret .= $this->testMethodTemplate( $method->getName(), $callers, $instantiateClassStatement);
        }
/*
        if(empty($ref->getNamespaceName())) {
            $classFqn = $this->className;
        } else {
            $classFqn = $ref->getNamespaceName() . '\\' . $this->className;
        }
*/
        return "<?php\r\n".$this->testClassHeader($this->className, $ref->getShortName()).$ret."\r\n}\r\n";
    }
    private function testMethodTemplate( $methodName, $callers, $instanciateClassStatement) {
        $testValue = implode(', ', array_fill(0, $callers['parameterCount']+1, "''") );
        $coma = empty($callers['parametersList']) ? '' : ',';
        return <<< TESTMETHODTAG
    /**
     * @dataProvider {$methodName}_Provider
     *
     */
    public function test_{$methodName}($callers[parametersList] $coma \$expected) {
        $instanciateClassStatement
        \$this->assertEquals(\$expected, $callers[callWithVariables] );
    }
        function {$methodName}_Provider() {
            return [
                'test 1' => [ $testValue ],
            ];
        }
TESTMETHODTAG;
    }
    private function testClassHeader($classFqn, $className) {
        return <<< TESTCLASSSTART
use $classFqn;
use PHPUnit\Framework\TestCase;
class {$className}Test extends TestCase {
TESTCLASSSTART;
    }
}