الجمعة، 22 سبتمبر 2017

كلاس التحميل الزائد الاصدار الجديد 3.8 php real overloading

بعد غياب طويل اعود لأكتب مقالة جديدة حول التحميل الزائد 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;
 }
}


2) قم باضافة دالة صغيرة لكلاسك الذي ترغب في تفعيل التحميل الزائد به.. هذه الدالة كالتالي:

 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


وهذا كلاس test كاملاً:

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



 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Blogger Templates | تعريب وتطوير : قوالب بلوجر معربة