Passed
Push — master ( 7e107f...31ffea )
by Akpé Aurelle Emmanuel Moïse
01:48
created

Shortcut::_create()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 30
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 21
nc 7
nop 2
dl 0
loc 30
rs 8.4444
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, $name){
0 ignored issues
show
Unused Code introduced by
The parameter $name is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

35
		private static function _init($classname, /** @scrutinizer ignore-unused */ $name){

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
36
			return [
37
				'reflectionClass'=>$tmp=new \reflectionClass($classname),
38
				'classname'=>$tmp->getName(),
39
				'fullQualifiedClassname'=>str_replace('\\', '_', $classname),
40
			];
41
		}
42
		
43
		private static function forwardInit($name,$Dir,\reflectionClass $reflectionClass){
44
			self::createDir($Dir);
45
			return [
46
				'private_scope'=>false,
47
				'name'=>trim($name),
48
				'reflectionMethod'=>$reflectionClass->getConstructor(),
49
				'notInstantiable'=>false,
50
			];
51
		}
52
		
53
		private static function _create($classname, $name){
54
			extract(self::_init($classname, $name));
55
			self::getTheRightDir($file, $Dir, $fullQualifiedClassname);
56
			$fileExists=file_exists($file);
57
			if (!function_exists($classname)&&!function_exists($name)) {
58
				if ($fileExists) {
59
					return include_once($file);
60
				}
61
					extract(self::forwardInit($name,$Dir,$reflectionClass));
62
					if (is_null($reflectionMethod)||$notInstantiable=!$reflectionClass->isInstantiable()) {
63
						self::HandleNotInstantiableAndHasNoConstructor($Shortcut, $fullQualifiedClassname, $name, $notInstantiable, $classname);
64
						if ($Shortcut) {
65
							return self::pushAndShow($file, $Shortcut);
66
						}
67
						$private_scope=true;
68
					}
69
					
70
					self::getSignature($reflectionMethod, $signature, $parameters, $paramsNum, $count);
71
					
72
					$hasInternal='';
73
					if ($count) {
74
						self::BuildTheSwitch($hasInternal, $count, $paramsNum, $parameters, $classname);
75
					}
76
					self::useTheRightNameAndScope($Shortcut, $name, $fullQualifiedClassname, $signature, $private_scope, $classname);
77
					
78
					self::handleInternals($Shortcut, $hasInternal, $parameters, $signature, $classname);
79
						
80
					return self::pushAndShow($file, $Shortcut);
81
			} else {
82
				self::GetTheRightExceptionMessage($fileExists, $name, $fullQualifiedClassname);
83
			}			
84
		}
85
86
        private static function getSignature(\ReflectionMethod $method, &$signature, &$parameters, &$paramsNum, &$count)
87
        {
88
            $params=$method->getParameters();
89
            $paramsNum=count($params);
90
            $signature='';
91
            $parameters=array();
92
            $count=0;
93
            foreach ($params as $k=>$param) {
94
                self::getParameterDeclaration($param, $tmp, $count, $method);
95
                $signature.=$tmp;
96
                $parameters[]='$'.$param->getName();
97
                $tmp='';
98
                if ($k<$paramsNum-1) {
99
                    $signature.=',';
100
                }
101
            }
102
        }
103
        
104
        private static function getParameterDeclaration(\reflectionParameter $param, &$tmp, &$count, $method)
105
        {
106
            $tmp=$param->isPassedByReference()?'&$'.$param->getName():'$'.$param->getName();
107
            if ($param->isOptional()) {
108
                $count++;
109
                if ($method->isInternal()) {
110
                    $tmp.='="acce91966cd8eee995ee1ac30c98c3d89d8f9235"';
111
                } else {
112
                    self::handleOptionalParameter($param, $tmp);
113
                }
114
            }
115
        }
116
        
117
        private static function handleOptionalParameter(\reflectionParameter $param, &$tmp)
118
        {
119
            if ($param->isDefaultValueConstant()) {
120
                $tmp.='='.$param->getDefaultValueConstantName();
121
            } elseif ($param->isDefaultValueAvailable()) {
122
                $tmp.='='.var_export($param->getDefaultValue(), true);
123
            } elseif ($param->allowsNull()) {
124
                $tmp.='=null';
125
            }
126
        }
127
        
128
        private static function BuildTheSwitch(&$hasInternal, $count, $paramsNum, $parameters, $classname)
129
        {
130
            $hasInternal.='switch($count){';
131
            while ($count>0) {
132
                $hasInternal.="case $count:return new $classname(".join(',', array_slice($parameters, 0, $paramsNum-$count))."); break;";
133
                $count--;
134
            }
135
            $hasInternal.='default:return new '.$classname.'('.join(',', $parameters).');break;}';
136
        }
137
        
138
        private static function useTheRightNameAndScope(&$Shortcut, $name, $fullQualifiedClassname, $signature, $scope, $classname)
139
        {
140
            if (strtolower($name)!=='new'&&preg_match(self::VALID_PHP_FUNCTION_NAME_PATTERN, $name)) {
141
                $Shortcut="<?php
142
							function $name($signature){";
143
                if ($scope) {
144
                    $Shortcut.="if(".'@get_class()'."!==$classname){
145
									throw new scopeException(\"Shortcut function $name can only be called in class $classname scope\");
146
								}";
147
                }
148
            } else {
149
                $Shortcut="<?php
150
							function $fullQualifiedClassname($signature){";
151
                if ($scope) {
152
                    $Shortcut.="if(@get_class()!==\"$classname\"){
153
							throw new scopeException(\"Shortcut function $fullQualifiedClassname can only be called in class $classname scope\");
154
						}";
155
                }
156
            }
157
        }
158
        
159
        private static function handleInternals(&$Shortcut, $hasInternal, $parameters, $signature, $classname)
160
        {
161
            if (!strpos($signature, "acce91966cd8eee995ee1ac30c98c3d89d8f9235")) {
162
                $Shortcut.="return new $classname(".join(',', $parameters).");
163
							}";
164
            } else {
165
                $Shortcut.='
166
							$count=count(array_keys(get_defined_vars(),"'.self::PLACEHOLDER_FOR_INTERNALS_CLASSES_OPTIONALS_PARAMETERS.'"));
167
							'.$hasInternal.'
168
							}';
169
            }
170
        }
171
        
172
        private static function pushAndShow($file, $Shortcut)
173
        {
174
            file_put_contents($file, str_replace("\t", '    ', $Shortcut));
175
            file_put_contents($file, php_strip_whitespace($file)); //just for cleanliness of the generated code
176
            return include_once($file);
177
        }
178
        
179
        private static function GetTheRightExceptionMessage($fileExists, $name, $fullQualifiedClassname)
180
        {
181
            if (!$fileExists) {
182
                if (strtolower($name)!=='new'&&preg_match(self::VALID_PHP_FUNCTION_NAME_PATTERN, $name)) {
183
                    throw new \InvalidArgumentException('function '.$name.' passed as second Argument already exists.
184
					Can\'t create a shortcut with the same name');
185
                } else {
186
                    throw new \InvalidArgumentException('function '.$fullQualifiedClassname.' already exists and An alias has not been provided as Argument 2.
187
					Can\'t create a shortcut function with this name');
188
                }
189
            }
190
        }
191
        
192
        private static function HandleNotInstantiableAndHasNoConstructor(&$Shortcut, $fullQualifiedClassname, $name, $notInstantiable, $classname)
193
        {
194
            if ($notInstantiable) {
195
                if (!self::$SHORTCUT_FOR_ALL) {
196
                    throw new \InvalidArgumentException('Not Instantiable class '.$fullQualifiedClassname.' passed as Argument');
197
                }
198
            } else {
199
                self::useTheRightNameAndScope($Shortcut, $name, $fullQualifiedClassname, '', false, $classname);
200
                $Shortcut.="return new $classname();
201
						}";
202
            }
203
        }
