Passed
Pull Request — master (#8)
by Akpé Aurelle Emmanuel Moïse
03:54
created

Shortcut::BuildCacheAndShow()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 6
dl 0
loc 11
rs 10
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
            return  self::BuildCacheAndShow($classname,$name,$file,$fullQualifiedClassname,$reflectionMethod,$private_scope);      
82
           
83
        }
84
		
85
		private static function BuildCacheAndShow($classname,$name,$file,$fullQualifiedClassname,$reflectionMethod,$private_scope){
86
			self::getSignature($reflectionMethod, $signature, $parameters, $paramsNum, $count);        
87
            $hasInternal='';
88
            if ($count) {
89
                self::BuildTheSwitch($hasInternal, $count, $paramsNum, $parameters, $classname);
90
            }
91
            self::useTheRightNameAndScope($Shortcut, $name, $fullQualifiedClassname, $signature, $private_scope, $classname);
92
                    
93
            self::handleInternals($Shortcut, $hasInternal, $parameters, $signature, $classname);
94
                        
95
            return self::pushAndShow($file, $Shortcut);
96
		}
97
98
        private static function getSignature(\ReflectionMethod $method, &$signature, &$parameters, &$paramsNum, &$count)
99
        {
100
            $params=$method->getParameters();
101
            $paramsNum=count($params);
102
            $signature='';
103
            $parameters=array();
104
            $count=0;
105
            foreach ($params as $k=>$param) {
106
                self::getParameterDeclaration($param, $tmp, $count, $method);
107
                $signature.=$tmp;
108
                $parameters[]='$'.$param->getName();
109
                $tmp='';
110
                if ($k<$paramsNum-1) {
111
                    $signature.=',';
112
                }
113
            }
114
        }
115
        
116
        private static function getParameterDeclaration(\reflectionParameter $param, &$tmp, &$count, $method)
117
        {
118
            $tmp=$param->isPassedByReference()?'&$'.$param->getName():'$'.$param->getName();
119
            if ($param->isOptional()) {
120
                $count++;
121
                if ($method->isInternal()) {
122
                    $tmp.='="acce91966cd8eee995ee1ac30c98c3d89d8f9235"';
123
                } else {
124
                    self::handleOptionalParameter($param, $tmp);
125
                }
126
            }
127
        }
128
        
129
        private static function handleOptionalParameter(\reflectionParameter $param, &$tmp)
130
        {
131
            if ($param->isDefaultValueConstant()) {
132
                $tmp.='='.$param->getDefaultValueConstantName();
133
            } elseif ($param->isDefaultValueAvailable()) {
134
                $tmp.='='.var_export($param->getDefaultValue(), true);
135
            } elseif ($param->allowsNull()) {
136
                $tmp.='=null';
137
            }
138
        }
139
        
140
        private static function BuildTheSwitch(&$hasInternal, $count, $paramsNum, $parameters, $classname)
141
        {
142
            $hasInternal.='switch($count){';
143
            while ($count>0) {
144
                $hasInternal.="case $count:return new $classname(".join(',', array_slice($parameters, 0, $paramsNum-$count))."); break;";
145
                $count--;
146
            }
147
            $hasInternal.='default:return new '.$classname.'('.join(',', $parameters).');break;}';
148
        }
149
        
150
        private static function useTheRightNameAndScope(&$Shortcut, $name, $fullQualifiedClassname, $signature, $scope, $classname)
