2011-02-06 19 views
6

के लिए PHP-instanceof-operator में हेरफेर करें, मैं कुछ कक्षाओं के लिए एक सामान्य रैपर-क्लास रखना चाहता हूं ताकि कुछ विधि-कॉल को रोक और छेड़छाड़ की जा सके। विधि-कॉल-अग्रेषण, अवरोध, अब तक कोई समस्या नहीं है। लेकिन थोड़ी देर सोचने के बाद, मुझे एक समस्या मिली जिसके लिए मेरे पास कोई समाधान नहीं है: मैं अपने आवेदन में हर जगह अंतर्निहित इंस्टेंस-ऑपरेटर का उपयोग कर रहा हूं। बेशक यह अब और काम नहीं करेगा, क्योंकि रैपर इसके अंदर कक्षा का उदाहरण नहीं है। मैं ऑपरेटर का उपयोग जारी रखना चाहता हूं और इसे किसी अन्य फ़ंक्शन के साथ प्रतिस्थापित नहीं करना चाहता हूं।रैपर-क्लास

क्या इस समस्या के लिए समाधान लागू करने का कोई तरीका है? यह ऑपरेटर कैसे काम करता है? क्या यह कक्षाओं का कोर-फ़ंक्शन कॉल करता है जिसे मैं शायद अपने रैपर में ओवरराइट करने में सक्षम हूं?

मुझे पता है कि यह इस ऑपरेटर में हेरफेर करने के लिए वास्तव में "साफ" समाधान नहीं होगा, लेकिन मुझे लगता है कि यह मेरे लिए सबसे आसान समाधान होगा। और जैसा कि हम जानते हैं, वहाँ PHP में बहुत सी बातें जो कि स्वच्छ नहीं हैं

अपने जवाब के लिए धन्यवाद, एक अंतरफलक के बजाय ठोस वर्ग बेन

+0

यह बिल्कुल वैसा ही समस्या मैं पता करने के लिए कोशिश कर रहा हूँ है। क्या आप इसे किसी भी तरह काम करने के लिए प्रबंधित करते थे? यदि ऐसा है, तो मुझे बहुत दिलचस्पी होगी कि आपने यह कैसे किया .. – mrjames

उत्तर

0

उपयोग कर रहे हैं ... :-)। इंटरफ़ेस को रैपर और कंक्रीट क्लास पर लागू करें।

http://de3.php.net/manual/en/language.oop5.interfaces.php

+0

मैं कई कक्षाओं के लिए एक सामान्य रैपर रखना चाहता हूं जो एक ही इंटरफ़ेस को लागू नहीं करते हैं और समान कक्षाएं नहीं हैं। तो आपके समाधान के साथ मुझे हर अलग वर्ग के लिए एक रैपर-वर्ग की आवश्यकता है। या मैं गलत हूँ? – Ben

+0

@ बेन आप सही हैं। यह नुकसान है। – Gordon

2

देखें मैं नहीं जानता कि क्या यह संभव है रास्ते में एक instanceof ऑपरेटर आप चाहते हैं चाल (उपवर्ग के रूप में एक वर्ग को पहचान अगर यह नहीं है) करने के लिए, लेकिन मैं मैं एक समाधान है कि आपकी आवश्यकताओं के अनुरूप पाया लगता है । अगर मैं आपकी समस्या को सही ढंग से समझता हूं तो आप बस अपने पूरे कोड में न्यूनतम परिवर्तन के साथ किसी भी वर्ग में कुछ तरीकों को इंजेक्ट करना चाहते हैं।

मुझे लगता है कि इस मामले में समाधान तैयार करने का सबसे अच्छा तरीका लक्षणों का उपयोग कर रहा है (वर्णित here)। लक्षणों के साथ आप सीधे विरासत के बिना किसी वर्ग में विधियों को जोड़ सकते हैं और यह बेस क्लास से विधियों को ओवरराइट कर सकता है। लक्षणों के साथ ओवरराइटिंग विधि के लिए आपको निश्चित रूप से उप-वर्गों की आवश्यकता होती है लेकिन उन्हें गतिशील रूप से बनाया जा सकता है। मुझे आपकी रैपिंग प्रक्रिया के बारे में कुछ नहीं पता लेकिन मेरे समाधान में मैंने इसके लिए एक विशेष कक्षा का उपयोग किया। आइए मेरे समाधान को देखें:

namespace someNameSpace; 

//this is one of your class that you want to wrap - it can be declare under some other namespace if you need 
class yourBaseClass { } 

//your wrapper class as a trait 
trait yourWrapper { } 

//class for wrapping any object 
class ObjectWrapperClass 
{ 
    //method for change object class (described on http://stackoverflow.com/a/3243949/4662836) 
    protected static function objectToObject($instance, $className) 
    { 
     return unserialize(sprintf('O:%d:"%s"%s', strlen($className), $className, strstr(strstr(serialize($instance), '"'), ':'))); 
    } 

    //wrapping method 
    //$object is a object to be wrapped 
    //$wrapper is a full name of the wrapper trait 
    public static function wrap($object, $wrapper) 
    { 
     //take some information about the object to be wrapped 
     $reflection = new \ReflectionClass($object); 
     $baseClass = $reflection->getShortName(); 
     $namespace = $reflection->getNamespaceName(); 

     //perpare the name of the new wrapped class 
     $newClassName = "{$baseClass}Wrapped"; 

     //if new wrapped class has not been declared before we need to do it now 
     if (!class_exists($newClassName)) { 
      //prepare a code of the wrapping class that inject trait 
      $newClassCode = "namespace {$namespace} { class {$newClassName} extends {$baseClass} { use {$wrapper}; } }"; 

      //run the prepared code 
      eval($newClassCode); 
     } 

     //change the object class and return it 
     return self::objectToObject($object, $namespace . '\\' . $newClassName); 
    } 

} 

