2015-05-22 6 views
13

मुझे परेशान है कि क्यों ArrayIterator पर उप-वर्ग को इसकी __construct विधि कभी नहीं मिल रही है। इस उदाहरण पर विचार:ArrayIterator subclass के निर्माता को कभी क्यों नहीं कहा जाता है?

<?php 

class ConstructorException extends Exception {} 

class Foo extends ArrayObject { 
    function __construct($arr = array(), $flags = 0, $iterator = 'ArrayIterator') { 
     $iterator = 'FooIterator'; 
     parent::__construct($arr, $flags, $iterator); 
    } 
} 

class FooIterator extends ArrayIterator { 
    function __construct($array = array(), $flags = 0) { 
     throw new ConstructorException('HELLO WORLD'); // I AM NEVER CALLED. 
     parent::__construct($array, $flags); 
    } 
} 

try { 
    $f = new Foo(array(1, 2, 3)); 
    $it = $f->getIterator(); 
    if (get_class($it) !== 'FooIterator') { 
     throw new Exception('Expected iterator to be FooIterator.'); 
    } 
    die("FAIL\n"); 
} catch (ConstructorException $e) { 
    die("PASS\n"); 
} catch (\Exception $e) { 
    die(sprintf("ERROR: %s\n", $e->getMessage())); 
} 

दोनों पीएचपी 5.4 एक 5.5 में, परिणाम FAIL है। क्यूं कर?

+1

जॉन: http://3v4l.org/HnFQm

बिना किसी अपवाद के पिछला कोड, लेकिन उस iterator काम अच्छी तरह से दिखा वर्ग फू में) विधि getIterator की परिवर्तन (के साथ (सूचकांक और unsetting से जोड़ने) ब्लैकबर्न नोट करता है कि यह एचएचवीएम में काम करता है: http://3v4l.org/e9qF9/https://twitter.com/johnbillion/status/601900452293300224 –

+2

के माध्यम से यह php में एक आंतरिक समस्या प्रतीत होता है। [spl_array_object_new_ex] (https://github.com/php/php-src/blob/master/ext/spl/spl_array.c#L143) अभिभावक के कन्स्ट्रक्टर को कॉल न करें। इसकी सूचना दी जानी चाहिए? – Federkun

+0

बिल्ट-इन ऑब्जेक्ट्स पर कन्स्ट्रक्टर्स थोड़ा मजेदार हैं - उदाहरण के लिए नोट करें कि [SimpleXMLElement अपने कन्स्ट्रक्टर को 'अंतिम' होने की घोषणा करता है] (http://php.net/manual/en/simplexmlelement.construct।php), ताकि subclasses सही कारखाने की प्रक्रिया के माध्यम से जाना है। यह 'ArrayIterator' के बारे में सच नहीं है, हालांकि, शायद यह सिर्फ एक बग है। – IMSoP

उत्तर

2

@Leggendario यह कहने का अधिकार है कि समस्या spl_array_object_new_ex विधि में निहित है। हालांकि, अगर यह एक बग है तो मुझे यकीन नहीं है। हालांकि, यह वास्तव में मानक नहीं है कि यहां क्या हो रहा है।

निर्माता से iteratorClass चर (या setIteratorClass के माध्यम से सेट) सुझाव है कि इस वर्ग के instantiated हो जाता है जब भी हम ArrayObject से इटरेटर को पुनः प्राप्त। लेकिन यह नियमित "तत्काल" नहीं करता है, क्योंकि यह संभव नहीं है।

यह केवल इटरेटर (स्मृति आवंटित आवंटित) शुरू करेगा, लेकिन कन्स्ट्रक्टर को कॉल नहीं करेगा। ArrayIterator के निर्माता के रूप में कन्स्ट्रक्टर को कॉल करने के लिए यह समझ में आता है कि दो तर्क ($array और $flags) लेते हैं, और आपकी कक्षा ने अपना हस्ताक्षर बदल दिया होगा, शायद और भी (अनिवार्य मान) जोड़ना होगा।

आम तौर पर ArrayIterator (या RecursiveArrayIterator), आंतरिक कक्षाएं हैं और उनके साथ एक आंतरिक संरचना संलग्न है (मूल रूप से उन गुणों के अपने आंतरिक सेट की तरह जो आप सीधे PHP उपयोगकर्तालैंड से नहीं प्राप्त कर सकते हैं)। spl_array_object_new_ex इन आंतरिक मानों को सीधे सेट करेगा (सबसे विशेष रूप से, ce_get_iterator और ar_flags)। तो मूल रूप से यह ArrayIterator कन्स्ट्रक्टर के काम पर ले जाता है।

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

+0

असल में, मैं 'ऐरेइटरेटर' सबक्लास में विधियों को ओवरराइड कर सकता हूं और उन्हें अपेक्षित के रूप में कॉल किया जा सकता है: https://github.com/xwp/wp-customize-widgets-plus/blob/8a9cd09eae1e936378add62ddb992460649d9573/php/class-widget-settings। php # L51-L141 –

+0

आप देख सकते हैं कि 'ऑफसेट गेट() 'को PHP के सभी संस्करणों में सफलतापूर्वक ओवरराइड किया जा सकता है जिसमें' ArrayIterator' है (5.0.0 - 5.0.3 को छोड़कर): http://3v4l.org/tJgV5 –

+0

हाँ आप सही है। मैंने कोड को गलत तरीके से पढ़ा, और गलत परीक्षण के साथ इसकी जांच की (सरणीकरण के बजाय सरणी को क्रियाएं जोड़ना)। इसके बजाए, यह क्या करता है मैन्युअल रूप से दिए गए वर्ग ('FooIterator') से कुछ विधियों को मैन्युअल रूप से प्रतिलिपि/कैश करना आंतरिक इटरेटर कार्यों में। सबसे अधिक चीजों को गति देने की संभावना है, या क्योंकि पहली जगह कक्षा के "उचित" प्रारंभिक होने की कमी है। – JayTaph

3

विधि __construct आमतौर पर के रूप में, कहा जाता था नया उदाहरण बनाने पर,:

$it = new FooIterator(); 

इसलिए, यह कुछ समय लगेगा और मैं एक समाधान है: ओवरराइड विधि getIterator() अपने वर्ग फू में अगले करने के लिए अपने उदाहरण में (ArrayObject के उपवर्ग):

class Foo extends ArrayObject { 

    public function __construct($arr = array(), $flags = 0, $iterator = 'FooIterator') { 
     parent::__construct($arr, $flags, $iterator); 
    } 

    /** 
    * @return ArrayIterator 
    */ 
    public function getIterator() 
    { 
     $class = $this->getIteratorClass(); 
     return new $class($this); 
    } 
} 

इस सुधार के साथ अपने कोड होगा 'पास'।

सवाल से बदली हुई कोड का परिणाम: http://3v4l.org/R8PHr

+0

क्या आप अपने उदाहरण में कोड को अनुकूलित कर सकते हैं ताकि इसे पास करने के लिए पास किया जा सके? मैं वास्तव में बुलाया गया 'FooIterator :: __ निर्माण()' कैसे प्राप्त करने के लिए एक प्रदर्शन देखना चाहता हूं। यह मेरे उदाहरण में अपवाद फेंकने का एकमात्र उद्देश्य है। –

+0

@WestonRuter इसे जांचें: [लिंक] (http://3v4l.org/HnFQm) बस फ़ंक्शन getIterator() को ओवरराइड करें। –

+0

@WestonRuter यह भी जांचें: अपवाद के बिना, लेकिन पुनरावृत्त और अनसेट करने के साथ: http://3v4l.org/R8PHr –

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