Passed
Push — master ( 761eee...3cb13d )
by Akpé Aurelle Emmanuel Moïse
01:52
created

Shortcut::handleNewShortcut()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 6
nop 7
dl 0
loc 25
rs 9.2222
c 0
b 0
f 0
1
<?php
2
namespace EZAMA
3
4
/**
5
*
6
* @Name : Shortcut
7
* @Version : 1.0.0
8
* @Programmer : Akpé Aurelle Emmanuel Moïse Zinsou
9
* @Date : 2019-04-01
10
* @Released under : https://github.com/manuwhat/Shortcut/blob/master/LICENSE
11
* @Repository : https://github.com/manuwhat/Shortcut
12
*
13
**/
14
{
15
16
    class Shortcut
17
    {
18
        const VALID_PHP_FUNCTION_NAME_PATTERN='#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#';
19
        const CAN_NEVER_EVER_CHOOSE_THIS_AS_FUNCTION_NAME="new";
20
        const PLACEHOLDER_FOR_INTERNALS_CLASSES_OPTIONALS_PARAMETERS="acce91966cd8eee995ee1ac30c98c3d89d8f9235";
21
        private static $DIR=null;
22
        private static $SHORTCUT_FOR_ALL=false;
23
        
24
        public static function SetShortcutForAll($bool)
25
        {
26
            self::$SHORTCUT_FOR_ALL=(bool)$bool;
27
        }
28
        public static function create($classname, $name=self::CAN_NEVER_EVER_CHOOSE_THIS_AS_FUNCTION_NAME)
29
        {
30
            if (is_string($classname)&&class_exists($classname, true)) {
31
                return self::_create($classname, $name);
32
            }
33
        }
34
        
35
        private static function _init($classname)
36
        {
37
            return [
38
                'reflectionClass'=>$tmp=new \reflectionClass($classname),
39
                'classname'=>$tmp->getName(),
40
                'fullQualifiedClassname'=>str_replace('\\', '_', $classname),
41
            ];
42
        }
43
        
44
        private static function forwardInit($name, $Dir, \reflectionClass $reflectionClass)
45
        {
46
            self::createDir($Dir);
47
            return [
48
                'private_scope'=>false,
49
                'name'=>trim($name),
50
                'reflectionMethod'=>$reflectionClass->getConstructor(),
51
                'notInstantiable'=>false,
52
            ];
53
        }
54
        
55
        private static function _create($classname, $name)
56
        {
57
            extract(self::_init($classname));
58
            self::getTheRightDir($file, $Dir, $fullQualifiedClassname);
59
            $fileExists=file_exists($file);
60
            if (!function_exists($classname)&&!function_exists($name)) {
61
                return self::handleNewShortcut($classname, $name, $file, $Dir, $fullQualifiedClassname, $fileExists, $reflectionClass);
62
            } else {
63
                self::GetTheRightExceptionMessage($fileExists, $name, $fullQualifiedClassname);
64
            }
65
        }
66
        
67
        private static function handleNewShortcut($classname, $name, $file, $Dir, $fullQualifiedClassname, $fileExists, \reflectionClass $reflectionClass)
68
        {
69
            if ($fileExists) {
70
                return include_once($file);
71
            }
72
            extract(self::forwardInit($name, $Dir, $reflectionClass));
73
            if (is_null($reflectionMethod)||$notInstantiable=!$reflectionClass->isInstantiable()) {
74
                self::HandleNotInstantiableAndHasNoConstructor($Shortcut, $fullQualifiedClassname, $name, $notInstantiable, $classname);
75
                if ($Shortcut) {
76
                    return self::pushAndShow($file, $Shortcut);
77
                }
78
                $private_scope=true;
79
            }
80
                    
81
            self::getSignature($reflectionMethod, $signature, $parameters, $paramsNum, $count);
82
                    
83
            $hasInternal='';
84
            if ($count) {
85
                self::BuildTheSwitch($hasInternal, $count, $paramsNum, $parameters, $classname);
86
            }
87
            self::useTheRightNameAndScope($Shortcut, $name, $fullQualifiedClassname, $signature, $private_scope, $classname);
88
                    
89
            self::handleInternals($Shortcut, $hasInternal, $parameters, $signature, $classname);
90
                        
91
            return self::pushAndShow($file, $Shortcut);
92
        }
93
94
        private static function getSignature(\ReflectionMethod $method, &$signature, &$parameters, &$paramsNum, &$count)
95
        {
96
            $params=$method->getParameters();
97
            $paramsNum=count($params);
98
            $signature='';
99
            $parameters=array();
100
            $count=0;
101
            foreach ($params as $k=>$param) {
102
                self::getParameterDeclaration($param, $tmp, $count, $method);
103
                $signature.=$tmp;
104
                $parameters[]='$'.$param->getName();
105
                $tmp='';
106
                if ($k<$paramsNum-1) {
107
                    $signature.=',';
108
                }
109
            }
110
        }
111
        
112
        private static function getParameterDeclaration(\reflectionParameter $param, &$tmp, &$count, $method)
113
        {
114
            $tmp=$param->isPassedByReference()?'&$'.$param->getName():'$'.$param->getName();
115
            if ($param->isOptional()) {
116
                $count++;
117
                if ($method->isInternal()) {
118
                    $tmp.='="acce91966cd8eee995ee1ac30c98c3d89d8f9235"';
119
                } else {
120
                    self::handleOptionalParameter($param, $tmp);
121
                }
122
            }
123
        }
124
        
125
        private static function handleOptionalParameter(\reflectionParameter $param, &$tmp)
126
        {
127
            if ($param->isDefaultValueConstant()) {
128
                $tmp.='='.$param->getDefaultValueConstantName();
129
            } elseif ($param->isDefaultValueAvailable()) {
130
                $tmp.='='.var_export($param->getDefaultValue(), true);
131
            } elseif ($param->allowsNull()) {
132
                $tmp.='=null';
133
            }
134
        }
135
        
136
        private static function BuildTheSwitch(&$hasInternal, $count, $paramsNum, $parameters, $classname)
137
        {
138
            $hasInternal.='switch($count){';
139
            while ($count>0) {
140
                $hasInternal.="case $count:return new $classname(".join(',', array_slice($parameters, 0, $paramsNum-$count))."); break;";
141
                $count--;
142
            }
143
            $hasInternal.='default:return new '.$classname.'('.join(',', $parameters).');break;}';
144
        }
145
        
146
        private static function useTheRightNameAndScope(&$Shortcut, $name, $fullQualifiedClassname, $signature, $scope, $classname)
147
        {
148
            if (strtolower($name)!=='new'&&preg_match(self::VALID_PHP_FUNCTION_NAME_PATTERN, $name)) {
149
                $Shortcut="<?php
150
							function $name($signature){";
151
                if ($scope) {
152
                    $Shortcut.="if(".'@get_class()'."!==$classname){
153
									throw new scopeException(\"Shortcut function $name can only be called in class $classname scope\");
154
								}";
155
                }
156
            } else {
157
                $Shortcut="<?php
158
							function $fullQualifiedClassname($signature){";
159
                if ($scope) {
160
                    $Shortcut.="if(@get_class()!==\"$classname\"){
161
							throw new scopeException(\"Shortcut function $fullQualifiedClassname can only be called in class $classname scope\");
162
						}";
163
                }
164
            }
165
        }
166
        
167
        private static function handleInternals(&$Shortcut, $hasInternal, $parameters, $signature, $classname)
168
        {
169
            if (!strpos($signature, "acce91966cd8eee995ee1ac30c98c3d89d8f9235")) {
170
                $Shortcut.="return new $classname(".join(',', $parameters).");
171
							}";
172
            } else {
173
                $Shortcut.='
174
							$count=count(array_keys(get_defined_vars(),"'.self::PLACEHOLDER_FOR_INTERNALS_CLASSES_OPTIONALS_PARAMETERS.'"));
175
							'.$hasInternal.'
176
							}';
177
            }
178
        }
179
        
180
        private static function pushAndShow($file, $Shortcut)
181
        {
182
            file_put_contents($file, str_replace("\t", '    ', $Shortcut));
183
            file_put_contents($file, php_strip_whitespace($file)); //just for cleanliness of the generated code
184
            return include_once($file);
185
        }
186
        
187
        private static function GetTheRightExceptionMessage($fileExists, $name, $fullQualifiedClassname)
188
        {
189
            if (!$fileExists) {
190
                if (strtolower($name)!=='new'&&preg_match(self::VALID_PHP_FUNCTION_NAME_PATTERN, $name)) {
191
                    throw new \InvalidArgumentException('function '.$name.' passed as second Argument already exists.
192
					Can\'t create a shortcut with the same name');
193
                } else {
194
                    throw new \InvalidArgumentException('function '.$fullQualifiedClassname.' already exists and An alias has not been provided as Argument 2.
195
					Can\'t create a shortcut function with this name');
196
                }
197
            }
198
        }
199
        
200
        private static function HandleNotInstantiableAndHasNoConstructor(&$Shortcut, $fullQualifiedClassname, $name, $notInstantiable, $classname)
201
        {
202
            if ($notInstantiable) {
203
                if (!self::$SHORTCUT_FOR_ALL) {
204
                    throw new \InvalidArgumentException('Not Instantiable class '.$fullQualifiedClassname.' passed as Argument');
205
                }
206
            } else {
207
                self::useTheRightNameAndScope($Shortcut, $name, $fullQualifiedClassname, '', false, $classname);
208
                $Shortcut.="return new $classname();
209
						}";
210
            }
211
        }
212
        
213
        private static function getTheRightDir(&$file, &$Dir, $fullQualifiedClassname)
214
        {
215
            if ($Dir=self::$DIR) {
216
                $file=self::$DIR.DIRECTORY_SEPARATOR.$fullQualifiedClassname.".Shortcut.php";
217
            } else {
218
                $Dir=dirname(__DIR__).DIRECTORY_SEPARATOR.'ClassShortcuts';
219
                $file=$Dir.DIRECTORY_SEPARATOR.$fullQualifiedClassname.".Shortcut.php";
220
            }
221
        }
222
        
223
        private static function createDir($Dir)
224
        {
225
            if (!file_exists($Dir)) {
226
                mkdir($Dir);
227
            }
228
        }
229
        
230
        
231
        
232
        public static function setDir($dirname)
233
        {
234
            if (is_dir($dirname)&&is_writable($dirname)&&!self::$DIR) {
235
                self::$DIR=$dirname;
236
            }
237
        }
238
        
239
        
240
        private function __construct()
241
        {
242
        }
243
    }
244
    
245
    
246
}
247
248
namespace{
249
    function create_Shortcut($classname, $name='new')
250
    {
251
        return EZAMA\Shortcut::create($classname, $name);
252
    }
253
    class scopeException extends \Exception
254
    {
255
    }
256
}
257