//lets test this solution 

$originalObject = new yourBaseClass(); 

$wrappedObject = ObjectWrapperClass::wrap($originalObject, 'yourWrapper'); 

if ($wrappedObject instanceof yourBaseClass) { 
    echo 'It is working'; 
} 

जैसा कि आप देख सकते हैं सब कुछ लपेटने की प्रक्रिया के दौरान होता है।

यदि आपके पास अधिक रैपर हैं तो आप नए लपेटे हुए क्लास नाम को अन्य तरीके से तैयार कर सकते हैं (उदाहरण के लिए रैपर नाम के साथ कोरलाइट किया जाना चाहिए)।

+0

यह एक चालाक समाधान है, लेकिन मैं प्रतिबिंब का उपयोग नहीं करना चाहता हूं और न ही eval क्योंकि वे बहुत धीमी हैं, और वे मेरे उपयोग के मामले के लिए बहुत उपयोग किया जाएगा। हो सकता है कि मैं कोड पीढ़ी के साथ समस्या का समाधान करूंगा और प्रत्येक वर्ग के लिए एक रैपर बनाने के लिए मुझे लपेटने की ज़रूरत है, यदि रैपर लिपटे वर्गों से विस्तारित होते हैं तो मेरी समस्या हल हो जाती है, लेकिन मुझे वास्तव में यह पसंद नहीं है। मैं इस समस्या को हल करने के लिए एक जादू विधि __instanceOf वहाँ था चाहते हैं। वैसे भी धन्यवाद। – hchinchilla

0

decorator pattern पर एक नज़र डालें। यदि आपके रैपर/लपेटा वर्ग एक ही इंटरफ़ेस को लागू करते हैं, तो आप सबकुछ सुन्दर तरीके से कर सकते हैं (और पूरे कोड में इंटरफ़ेस का उदाहरण उपयोग करें)।

क्या इस समस्या के लिए समाधान लागू करने का कोई तरीका है? यह ऑपरेटर कैसे काम करता है? क्या यह कक्षाओं का कोर-फ़ंक्शन कॉल करता है जिसे मैं शायद अपने रैपर में ओवरराइट करने में सक्षम हूं?

आप उदाहरण ऑपरेटर में हेरफेर नहीं कर सकते हैं।

class php_class { 
    public $interfaces = array(); // array of php_class objects (php classes can implement more than one interface) 
    public $parent = null; // php_class object (php classes can only extend one class) 
} 

function instanceof_operator($implementation, $abstraction) { 
    // forward recursion (iterates recursively through interfaces until a match is found) 
    for($i=0; $i<count($implementation->interfaces); $i++) { 
     if(instanceof_operator($implementation->interfaces[$i], $abstraction)) { 
      return true; 
     } 
    } 
    // backward recursion (iterates recursively through parents until a match is found) 
    while($implementation!=null) { 
     if($implementation == $abstraction) { 
      return true; 
     } 
     $implementation = $implementation->parent; 
    } 
    // no match was found 
    return false; 
} 

जब भी आप को लागू करने के एक वर्ग की घोषणा /, एक अंतरफलक/वर्ग का विस्तार कल्पना एक प्रवेश $ इंटरफेस या पर जमा किया जाता है: चूंकि आप कैसे instanceof ऑपरेटर कार्यान्वित किया जाता है रुचि रखते थे, यहाँ मूल सी कोड का एक पीएचपी प्रतिनिधित्व है $ पेरेंट फ़ील्ड्स जो स्क्रिप्ट समाप्त होने तक अपरिवर्तनीय बनी हुई है।

2

शायद मैं अपनी आवश्यकताओं के लिए एक समाधान का वर्णन कर सकते हैं। (अस्वीकरण: मैं Go! AOP Framework का लेखक हूं) आपके विवरण से ऐसा लगता है कि आप कक्षा को छूए बिना अपने तरीकों से गतिशील रूप से अतिरिक्त तर्क जोड़ना चाहते हैं। अगर मैं सही हूँ, तो आप उस अपने स्रोत कोड के लिए इंटरसेप्टर की एक अवधारणा का परिचय Aspect-Oriented Paradigm पर एक नज़र हो सकता था, क्या अधिक महत्वपूर्ण है - अपने मूल वर्गों अछूता हो जाएगा।

कोई विचार नहीं है, यह आपके कोड पर कैसे लागू किया जा सकता है, आप मेरे लेख http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/ पर भी देख सकते हैं जो शास्त्रीय ऑब्जेक्ट-उन्मुख पैटर्न जैसे सजावटी, प्रॉक्सी के सभी फायदे और नुकसान को हाइलाइट करता है। मैं निष्कर्ष निकाल सकता हूं कि क्रॉस-कटिंग चिंताओं को हल करने के लिए आवश्यक जटिलता और PHP की सीमाओं के कारण ऑब्जेक्ट उन्मुख तरीके से सभी इंटरसेप्टर्स को अलग-अलग मॉड्यूल में नहीं निकाला जा सकता है। (पहलुओं कहा जाता है) अलग वर्गों में (सलाह कहा जाता है) AOP परंपरागत OOP मॉडल फैली हुई है, तो यह इंटरसेप्टर निकालने के लिए संभव नहीं होगा।

एओपी की शानदार विशेषता यह है कि यह आपके मूल वर्ग के नाम रखती है और इसका मतलब है कि आपको अपने कोड में टाइपहिंट्स नहीं बदलना चाहिए या instanceof ऑपरेटर को हाइजैक भी नहीं करना चाहिए। आपको अतिरिक्त तर्क के साथ अपनी कक्षा मिल जाएगी।