Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
70.00% covered (success)
70.00%
7 / 10
CRAP
88.00% covered (success)
88.00%
132 / 150
Reflection
0.00% covered (danger)
0.00%
0 / 1
70.00% covered (success)
70.00%
7 / 10
69.86
88.00% covered (success)
88.00%
132 / 150
 __construct
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
7 / 7
 getClassString
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getObjectInstance
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 isClassString
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 isObjectInstance
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 generator
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 buildGenerator
100.00% covered (success)
100.00%
1 / 1
14
100.00% covered (success)
100.00%
29 / 29
 getClassNamespace
0.00% covered (danger)
0.00%
0 / 1
7.07
88.89% covered (success)
88.89%
16 / 18
 getClassProperties
0.00% covered (danger)
0.00%
0 / 1
10.15
88.46% covered (success)
88.46%
23 / 26
 getClassMethods
0.00% covered (danger)
0.00%
0 / 1
30.00
80.00% covered (success)
80.00%
52 / 65
<?php
/**
 * Pop PHP Framework (http://www.popphp.org/)
 *
 * @link       https://github.com/popphp/popphp-framework
 * @author     Nick Sagona, III <dev@nolainteractive.com>
 * @copyright  Copyright (c) 2009-2018 NOLA Interactive, LLC. (http://www.nolainteractive.com)
 * @license    http://www.popphp.org/license     New BSD License
 */
/**
 * @namespace
 */
namespace Pop\Code;
use Pop\Code\Generator;
/**
 * Reflection code class
 *
 * @category   Pop
 * @package    Pop\Code
 * @author     Nick Sagona, III <dev@nolainteractive.com>
 * @copyright  Copyright (c) 2009-2018 NOLA Interactive, LLC. (http://www.nolainteractive.com)
 * @license    http://www.popphp.org/license     New BSD License
 * @version    3.0.1
 */
