2016-01-14 11 views
12

मैं PHP के SOAPClient class का उपयोग कर एक SOAP API के साथ संचार कर रहा हूं।एसओएपी प्रतिक्रिया वस्तु से निर्माता नहीं कहा जा रहा है

The classmap option can be used to map some WSDL types to PHP classes. This option must be an array with WSDL types as keys and names of PHP classes as values.

मैं जैसे अपने ग्राहक बनाने के लिए:: विकल्पों में से एक वहाँ आप प्रकार अपने स्वयं के वर्गों के साथ WSDL फ़ाइल में निर्दिष्ट पुन: मैप की सुविधा देता है

$api = new SOAPClient('http://example.com/soap.wsdl', [ 
    'location' => 'http://example.com/soap/endpoint', 
    'soap_version' => SOAP_1_2, 
    'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP, 
    'cache_wsdl' => WSDL_CACHE_BOTH, 
    'classmap' => [ 
     'APIResultObject' => 'Result' 
    ], 
    # TODO: Set for debug only? 
    'trace' => TRUE, 
    'exceptions' => TRUE 
]); 

यह काम करता है और जब मैं $api->method('param') कहते हैं, मैं Result ऑब्जेक्ट वापस प्राप्त करें (केवल StdClass ऑब्जेक्ट के बजाय)। समस्या यह है कि Result::__construct() विधि कभी नहीं कहा जाता है, इसलिए Result के कुछ निजी गुण कभी सेट नहीं होते हैं।

यहाँ Result है:

Notice: Undefined property: Result::$data

मैं कैसे जब एक सोप हो रही करने के लिए निर्माता समारोह मैं की जरूरत है कॉल कर सकते हैं:

class DataClass{ 
    protected $data; 

    function __construct(){ 
     $this->data = ['a' => 0, 'b' => 1, 'c' => 2]; 
    } 
} 

class Result extends DataClass{ 
    public $value, $name, $quantity; 

    function __construct(array $values){ 
     parent::__construct(); 

     foreach(['value', 'name', 'quantity'] as $var){ 
      $this->$var = isset($values[$var]) ? $values[$var] : NULL; 
     } 
    } 

    function getData(){ 
     return $this->data[$this->name]; 
    } 
} 

क्या हो रहा है मैं $api->method('param')->getData() कर रहा हूँ और निम्न त्रुटि हो रही है प्रतिक्रिया? मैंने __wakeup() का उपयोग करने का प्रयास किया, लेकिन ऐसा लगता है कि यह भी काम नहीं करता है।

पीएस मैंने इसे एक छोटे से कामकाज के साथ हल किया, लेकिन मुझे नहीं लगता कि यह आदर्श है। यहाँ मैं क्या किया है:

function getData(){ 
    if($this->data === NULL){ 
     parent::__construct(); 
    } 

    return $this->data[$this->name]; 
} 
+0

हैलो, im सिर्फ उत्सुक कैसे क्या आप $ api-> विधि ('param') -> getName() का उपयोग करते समय getData फ़ंक्शन को कॉल करते हैं? मुझे pls downvote मत करो, मैं बस कुछ सीखना चाहता हूं ... मैंने सोचा था कि कक्षा के पास विधि के लिए विधि का नाम होना चाहिए, लेकिन मैं वहां कोई विधि देख सकता हूं नाम() जो मैं पूछ रहा हूं ... अग्रिम में Thx आपका उत्तर :-) – Redrif

+0

@Redrif: यह सिर्फ एक छोटा टाइपो था, मुझे खेद है। मैंने इसे अपने वास्तविक कोड से कॉपी और पेस्ट करने की कोशिश की (और चीजों के नामों को बदलें)। यह वास्तव में '$ api-> विधि ('param') होना चाहिए -> getData()'। यहां कोई जादू नहीं है, सिर्फ प्रश्न में एक टाइपो। –

उत्तर

4

यह ज्ञात व्यवहार (bug report) है।

के रूप में बग रिपोर्ट में सलाह दी someoned (वेब ​​पर miceleparkip डी डॉट):

This is not a bug. It's quite normal.

The soap object is created on the server side. So the constructor is just called on the server.

मैं उसकी स्थिति को साझा करें।

परवर्ती टिप्पणी ही बग रिपोर्ट में (hotblocks पर php nl डॉट) इससे सहमत नहीं हैं:

The server doesn't create objects, it sends XML. The client decodes that XML and creates the objects.

हालांकि यह देखने की एक तकनीकी पोंट से निर्विवाद रूप से सच है, "सार" वस्तु यकीनन बनाई गई है सर्वर की तरफ। चाहे इसे पहले एक्सएमएल में परिवर्तित किया गया हो, फिर क्लाइंट साइड पर पुनर्निर्मित एक निम्न स्तर की चिंता है कि एप्लिकेशन परत को इसके बारे में पता नहीं होना चाहिए।

आपके आवेदन सर्वर द्वारा प्रदान की तुलना में अधिक सुविधाओं के साथ वस्तुओं की जरूरत है, मैं एक स्थानीय वर्ग कि एक निर्माता तर्क के रूप में SOAPClient द्वारा बनाई गई वस्तु लेता बनाना होगा:

class MySoapResultClass { 
    // whatever 
} 

class LocalApplicationClass { 

