PHP
प्रतिबिंब
खोज…
निजी और संरक्षित सदस्य चर तक पहुँचना
प्रतिबिंब का उपयोग अक्सर सॉफ़्टवेयर परीक्षण के भाग के रूप में किया जाता है, जैसे कि रनटाइम निर्माण / नकली वस्तुओं की तात्कालिकता। किसी समय में किसी वस्तु की स्थिति का निरीक्षण करने के लिए भी यह बहुत अच्छा है। यहां एक परीक्षण में रिफ्लेक्शन का उपयोग करने का एक उदाहरण है एक संरक्षित वर्ग के सदस्य को सत्यापित करने के लिए अपेक्षित मूल्य शामिल है।
नीचे एक कार के लिए एक बहुत ही बुनियादी वर्ग है। इसमें एक संरक्षित सदस्य चर है जिसमें कार के रंग का प्रतिनिधित्व करने वाला मान होगा। क्योंकि सदस्य चर संरक्षित है, हम इसे सीधे एक्सेस नहीं कर सकते हैं और क्रमशः इसके मूल्य को पुनः प्राप्त और सेट करने के लिए एक गेटर और सेटर विधि का उपयोग करना चाहिए।
class Car
{
protected $color
public function setColor($color)
{
$this->color = $color;
}
public function getColor($color)
{
return $this->color;
}
}
यह परीक्षण करने के लिए कई डेवलपर्स एक कार ऑब्जेक्ट बनाएंगे, कार का रंग कार सेट करें Car::setColor()
, Car::getColor()
का उपयोग कर रंग पुनः प्राप्त करें Car::getColor()
, और उनके द्वारा सेट किए गए रंग के लिए उस मूल्य की तुलना करें:
/**
* @test
* @covers \Car::setColor
*/
public function testSetColor()
{
$color = 'Red';
$car = new \Car();
$car->setColor($color);
$getColor = $car->getColor();
$this->assertEquals($color, $reflectionColor);
}
सतह पर यह ठीक लगता है। सब के बाद, सभी Car::getColor()
संरक्षित सदस्य चर Car::$color
का मूल्य लौटाता है। लेकिन यह परीक्षण दो तरह से त्रुटिपूर्ण है:
- यह
Car::getColor()
करता है जो इस परीक्षण के दायरे से बाहर है - यह
Car::getColor()
पर निर्भर करता हैCar::getColor()
जिसमें स्वयं बग हो सकता है जो परीक्षण को गलत सकारात्मक या नकारात्मक बना सकता है
आइए देखें कि हमें अपनी इकाई परीक्षण में Car::getColor()
उपयोग क्यों नहीं करना चाहिए और इसके बजाय परावर्तन का उपयोग करना चाहिए। मान लीजिए कि किसी डेवलपर को हर कार के रंग में "मेटैलिक" जोड़ने का काम सौंपा गया है। इसलिए वे Car::getColor()
को संशोधित करने का प्रयास करते हैं Car::getColor()
कार के रंग को "धातु" बनाने के लिए:
class Car
{
protected $color
public function setColor($color)
{
$this->color = $color;
}
public function getColor($color)
{
return "Metallic "; $this->color;
}
}
क्या आप त्रुटि देखते हैं? डेवलपर ने कार के रंग के लिए "मेटालिक" को प्रिपेंड करने के प्रयास में कॉन्टेक्टेशन ऑपरेटर के बजाय एक अर्ध-उपनिवेश का उपयोग किया। नतीजतन, जब भी Car::getColor()
कहा जाता है, तो "धातु" को वापस कर दिया जाएगा, भले ही कार का वास्तविक रंग क्या हो। परिणामस्वरूप हमारी Car::setColor()
इकाई परीक्षण विफल हो जाएगा , हालांकि Car::setColor()
पूरी तरह से ठीक काम करता है और इस परिवर्तन से प्रभावित नहीं था ।
तो हम Car::$color
सत्यापन कैसे करते हैं Car::$color
में वह मूल्य है जो हम Car::setColor()
माध्यम से सेट कर रहे हैं Car::setColor()
? हम सीधे सदस्य चर का निरीक्षण करने के लिए Refelection का उपयोग कर सकते हैं। तो कैसे हम ऐसा करते हैं? हम अपने कोड के लिए संरक्षित सदस्य चर को सुलभ बनाने के लिए Refelection का उपयोग कर सकते हैं ताकि यह मूल्य पुनः प्राप्त कर सके।
आइए पहले कोड देखें और फिर उसे तोड़ दें:
/**
* @test
* @covers \Car::setColor
*/
public function testSetColor()
{
$color = 'Red';
$car = new \Car();
$car->setColor($color);
$reflectionOfCar = new \ReflectionObject($car);
$protectedColor = $reflectionOfForm->getProperty('color');
$protectedColor->setAccessible(true);
$reflectionColor = $protectedColor->getValue($car);
$this->assertEquals($color, $reflectionColor);
}
यहाँ हम Car::$color
का मूल्य पाने के लिए परावर्तन का उपयोग कैसे कर रहे हैं Car::$color
ऊपर दिए गए कोड में Car::$color
:
- हम अपनी कार ऑब्जेक्ट का प्रतिनिधित्व करते हुए एक नया रिफ्लेक्शनऑनजेक्ट बनाते हैं
- हमें
Car::$color
लिए एक परावर्तन शक्ति प्राप्त होती है (यह "Car::$color
" का प्रतिनिधित्व करता हैCar::$color
चर) - हम
Car::$color
सुलभ बनाते हैं - हमें
Car::$color
का मूल्य मिलता है
जैसा कि आप परावर्तन का उपयोग करके देख सकते हैं कि हम Car::$color
कॉल के बिना Car::$color
का मूल्य प्राप्त कर सकते हैं Car::getColor()
या कोई अन्य Car::getColor()
फ़ंक्शन जो अमान्य परीक्षण परिणामों का कारण बन सकता है। अब Car::setColor()
लिए हमारा यूनिट टेस्ट Car::setColor()
सुरक्षित और सटीक है।
कक्षाओं या वस्तुओं की सुविधा का पता लगाना
कक्षाओं की सुविधा का पता लगाने आंशिक रूप से साथ किया जा सकता property_exists
और method_exists
कार्य करता है।
class MyClass {
public $public_field;
protected $protected_field;
private $private_field;
static $static_field;
const CONSTANT = 0;
public function public_function() {}
protected function protected_function() {}
private function private_function() {}
static function static_function() {}
}
// check properties
$check = property_exists('MyClass', 'public_field'); // true
$check = property_exists('MyClass', 'protected_field'); // true
$check = property_exists('MyClass', 'private_field'); // true, as of PHP 5.3.0
$check = property_exists('MyClass', 'static_field'); // true
$check = property_exists('MyClass', 'other_field'); // false
// check methods
$check = method_exists('MyClass', 'public_function'); // true
$check = method_exists('MyClass', 'protected_function'); // true
$check = method_exists('MyClass', 'private_function'); // true
$check = method_exists('MyClass', 'static_function'); // true
// however...
$check = property_exists('MyClass', 'CONSTANT'); // false
$check = property_exists($object, 'CONSTANT'); // false
ReflectionClass
साथ, स्थिरांक का भी पता लगाया जा सकता है:
$r = new ReflectionClass('MyClass');
$check = $r->hasProperty('public_field'); // true
$check = $r->hasMethod('public_function'); // true
$check = $r->hasConstant('CONSTANT'); // true
// also works for protected, private and/or static members.
नोट: property_exists
और method_exists
लिए भी, क्लास के नाम के बजाय ब्याज की एक वस्तु प्रदान की जा सकती है। प्रतिबिंब का उपयोग करते हुए, ReflectionObject
वर्ग का उपयोग ReflectionClass
बजाय किया जाना चाहिए।
निजी / संरक्षित विधियों का परीक्षण
कभी-कभी निजी और संरक्षित विधियों के साथ-साथ सार्वजनिक लोगों का परीक्षण करना उपयोगी होता है।
class Car
{
/**
* @param mixed $argument
*
* @return mixed
*/
protected function drive($argument)
{
return $argument;
}
/**
* @return bool
*/
private static function stop()
{
return true;
}
}
ड्राइव विधि का परीक्षण करने के लिए सबसे आसान तरीका प्रतिबिंब का उपयोग कर रहा है
class DriveTest
{
/**
* @test
*/
public function testDrive()
{
// prepare
$argument = 1;
$expected = $argument;
$car = new \Car();
$reflection = new ReflectionClass(\Car::class);
$method = $reflection->getMethod('drive');
$method->setAccessible(true);
// invoke logic
$result = $method->invokeArgs($car, [$argument]);
// test
$this->assertEquals($expected, $result);
}
}
यदि विधि स्थिर है तो आप कक्षा के उदाहरण के स्थान पर अशक्त हो जाते हैं
class StopTest
{
/**
* @test
*/
public function testStop()
{
// prepare
$expected = true;
$reflection = new ReflectionClass(\Car::class);
$method = $reflection->getMethod('stop');
$method->setAccessible(true);
// invoke logic
$result = $method->invoke(null);
// test
$this->assertEquals($expected, $result);
}
}