سؤال

هل هناك أي طريقة لإعادة تعريف الفصل أو بعض أساليبه دون استخدام الميراث النموذجي؟على سبيل المثال:

class third_party_library {
    function buggy_function() {
        return 'bad result';
    }
    function other_functions(){
        return 'blah';
    }
}

ماذا يمكنني أن أفعل ليحل محل buggy_function()؟ومن الواضح أن هذا ما أود أن أفعله

class third_party_library redefines third_party_library{
    function buggy_function() {
        return 'good result';
    }
    function other_functions(){
        return 'blah';
    }
}

هذه هي معضلتي بالضبط:لقد قمت بتحديث مكتبة تابعة لجهة خارجية تكسر الكود الخاص بي.لا أريد تعديل المكتبة مباشرة، لأن التحديثات المستقبلية قد تؤدي إلى كسر الكود مرة أخرى.أنا أبحث عن طريقة سلسة لاستبدال طريقة الفصل.

لقد وجدت هذا مكتبة يقول أنه يمكن أن يفعل ذلك، لكنني حذر لأنه عمره 4 سنوات.

يحرر:

كان يجب أن أوضح أنه لا يمكنني إعادة تسمية الفصل من third_party_library ل magical_third_party_library أو أي شيء آخر بسبب قيود الإطار.

لأغراضي، هل من الممكن إضافة وظيفة إلى الفصل فقط؟أعتقد أنه يمكنك القيام بذلك في لغة C# باستخدام ما يسمى "فئة جزئية".

هل كانت مفيدة؟

المحلول

تسمى ترقيع القرد.لكن PHP ليس لديها دعم أصلي لها.

على الرغم من ذلك، كما أشار آخرون أيضًا، فإن مكتبة رانكيت متاح لإضافة الدعم للغة وهو خليفة ل com.classkit.وعلى الرغم من أنه يبدو أنه كان كذلك متروك بواسطة منشئه (بعد أن ذكر أنه غير متوافق مع PHP 5.2 والإصدارات الأحدث)، يبدو أن المشروع الآن يحتوي على منزل جديد ومشرف.

مازلت لا أستطيع أن أقول أنني معجب من نهجها.لقد بدا لي دائمًا أن إجراء التعديلات من خلال تقييم سلاسل التعليمات البرمجية أمر خطير ويصعب تصحيحه.

ما زال، runkit_method_redefine يبدو أنه ما تبحث عنه، ويمكن العثور على مثال لاستخدامه في /tests/runkit_method_redefine.phpt في المستودع:

runkit_method_redefine('third_party_library', 'buggy_function', '',
    'return \'good result\''
);

نصائح أخرى

يبدو أن runkit حل جيد ولكن لم يتم تمكينه افتراضيًا ولا تزال أجزاء منه تجريبية.لذلك قمت باختراق فئة صغيرة تحل محل تعريفات الوظائف في ملف فئة.مثال على الاستخدام:

class Patch {

private $_code;

public function __construct($include_file = null) {
    if ( $include_file ) {
        $this->includeCode($include_file);
    }
}

public function setCode($code) {
    $this->_code = $code;
}

public function includeCode($path) {

    $fp = fopen($path,'r');
    $contents = fread($fp, filesize($path));
    $contents = str_replace('<?php','',$contents);
    $contents = str_replace('?>','',$contents);
    fclose($fp);        

    $this->setCode($contents);
}

function redefineFunction($new_function) {

    preg_match('/function (.+)\(/', $new_function, $aryMatches);
    $func_name = trim($aryMatches[1]);

    if ( preg_match('/((private|protected|public) function '.$func_name.'[\w\W\n]+?)(private|protected|public)/s', $this->_code, $aryMatches) ) {

        $search_code = $aryMatches[1];

        $new_code = str_replace($search_code, $new_function."\n\n", $this->_code);

        $this->setCode($new_code);

        return true;

    } else {

        return false;

    }

}

function getCode() {
    return $this->_code;
}
}

ثم قم بتضمين الفئة المراد تعديلها وإعادة تعريف أساليبها:

$objPatch = new Patch('path_to_class_file.php');
$objPatch->redefineFunction("
    protected function foo(\$arg1, \$arg2)
    {   
        return \$arg1+\$arg2;
    }");

ثم قم بتقييم الكود الجديد:

eval($objPatch->getCode());

الخام قليلا لكنه يعمل!

من أجل الاكتمال - يتوفر تصحيح القرد في PHP من خلال runkit.لمزيد من التفاصيل، انظر runkit_method_redefine().

ماذا عن لفه في فئة أخرى مثل

class Wrapper {
 private $third_party_library;
 function __construct() { $this->third_party_library = new Third_party_library(); }
 function __call($method, $args) {
  return call_user_func_array(array($this->third_party_library, $method), $args);
 }
}

نعم، يطلق عليه extend:

<?php
class sd_third_party_library extends third_party_library
{
    function buggy_function() {
        return 'good result';
    }
    function other_functions(){
        return 'blah';
    }
}

لقد سبقت بـ "sd".؛-)

ضع في اعتبارك أنه عند توسيع فئة لتجاوز الأساليب، يجب أن يتطابق توقيع الطريقة مع الأصل.فمثلا إذا قال الأصل buggy_function($foo, $bar), يجب أن يتطابق مع المعلمات الموجودة في الفصل الذي يمتد إليه.

PHP مطول جدًا في هذا الشأن.