    public function __construct(MySoapResultClass $soapResult) { 

     // your local initialization code 
     $this->data = ['a' => 0, 'b' => 1, 'c' => 2]; 

     // then either extract your data from $soapResult, 
     // or just store a reference to it 
    } 

    public function getData(){ 
     return $this->data[$this->name]; 
    } 
} 

$api = new SOAPClient(...); 
$soapResult = $api->method('param'); 
$myUsefulObject = new LocalApplicationClass($soapResult); 
+1

मेरे लिए यह भी इस विशिष्ट मामले से अधिक में एक सर्वोत्तम अभ्यास दृष्टिकोण की तरह दिखता है। – FloydThreepwood

5

अद्यतन: एक और वैकल्पिक हल

आप किसी अन्य वर्ग है कि निर्माताओं को ठीक से लागू करेगा में SoapClient लपेट कर सकते हैं। परेशानी को बचाने के लिए, कक्षा जांचती है कि इसकी आवश्यकता है या नहीं। अभी भी आदर्श नहीं है, लेकिन मुझे लगता है कि यह इस तरह से आसान है।

class ActiveSOAPClient extends SoapClient { 
    // Intercept all calls to class. 
    public function __call($func, $params) { 
     // Pass it to parent class. 
     $ret = parent::__call($func, $params); 
     // If the answer is an object... 
     if (is_object($ret) 
      // Taboo functions that will pass unhindered. 
      // && (!in_array($func, [ ARRAY OF EXCLUDED_FUNCTIONS ])) 
     ) { 
      // ...and the object is in the auto classmap... 
      if (false !== array_search(get_class($ret), $this->_classmap, true)) { 
       // ...then assume it's an incomplete object and try activating it. 
       $ret->__construct(); 
      } 
     } 
     return $ret; 
    } 
} 

लाभ यह है कि ActiveSOAPClient वर्ग अपने खुद के तर्क के बारे में कोई जानकारी नहीं है की जरूरत नहीं है है, और अपने तर्क को बदलने की जरूरत नहीं है।

समस्या

मुझे लगता है कि इस व्यवहार जानबूझकर है या एक ज्ञात बग, (जैसे कि, वहाँ किसी न किसी कारण समस्या के पीछे होना चाहिए) क्योंकि मैनुअल पृष्ठ में यह already noted as of seven years ago है।

मैंने PHP 5.5.6 से स्रोत कोड की जांच की। जहाँ तक मेरा php_encoding.c में कोड,

/* Struct encode/decode */ 
static zval *to_zval_object_ex(encodeTypePtr type, xmlNodePtr data, zend_class_entry *pce TSRMLS_DC) 
{ 
     zval *ret; 
     xmlNodePtr trav; 
     sdlPtr sdl; 
     sdlTypePtr sdlType = type->sdl_type; 
     zend_class_entry *ce = ZEND_STANDARD_CLASS_DEF_PTR; 
     zval *redo_any = NULL; 

     if (pce) { 
       ce = pce; 
     } else if (SOAP_GLOBAL(class_map) && type->type_str) { 
       zval    **classname; 
       zend_class_entry *tmp; 

       if (zend_hash_find(SOAP_GLOBAL(class_map), type->type_str, strlen(type->type_str)+1, (void**)&classname) == SUCCESS && 
        Z_TYPE_PP(classname) == IS_STRING && 
        (tmp = zend_fetch_class(Z_STRVAL_PP(classname), Z_STRLEN_PP(classname), ZEND_FETCH_CLASS_AUTO TSRMLS_CC)) != NULL) { 
         ce = tmp; 
       } 
     } 

पढ़ सकते हैं ... अगर एक वर्ग नक्शा परिभाषित किया गया है, और जाना जाता है, zend_fetch_class() शुरू हो जाती है।

मेरा मानना ​​है कि कुछ प्रकार के ctor() फ़ंक्शन को बाद में नोड से प्राप्त मूल्यों पर बुलाया जाना चाहिए, जैसा कि किया गया है। पीडीओ में :: fetchObject (फ़ाइल "ext/pdo/pdo_stmt.c" देखें)।

वर्तमान में, ऐसा नहीं लगता है। संभवतः यह एक्सएमएल पेड़ में ऑब्जेक्ट्स के मूल्यांकन के क्रम के कारण है, जो कन्स्ट्रक्टर को उचित तर्क प्रदान करता है, लेकिन इस बिंदु पर मैं बस अनुमान लगा रहा हूं।

हालांकि, तुम वहाँ समय कोई "आधिकारिक" समाधान होने में सही कर रहे हैं (आप भी बहुत कुछ आधिकारिक स्रोत कोड से नहीं मिल सकता है)।

स्रोत

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

शायद पैरामीटर-कम कन्स्ट्रक्टर और कोई विनाशक के साथ किसी ऑब्जेक्ट के सबसे सरल मामले में छोड़कर, कोड में आवश्यक संशोधन मुझे मामूली नहीं लग रहा है।

+0

मुझे वास्तव में आपका 'ActiveSOAPClient' विचार पसंद है! इससे 'माता-पिता :: __ निर्माण();' को हर विधि में रखने की आवश्यकता होती है, जिसके लिए इसकी आवश्यकता होती है। –

संबंधित मुद्दे