204
        
205
        private static function getTheRightDir(&$file, &$Dir, $fullQualifiedClassname)
206
        {
207
            if ($Dir=self::$DIR) {
208
                $file=self::$DIR.DIRECTORY_SEPARATOR.$fullQualifiedClassname.".Shortcut.php";
209
            } else {
210
                $Dir=dirname(__DIR__).DIRECTORY_SEPARATOR.'ClassShortcuts';
211
                $file=$Dir.DIRECTORY_SEPARATOR.$fullQualifiedClassname.".Shortcut.php";
212
            }
213
        }
214
        
215
        private static function createDir($Dir)
216
        {
217
            if (!file_exists($Dir)) {
218
                mkdir($Dir);
219
            }
220
        }
221
        
222
        
223
        
224
        public static function setDir($dirname)
225
        {
226
            if (is_dir($dirname)&&is_writable($dirname)&&!self::$DIR) {
227
                self::$DIR=$dirname;
228
            }
229
        }
230
        
231
        
232
        private function __construct()
233
        {
234
        }
235
    }
236
    
237
    
238
}
239
240
namespace{
241
    function create_Shortcut($classname, $name='new')
242
    {
243
        return EZAMA\Shortcut::create($classname, $name);
244
    }
245
    class scopeException extends \Exception
246
    {
247
    }
248
}
249