class Reflection extends \ReflectionClass
{
    /**
     * Class string to reflect
     * @var string
     */
    protected $classString = null;
    /**
     * Object instance to reflect
     * @var Object
     */
    protected $objectInstance = null;
    /**
     * Code generator object
     * @var Generator
     */
    protected $generator = null;
    /**
     * Constructor
     *
     * Instantiate the code reflection object
     *
     * @param  mixed $class
     */
    public function __construct($class)
    {
        if (is_string($class)) {
            $this->classString = $class;
        } else {
            $this->objectInstance = $class;
            $this->classString    = get_class($class);
        }
        parent::__construct($class);
        $this->buildGenerator();
    }
    /**
     * Get the class string
     *
     * @return string
     */
    public function getClassString()
    {
        return $this->classString;
    }
    /**
     * Get the object instance
     *
     * @return string
     */
    public function getObjectInstance()
    {
        return $this->objectInstance;
    }
    /**
     * Determine if the argument passed was a class string
     *
     * @return boolean
     */
    public function isClassString()
    {
        return (null === $this->objectInstance);
    }
    /**
     * Determine if the argument passed was an object instance
     *
     * @return boolean
     */
    public function isObjectInstance()
    {
        return (null !== $this->objectInstance);
    }
    /**
     * Get the generator
     *
     * @return Generator
     */
    public function generator()
    {
        return $this->generator;
    }
    /**
     * Build the code generator based the reflection class
     *
     * @return void
     */
    protected function buildGenerator()
    {
        // Create generator object
        $type = ($this->isInterface()) ? Generator::CREATE_INTERFACE : Generator::CREATE_CLASS;
        $this->generator = new Generator($this->getShortName() . '.php', $type);
        // Get the namespace
        $this->getClassNamespace();
        // Detect and set the class doc block
        $classDocBlock = $this->getDocComment();
        if (!empty($classDocBlock) && (strpos($classDocBlock, '/*') !== false)) {
            $this->generator->code()->setDocblock(Generator\DocblockGenerator::parse($classDocBlock));
        }
        // Detect and set if the class is abstract
        if (!$this->isInterface() && $this->isAbstract()) {
            $this->generator->code()->setAbstract(true);
        }
        // Detect and set if the class is a child class
        $parent = $this->getParentClass();
        if ($parent !== false) {
            if ($parent->inNamespace()) {
                $this->generator->getNamespace()->setUse($parent->getNamespaceName() . '\\' . $parent->getShortName());
            }
            $this->generator->code()->setParent($parent->getShortName());
        }
        // Detect and set if the class implements any interfaces
        if (!$this->isInterface()) {
            $interfaces = $this->getInterfaces();
            if ($interfaces !== false) {
                $interfacesAry = [];
                foreach ($interfaces as $interface) {
                    if ($interface->inNamespace()) {
                        $this->generator->getNamespace()->setUse($interface->getNamespaceName() . '\\' . $interface->getShortName());
                    }
                    $interfacesAry[] = $interface->getShortName();
                }
                $this->generator->code()->setInterface(implode(', ', $interfacesAry));
            }
        }
        // Detect and set constants
        $constants = $this->getConstants();
        if (count($constants) > 0) {
            foreach ($constants as $key => $value) {
                $this->generator->code()->addProperty(new Generator\PropertyGenerator($key, gettype($value), $value, 'const'));
            }
        }
        // Get properties
        $this->getClassProperties();
        // Get Methods
        $this->getClassMethods();
    }
    /**
     * Get the namespace and uses, if any
     *
     * @return void
     */
    protected function getClassNamespace()
    {
        $fileContents = (file_exists($this->getFilename())) ? file_get_contents($this->getFilename()) : null;
        // Detect and set namespace
        if ($this->inNamespace()) {
            $this->generator->setNamespace(new Generator\NamespaceGenerator($this->getNamespaceName()));
            if (null !== $fileContents) {
                $matches = [];
                preg_match('/^use(.*)/m', $fileContents, $matches, PREG_OFFSET_CAPTURE);
                if (isset($matches[0][0])) {
                    $uses = substr($fileContents, $matches[0][1] + 4);
                    $uses = substr($uses, 0, strpos($uses, ';'));
                    $usesAry = explode(',', $uses);
                    foreach ($usesAry as $use) {
                        $use = trim($use);
                        $as = null;
                        if (stripos($use, 'as') !== false) {
                            $as = trim(substr($use, (strpos($use, 'as') + 2)));
                            $use = trim(substr($use, 0, strpos($use, 'as')));
                        }
                        $this->generator->getNamespace()->setUse($use, $as);
                    }
                }
            }
        }
    }
    /**
     * Get properties
     *
     * @return void
     */
    protected function getClassProperties()
    {
        // Detect and set properties
        $properties = $this->getDefaultProperties();
        if (count($properties) > 0) {
            foreach ($properties as $name => $value) {
                $property = $this->getProperty($name);
                $visibility = 'public';
                if ($property->isPublic()) {
                    $visibility = 'public';
                } else if ($property->isProtected()) {
                    $visibility = 'protected';
                } else if ($property->isPrivate()) {
                    $visibility = 'private';
                }
                $doc = $property->getDocComment();
                if ((null !== $doc) && (strpos($doc, '/*') !== false)) {
                    $docblock = Generator\DocblockGenerator::parse($doc);
                    $desc = $docblock->getDesc();
                    $type = $docblock->getTag('var');
                } else {
                    $type = strtolower(gettype($value));
                    $desc = null;
                }
                if (is_array($value)) {
                    $formattedValue = (count($value) == 0) ? null : $value;
                } else {
                    $formattedValue = $value;
                }
                $prop = new Generator\PropertyGenerator($property->getName(), $type, $formattedValue, $visibility);
                $prop->setStatic($property->isStatic());
                $prop->setDesc($desc);
                $this->generator->code()->addProperty($prop);
            }
        }
    }
    /**
     * Get methods
     *
     * @return void
     */
    protected function getClassMethods()
    {
        // Detect and set methods
        $methods = $this->getMethods();
        if (count($methods) > 0) {
            foreach ($methods as $value) {
                $methodExport = \ReflectionMethod::export($value->class, $value->name, true);
                // Get the method docblock
                if ((strpos($methodExport, '/*') !== false) && (strpos($methodExport, '*/') !== false)) {
                    $docBlock = substr($methodExport, strpos($methodExport, '/*'));
                    $docBlock = substr($docBlock, 0, (strpos($methodExport, '*/') + 2));
                } else {
                    $docBlock = null;
                }
                $method = $this->getMethod($value->name);
                $visibility = 'public';
                if ($method->isPublic()) {
                    $visibility = 'public';
                } else if ($method->isProtected()) {
                    $visibility = 'protected';
                } else if ($method->isPrivate()) {
                    $visibility = 'private';
                }
                $mthd = new Generator\MethodGenerator($value->name, $visibility, $method->isStatic());
                if ((null !== $docBlock) && (strpos($docBlock, '/*') !== false)) {
                    $mthd->setDocblock(Generator\DocblockGenerator::parse($docBlock, $mthd->getIndent()));
                }
                $mthd->setFinal($method->isFinal())
                     ->setAbstract($method->isAbstract());
                // Get the method parameters
                if (stripos($methodExport, 'Parameter') !== false) {
                    $matches = [];
                    preg_match_all('/Parameter \#(.*)\]/m', $methodExport, $matches);
                    if (isset($matches[0][0])) {
                        foreach ($matches[0] as $param) {
                            $name = null;
                            $value = null;
                            $type = null;
                            // Get name
                            $name = substr($param, strpos($param, '$'));
                            $name = trim(substr($name, 0, strpos($name, ' ')));
                            // Get value
                            if (strpos($param, '=') !== false) {
                                $value = trim(substr($param, (strpos($param, '=') + 1)));
                                $value = trim(substr($value, 0, strpos($value, ' ')));
                                $value = str_replace('NULL', 'null', $value);
                            }
                            // Get type
                            $type = substr($param, (strpos($param, '>') + 1));
                            $type = trim(substr($type, 0, strpos($type, '$')));
                            if ($type == '') {
                                $type = null;
                            }
                            $mthd->addArgument($name, $value, $type);
                        }
                    }
                }
                // Get method body
                if ((strpos($methodExport, '@@') !== false) && (file_exists($this->getFilename()))) {
                    $lineNums = substr($methodExport, (strpos($methodExport, '@@ ') + 3));
                    $start = trim(substr($lineNums, strpos($lineNums, ' ')));
                    $end = substr($start, (strpos($start, ' - ') + 3));
                    $start = substr($start, 0, strpos($start, ' '));
                    $end = (int)$end;
                    if (is_numeric($start) && is_numeric($end)) {
                        $classLines = file($this->getFilename());
                        $body = null;
                        $start = $start + 1;
                        $end = $end - 1;
                        for ($i = $start; $i < $end; $i++) {
                            if (isset($classLines[$i])) {
                                if (substr($classLines[$i], 0, 8) == '        ') {
                                    $body .= substr($classLines[$i], 8);
                                } else if (substr($classLines[$i], 0, 4) == '    ') {
                                    $body .= substr($classLines[$i], 4);
                                } else if (substr($classLines[$i], 0, 2) == "\t\t") {
                                    $body .= substr($classLines[$i], 2);
                                } else if (substr($classLines[$i], 0, 1) == "\t") {
                                    $body .= substr($classLines[$i], 1);
                                } else {
                                    $body .= $classLines[$i];
                                }
                            }
                        }
                        $mthd->setBody(rtrim($body), false);
                    }
                }
                $this->generator->code()->addMethod($mthd);
            }
        }
    }
}