للأشخاص الذين ما زالوا يبحثون عن هذه الإجابة.

يجب عليك استخدام يمتد بالاشتراك مع مساحات الأسماء.

مثله:

namespace MyCustomName;

class third_party_library extends \third_party_library {
  function buggy_function() {
      return 'good result';
  }
  function other_functions(){
      return 'blah';
  }
}

ثم لاستخدامه قم بهذه الطريقة:

use MyCustomName\third_party_library;

$test = new third_party_library();
$test->buggy_function();
//or static.
third_party_library::other_functions();

يحتوي Zend Studio وPDT (بيئة تطوير متكاملة تعتمد على Eclipse) على بعض أدوات الانكسار المضمنة.ولكن لا توجد أساليب مدمجة للقيام بذلك.

كما أنك لا ترغب في وجود تعليمات برمجية سيئة في نظامك على الإطلاق.لأنه يمكن أن يتم استدعاؤه عن طريق الخطأ.

إذا كانت المكتبة تنشئ فئة سيئة بشكل صريح ولا تستخدم نظام تحديد المواقع أو التبعية، فلن يحالفك الحظ.لا توجد طريقة لتجاوز طريقة ما في فئة أخرى إلا إذا كنت تنتمي إلى فئة فرعية.قد يكون الحل هو إنشاء ملف تصحيح يعمل على إصلاح المكتبة، بحيث يمكنك ترقية المكتبة وإعادة تطبيق التصحيح لإصلاح تلك الطريقة المحددة.

قد تتمكن من القيام بذلك باستخدام runkit. http://php.net/runkit

هناك دائمًا توسيع الفصل بطريقة جديدة ومناسبة واستدعاء تلك الفئة بدلاً من الفئة التي تجرها الدواب.

class my_better_class Extends some_buggy_class {
    function non_buggy_function() {
        return 'good result';
    }
}

(آسف على التنسيق الرديء)

يمكنك عمل نسخة من فصل المكتبة، مع كل شيء كما هو باستثناء اسم الفصل.ثم تجاوز تلك الفئة المعاد تسميتها.

إنها ليست مثالية، ولكنها تعمل على تحسين رؤية تغييرات الفئة الممتدة.إذا قمت بإحضار المكتبة باستخدام شيء مثل Composer، فسيتعين عليك الالتزام بالنسخة للتحكم بالمصادر وتحديثها عند تحديث المكتبة.

في حالتي كان الإصدار القديم من https://github.com/bshaffer/oauth2-server-php.لقد قمت بتعديل أداة التحميل التلقائي للمكتبة لجلب ملف الفصل الدراسي الخاص بي بدلاً من ذلك.اتخذ ملف صفي الاسم الأصلي وقام بتوسيع نسخة منسوخة من أحد الملفات.

نظرًا لأنه يمكنك دائمًا الوصول إلى الكود الأساسي في PHP، أعد تعريف وظائف الفئة الرئيسية التي تريد تجاوزها على النحو التالي، وهذا من شأنه أن يترك واجهاتك سليمة:

class third_party_library {
    public static $buggy_function;
    public static $ranOnce=false;

    public function __construct(){
        if(!self::$ranOnce){
            self::$buggy_function = function(){ return 'bad result'; };
            self::$ranOnce=true;
        }
        .
        .
        .
    }
    function buggy_function() {
        return self::$buggy_function();
    }        
}

يمكنك لسبب ما استخدام متغير خاص ولكن بعد ذلك لن تتمكن من الوصول إلى الوظيفة إلا عن طريق توسيع الفصل أو المنطق داخل الفصل.وبالمثل، من الممكن أن ترغب في أن يكون لكائنات مختلفة من نفس الفئة وظائف مختلفة.إذا كان الأمر كذلك، فلا تستخدم ثابتًا، ولكن عادةً ما تريد أن تكون ثابتًا حتى لا تكرر استخدام الذاكرة لكل كائن تم إنشاؤه.يضمن رمز "ranOnce" أنك تحتاج فقط إلى تهيئته مرة واحدة للفصل الدراسي، وليس لكل فصل $myObject = new third_party_library()

الآن، لاحقًا في التعليمات البرمجية الخاصة بك أو في فئة أخرى - عندما يصل المنطق إلى نقطة تحتاج فيها إلى تجاوز الوظيفة - ما عليك سوى القيام بما يلي:

$backup['buggy_function'] = third_party_library::$buggy_function;
third_party_library::$buggy_function = function(){
    //do stuff
    return $great_calculation;
}
.
.
.  //do other stuff that needs the override
.  //when finished, restore the original function
.
third_party_library::$buggy_function=$backup['buggy_function'];

كملاحظة جانبية، إذا كنت تقوم بجميع وظائف صفك بهذه الطريقة وتستخدم مخزنًا للمفتاح/القيمة يعتمد على سلسلة مثل public static $functions['function_name'] = function(...){...}; يمكن أن يكون هذا مفيدًا للتفكير.ليس كثيرًا في PHP مثل اللغات الأخرى لأنه يمكنك بالفعل الحصول على أسماء الفئات والوظائف، ولكن يمكنك حفظ بعض المعالجة ويمكن للمستخدمين المستقبليين لفصلك استخدام التجاوزات في PHP.ومع ذلك، فهو مستوى إضافي من المراوغة، لذا سأتجنب استخدامه في الفئات البدائية حيثما أمكن ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top