Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.04% covered (success)
98.04%
50 / 51
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
FunctionReflection
98.04% covered (success)
98.04%
50 / 51
0.00% covered (danger)
0.00%
0 / 1
26
0.00% covered (danger)
0.00%
0 / 1
 parse
98.04% covered (success)
98.04%
50 / 51
0.00% covered (danger)
0.00%
0 / 1
26
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Code\Reflection;
15
16use Pop\Code\Generator\FunctionGenerator;
17use ReflectionException;
18
19/**
20 * Function reflection code class
21 *
22 * @category   Pop
23 * @package    Pop\Code
24 * @author     Nick Sagona, III <dev@nolainteractive.com>
25 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
26 * @license    http://www.popphp.org/license     New BSD License
27 * @version    5.0.0
28 */
29class FunctionReflection extends AbstractReflection
30{
31
32    /**
33     * Method to parse a function or closure
34     *
35     * @param  mixed   $code
36     * @param  ?string $name
37     * @throws ReflectionException
38     * @return FunctionGenerator
39     */
40    public static function parse(mixed $code, ?string $name = null): FunctionGenerator
41    {
42        $reflection       = new \ReflectionFunction($code);
43        $reflectionName   = $reflection->getName();
44        $reflectionParams = $reflection->getParameters();
45        $isClosure        = ($reflectionName == '{closure}');
46
47        if (($name === null) && !($isClosure)) {
48            $name = $reflectionName;
49        }
50
51        $function = new FunctionGenerator($name, $isClosure);
52
53        foreach ($reflectionParams as $key => $reflectionParam) {
54            $paramName  = $reflectionParam->getName();
55            $paramType  = $reflectionParam->getType();
56            $paramType  = (!empty($paramType) && ($paramType instanceof \ReflectionType)) ? $paramType->getName() : null;
57
58            try {
59                $paramValue = $reflectionParam->getDefaultValue();
60            } catch (\ReflectionException $e) {
61                $paramValue = null;
62            }
63
64            $function->addArgument($paramName, $paramValue, $paramType);
65        }
66
67        // Parse the body if available
68        $file = $reflection->getFileName();
69
70        if (!empty($file) && file_exists($file)) {
71            $lines     = file($file);
72            $startLine = $reflection->getStartLine() - 1;
73            $endLine   = $reflection->getEndLine() - 1;
74            $length    = $endLine - $startLine;
75            $body      = null;
76
77            if (($length > 0) && isset($lines[$startLine]) && isset($lines[$endLine])) {
78                $lines = array_slice($lines, ($startLine + 1), ($length - 1));
79                if (isset($lines[0]) && (str_starts_with($lines[0], ' '))) {
80                    $spaces = strlen($lines[0]) - strlen(ltrim($lines[0]));
81                    if ($spaces > 0) {
82                        $lines = array_map(function($value) use ($spaces) {
83                            if (substr($value, 0, $spaces) == str_repeat(' ', $spaces)) {
84                                $value = substr($value, $spaces);
85                            }
86                            return $value;
87                        }, $lines);
88                    }
89                }
90                $body = implode('', $lines);
91            }
92
93            if (!empty($body)) {
94                $function->setBody($body, false);
95            }
96        }
97
98        // Get return type(s)
99        if ($reflection->hasReturnType()) {
100            $namedTypes  = [];
101            $returnTypes = $reflection->getReturnType();
102            if ($returnTypes instanceof \ReflectionUnionType) {
103                $types = $returnTypes->getTypes();
104                foreach ($types as $type) {
105                    $namedTypes[] = $type->getName();
106                }
107                if (($returnTypes->allowsNull()) && !in_array('null', $namedTypes)) {
108                    $namedTypes[] = 'null';
109                }
110            } else if ($returnTypes instanceof \ReflectionNamedType) {
111                $namedTypes[] = $returnTypes->getName();
112                if (($returnTypes->allowsNull()) && !in_array('null', $namedTypes)) {
113                    $namedTypes[] = 'null';
114                }
115            }
116            if (!empty($namedTypes)) {
117                $function->addReturnTypes($namedTypes);
118            }
119        }
120
121        return $function;
122    }
123
124}