بعد غياب طويل اعود لأكتب مقالة جديدة حول التحميل الزائد Overload.
في الاصدار الجديد لكلاس Overloadable الذي نشرت اصدارات سابقة في هذه المدونة قمت باضافات عدد من المميزات وهي:
- عدم الحاجة لوراثة الكلاس overloadable.
- عدم الحاجة لاستخدام الدالة eval.
- تقليل حجم الكلاس overloadable.
- تحتاج لاضافة دالة واحدة في كلاسك لتقوم بكل العمل.
والأن إلى الشرح:
1) قم بتضمين كلاس Overloadable في مشروعك، وهنا سأضيف الكلاس كاملاً لكي تتمكن من رؤية المثال كاملاً..
<?php /******************************* * author : hishamdalal@gmail.com * version : 3.8 * create on : 2017-09-17 *****************************/ class Overloadable { static function call($obj, $method, $params=null) { $class = get_class($obj); // Get real method name $suffix_method_name = $method.self::getMethodSuffix($method, $params); if (method_exists($obj, $suffix_method_name)) { // Call method return call_user_func_array(array($obj, $suffix_method_name), $params); }else{ throw new Exception('Tried to call unknown method '.$class.'::'.$suffix_method_name); } } static function getMethodSuffix($method, $params_ary=array()) { $c = '__'; if(is_array($params_ary)){ foreach($params_ary as $i=>$param){//var_dump($param); // Adding special characters to the end of method name switch(gettype($param)){ case 'array': $c .= 'a'; break; case 'boolean': $c .= 'b'; break; case 'double': $c .= 'd'; break; case 'integer': $c .= 'i'; break; case 'NULL': $c .= 'n'; break; case 'object': // Support closure params if($param instanceof Closure ){ $c .= 'c'; }else{ $c .= 'o'; } break; case 'resource': $c .= 'r'; break; case 'string': $c .= 's'; break; case 'unknown type':$c .= 'u'; break; } } } return $c; } // Get a reference variable by name static function &refAccess($var_name) { $r =& $GLOBALS["$var_name"]; return $r; } }
function __call($method, $args) { return Overloadable::call($this, $method, $args); }
الآن شاهد المثال التالي حيث قمت بعمل كلاس باسم Test واضفت في الدالة __call
لاحظ أن php لا تدعم التحميل الزائد لذا يجب عدم عمل دوال بنفس الأسم فمثلاً في كلاس Test تنتهي كل الدوال بشرطتين سفليتين __ ثم حرف أو اكثر يدل على نوع المتغيرات الخاصة بالدالة كالتالي:
لعمل دالة بدون متغيرات :
function myFunction__() { // your code }
اذا نسيت الشرطتين في آخر الدالة فلن يعمل كلاس التحميل الزائد وذلك لأن php ستستدعي الدالة myFunction مباشرة دون استدعاء الدالة __call التي يتم استدعائها في حالة عدم وجود الدالة في الكلاس.
واذا اردت عمل دالة بنفس الاسم مع اضافة متغير رقمي مثلاً تكتب الدالة بالشكل التالي:
function myFunction__i($int) { var_dump($int); }
لاحظ أن حرف i بعد الشرطتين يدل على ان متغير الدالة رقمي integer
ولاضافة دالة بنفس الاسم تحوي متغير نصي تكتب الدالة على النحو الآتي:
function myFunction__s($string) { // ... }
function anotherFunction__si($str, $int) { echo "str: $str, int: $int"; }
لاحظ ترتيب الأحرف في آخر اسم الدالة بعد الشرطتين يكون بنفس ترتيب متغيرات الدالة..
يمكنك عمل دوال بعدد لا نهائي من المتغيرات..
وهذه قائمة بالأحرف الخاصة بكل نوع من أنواع البيانات التي يمكنك استخدامها:
i: Integer d: double s: String b: Boolean a: Array o: Object c: Closure r: Resource n: Null
class test { private $name = 'test-1'; // Call Overloadable class // you must copy this method in your class to activate overloading function __call($method, $args) { return Overloadable::call($this, $method, $args); } // func(closure) // New in version 3.8 function func__c(Closure $callback) { $r = pre("func__c(".print_r($callback, 1).");", 'print_r(Closure)', false)."\n"; $r .= $callback($this->name); return $r; } // func(NULL) function func__n($null){ return '[[ NULL ]]'; } // myFunction(void) function myFunction__() { return 'myFunction(void)'; } // myFunction(integer) function myFunction__i($int) { return 'myFunction(integer='.$int.')'; } // myFunction(string) function myFunction__s($string) { return 'myFunction(string='.$string.')'; } // myFunction(string) function myFunction__so($string, $object) { $r = 'myFunction(string='.$string.', object='.get_class($object).')'; $object->x++; $r .= '<pre>Object: '; $r .= print_r($object, true); $r .= '</pre>'; return $r; } // anotherFunction(array) function anotherFunction__a($array) { $r = 'anotherFunction('.print_r($array, 1).')'."\n"; $array[0]++; // change the reference value $array['val']++; // change the reference value $r .= 'array[0]='.$array[0]."\n"; $r .= 'array[val]='.$array['val']."\n"; return $r; } // anotherFunction(string, integer) function anotherFunction__si($key, $value) { $r = 'anotherFunction(string='.$key.', integer='.$value.')'; // Get a reference $a2 =& Overloadable::refAccess($key); // => $a2 =& $GLOBALS['val']; $a2 *= 3; // change the reference value $r .= "\nkey=$key, value=$value ($$key * 3) => $$key=$a2;"; return $r; } }
function pre($mixed, $title=null, $print=true){ $output = ""; if(empty($mixed)){$output .= "<div><h3>-->Empty $title<--</h3></div>"; if($print){echo $output; return;}else{return $output;} } $output .= "<fieldset>"; if($title){$output .= "<legend><h2>$title</h2></legend>";} $output .= '<pre>'. print_r($mixed, true) .'</pre>'; $output .= "</fieldset>"; if($print){echo $output;}else{return $output;} }
//---------------------------------------------------------- // Start //---------------------------------------------------------- // Some data to work with: $val = 10; class obj { public $x=10; } //---------------------------------------------------------- // Start $t = new test; // Call first method with no args: pre( $t->myFunction() , 'myFunction()'); // Output: myFunction(void) pre( $t->myFunction($val), 'myFunction__i'); // Output: myFunction(integer=10) pre( $t->myFunction("hello"), 'myFunction__s'); // Output: myFunction(string=hello) pre( $t->myFunction("str", new obj()), 'myFunction__so'); /* Output: myFunction(string=str, object=obj) Object: obj Object ( [x] => 11 ) */ //// Passing by Reference: pre( '$val='.$val, '$val' ); // Output: $val=10 pre( $t->anotherFunction(array(&$val, 'val'=>&$val)), 'anotherFunction__a' ); // Output: anotherFunction(Array ( [0] => 10 [val] => 10 ) ) // array[0]=12 // array[val]=12 pre( '$val='.$val, '$val' ); // Output: $val=12 pre( $t->anotherFunction('val', $val), 'anotherFunction__si' ); // Output: anotherFunction(string=val, integer=12) // key=val, value=12 ($val * 3) => $val=36; pre('$val='.$val, '$val'); // Output: $val=36 // Closure example $f = $t->func( function($n){ return strtoupper($n); } ); /*Output: print_r(Closure) func__c(Closure Object ( [parameter] => Array ( [$n] => ) ) );*/ pre( $f, 'Closure' ); //Output: TEST-1 pre( $t->func(NULL) , 'Null');
كما تلاحظ ارفقت مخرجات كل سطر اسفل منه لكي يتسنى لك فهم الكود، ويمكنك تجربة الكلاس في جهازك والتعديل بما يناسبك..
أي ملاحظة أو استفسار يرجى مراسلتي.
تنزيل المشروع:
https://github.com/hishamdalal/overloadable