151
        {
152
            if (strtolower($name)!=='new'&&preg_match(self::VALID_PHP_FUNCTION_NAME_PATTERN, $name)) {
153
                $Shortcut="<?php
154
							function $name($signature){";
155
                if ($scope) {
156
                    $Shortcut.="if(".'@get_class()'."!==$classname){
157
									throw new scopeException(\"Shortcut function $name can only be called in class $classname scope\");
158
								}";
159
                }
160
            } else {
161
                $Shortcut="<?php
162
							function $fullQualifiedClassname($signature){";
163
                if ($scope) {
164
                    $Shortcut.="if(@get_class()!==\"$classname\"){
165
							throw new scopeException(\"Shortcut function $fullQualifiedClassname can only be called in class $classname scope\");
166
						}";
167
                }
168
            }
169
        }
170
        
171
        private static function handleInternals(&$Shortcut, $hasInternal, $parameters, $signature, $classname)
172
        {
173
            if (!strpos($signature, "acce91966cd8eee995ee1ac30c98c3d89d8f9235")) {
174
                $Shortcut.="return new $classname(".join(',', $parameters).");
175
							}";
176
            } else {
177
                $Shortcut.='
178
							$count=count(array_keys(get_defined_vars(),"'.self::PLACEHOLDER_FOR_INTERNALS_CLASSES_OPTIONALS_PARAMETERS.'"));
179
							'.$hasInternal.'
180
							}';
181
            }
182
        }
183
        
184
        private static function pushAndShow($file, $Shortcut)
185
        {
186
            file_put_contents($file, str_replace("\t", '    ', $Shortcut));
187
            file_put_contents($file, php_strip_whitespace($file)); //just for cleanliness of the generated code
188
            return include_once($file);
189
        }
190
        
191
        private static function GetTheRightExceptionMessage($fileExists, $name, $fullQualifiedClassname)
192
        {
193
            if (!$fileExists) {
194
                if (strtolower($name)!=='new'&&preg_match(self::VALID_PHP_FUNCTION_NAME_PATTERN, $name)) {
195
                    throw new \InvalidArgumentException('function '.$name.' passed as second Argument already exists.
196
					Can\'t create a shortcut with the same name');
197
                } else {
198
                    throw new \InvalidArgumentException('function '.$fullQualifiedClassname.' already exists and An alias has not been provided as Argument 2.
199
					Can\'t create a shortcut function with this name');
200
                }
201
            }
202
        }
203
        
204
        private static function HandleNotInstantiableAndHasNoConstructor(&$Shortcut, $fullQualifiedClassname, $name, $notInstantiable, $classname)
205
        {
206
            if ($notInstantiable) {
207
                if (!self::$SHORTCUT_FOR_ALL) {
208
                    throw new \InvalidArgumentException('Not Instantiable class '.$fullQualifiedClassname.' passed as Argument');
209
                }
210
            } else {
211
                self::useTheRightNameAndScope($Shortcut, $name, $fullQualifiedClassname, '', false, $classname);
212
                $Shortcut.="return new $classname();
213
						}";
214
            }
215
        }
216
        
217
        private static function getTheRightDir(&$file, &$Dir, $fullQualifiedClassname)
218
        {
219
            if ($Dir=self::$DIR) {
220
                $file=self::$DIR.DIRECTORY_SEPARATOR.$fullQualifiedClassname.".Shortcut.php";
221
            } else {
222
                $Dir=dirname(__DIR__).DIRECTORY_SEPARATOR.'ClassShortcuts';
223
                $file=$Dir.DIRECTORY_SEPARATOR.$fullQualifiedClassname.".Shortcut.php";
224
            }
225
        }
226
        
227
        private static function createDir($Dir)
228
        {
229
            if (!file_exists($Dir)) {
230
                mkdir($Dir);
231
            }
232
        }
233
        
234
        public static function setDir($dirname)
235
        {
236
            if (is_dir($dirname)&&is_writable($dirname)&&!self::$DIR) {
237
                self::$DIR=$dirname;
238
            }
239
        } 
240
        
241
        private function __construct()
242
        {
243
        }
244
    }
245
    
246
    
247
}
248
249
namespace{
250
    function create_Shortcut($classname, $name='new')
251
    {
252
        return EZAMA\Shortcut::create($classname, $name);
253
    }
254
    class scopeException extends \Exception
255
    {
256
    }
257
}
258