आपने अभी-अभी एक नया Laravel एप्लीकेशन इंस्टॉल किया है, इसे बूट किया है, और स्वागत पृष्ठ प्राप्त किया है। हर किसी की तरह, आप यह देखने की कोशिश करते हैं कि यह कैसे रेंडर किया गया है, इसलिए आप web.php
फ़ाइल में जाते हैं और इस कोड का सामना करते हैं:
<?php use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); });
यह स्पष्ट है कि हमें स्वागत दृश्य कैसे मिला, लेकिन आप इस बारे में उत्सुक हैं कि लारवेल का राउटर कैसे काम करता है, इसलिए आप कोड में गोता लगाने का फैसला करते हैं। प्रारंभिक धारणा यह है: एक Route
क्लास है जिस पर हम एक स्थिर विधि get()
कॉल कर रहे हैं। हालाँकि, इसे क्लिक करने पर, वहाँ कोई get()
विधि नहीं है। तो, किस तरह का काला जादू हो रहा है? आइए इसे समझें!
कृपया ध्यान दें कि मैंने अधिकांश PHPDocs को हटा दिया है और सरलता के लिए प्रकारों को इनलाइन कर दिया है, "..." अधिक कोड को संदर्भित करता है।
मैं दृढ़ता से सुझाव देता हूं कि किसी भी भ्रम से बचने के लिए आप अपना आईडीई खोलें और कोड का अनुसरण करें।
हमारे उदाहरण का अनुसरण करते हुए, आइए Route
वर्ग का अन्वेषण करें।
<?php namespace Illuminate\Support\Facades; class Route extends Facade { // ... protected static function getFacadeAccessor(): string { return 'router'; } }
यहाँ बहुत कुछ नहीं है, बस getFacadeAccessor()
विधि है जो स्ट्रिंग router
लौटाती है। इसे ध्यान में रखते हुए, चलिए पैरेंट क्लास पर चलते हैं।
<?php namespace Illuminate\Support\Facades; use RuntimeException; // ... abstract class Facade { // ... public static function __callStatic(string $method, array $args): mixed { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); } }
पैरेंट क्लास के अंदर, बहुत सारे तरीके हैं, हालांकि get()
तरीका नहीं है। लेकिन एक दिलचस्प तरीका है, __callStatic()
तरीका। यह एक जादुई तरीका है, जिसे तब भी बुलाया जाता है जब कोई अपरिभाषित स्थिर तरीका, जैसे कि हमारे मामले में get()
, बुलाया जाता है। इसलिए, हमारा कॉल __callStatic('get', ['/', Closure()])
दर्शाता है कि Route::get()
को बुलाते समय हमने क्या पास किया, रूट /
और एक Closure()
जो स्वागत दृश्य लौटाता है।
जब __callStatic()
ट्रिगर होता है, तो यह सबसे पहले getFacadeRoot()
को कॉल करके एक वेरिएबल $instance
सेट करने का प्रयास करता है, $instance
उस वास्तविक क्लास को रखता है जिस पर कॉल को अग्रेषित किया जाना चाहिए, आइए इसे करीब से देखें, यह थोड़ी देर में समझ में आ जाएगा
// Facade.php public static function getFacadeRoot() { return static::resolveFacadeInstance(static::getFacadeAccessor()); }
अरे, देखो यह चाइल्ड क्लास Route
से getFacadeAccessor()
है, जिसे हम जानते हैं कि स्ट्रिंग router
लौटाता है। यह router
स्ट्रिंग फिर resolveFacadeInstance()
को पास की जाती है, जो इसे एक क्लास में हल करने का प्रयास करती है, एक तरह की मैपिंग जो कहती है "यह स्ट्रिंग किस क्लास का प्रतिनिधित्व करती है?" आइए देखें।
// Facade.php protected static function resolveFacadeInstance($name) { if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } if (static::$app) { if (static::$cached) { return static::$resolvedInstance[$name] = static::$app[$name]; } return static::$app[$name]; } }
यह सबसे पहले जाँचता है कि क्या स्टैटिक ऐरे, $resolvedInstance
, में दिए गए $name
(जो, फिर से, router
है) के साथ कोई मान सेट है। यदि यह कोई मिलान पाता है, तो यह केवल उस मान को लौटाता है। यह प्रदर्शन को थोड़ा अनुकूलित करने के लिए Laravel कैशिंग है। यह कैशिंग एक ही अनुरोध के भीतर होती है। यदि इस विधि को एक ही अनुरोध के भीतर एक ही तर्क के साथ कई बार कॉल किया जाता है, तो यह कैश किए गए मान का उपयोग करता है। आइए मान लें कि यह प्रारंभिक कॉल है और आगे बढ़ें।
इसके बाद यह जाँचता है कि क्या $app
सेट है, और $app
एप्लीकेशन कंटेनर का एक उदाहरण है
// Facade.php protected static \Illuminate\Contracts\Foundation\Application $app;
अगर आप इस बारे में उत्सुक हैं कि एप्लीकेशन कंटेनर क्या है, तो इसे एक बॉक्स के रूप में सोचें जहाँ आपकी क्लासेस स्टोर की जाती हैं। जब आपको उन क्लासेस की ज़रूरत होती है, तो आप बस उस बॉक्स में पहुँच जाते हैं। कभी-कभी, यह कंटेनर थोड़ा जादू करता है। भले ही बॉक्स खाली हो, और आप क्लास लेने के लिए पहुँचते हैं, यह आपके लिए उसे ले आएगा। यह किसी दूसरे लेख का विषय है।
अब, आप सोच रहे होंगे, " $app
कब सेट किया जाता है?", क्योंकि इसे सेट किया जाना चाहिए, अन्यथा, हमारे पास $instance
नहीं होगा। यह एप्लिकेशन कंटेनर हमारे एप्लिकेशन की बूटस्ट्रैपिंग प्रक्रिया के दौरान सेट हो जाता है। आइए \Illuminate\Foundation\Http\Kernel
क्लास पर एक त्वरित नज़र डालें:
<?php namespace Illuminate\Foundation\Http; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Facade; use Illuminate\Contracts\Http\Kernel as KernelContract; // ... class Kernel implements KernelContract { // ... protected $app; protected $bootstrappers = [ \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, // <- this guy \Illuminate\Foundation\Bootstrap\RegisterProviders::class, \Illuminate\Foundation\Bootstrap\BootProviders::class, ]; public function bootstrap(): void { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } } }
जब कोई अनुरोध आता है, तो उसे राउटर को भेजा जाता है। उसके ठीक पहले, bootstrap()
विधि को लागू किया जाता है, जो एप्लिकेशन तैयार करने के लिए bootstrappers
सरणी का उपयोग करता है। यदि आप \Illuminate\Foundation\Application
क्लास में bootstrapWith()
विधि का पता लगाते हैं, तो यह इन बूटस्ट्रैपर्स के माध्यम से पुनरावृति करता है, उनकी bootstrap()
विधि को कॉल करता है।
सरलता के लिए, आइए हम केवल \Illuminate\Foundation\Bootstrap\RegisterFacades
पर ध्यान केंद्रित करें, जिसके बारे में हम जानते हैं कि इसमें bootstrap()
विधि शामिल है जिसे bootstrapWith()
में लागू किया जाएगा
<?php namespace Illuminate\Foundation\Bootstrap; use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\AliasLoader; use Illuminate\Foundation\PackageManifest; use Illuminate\Support\Facades\Facade; class RegisterFacades { // ... public function bootstrap(Application $app): void { Facade::clearResolvedInstances(); Facade::setFacadeApplication($app); // Interested here AliasLoader::getInstance(array_merge( $app->make('config')->get('app.aliases', []), $app->make(PackageManifest::class)->aliases() ))->register(); } }
और बस, हम स्टैटिक विधि setFacadeApplication().
का उपयोग करके Facade
क्लास पर एप्लिकेशन कंटेनर सेट कर रहे हैं।
// RegisterFacades.php public static function setFacadeApplication($app) { static::$app = $app; }
देखिए, हम $app
प्रॉपर्टी असाइन करते हैं जिसका परीक्षण हम resolveFacadeInstance()
के भीतर कर रहे हैं। इससे प्रश्न का उत्तर मिल जाता है; चलिए आगे बढ़ते हैं।
// Facade.php protected static function resolveFacadeInstance($name) { if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } if (static::$app) { if (static::$cached) { return static::$resolvedInstance[$name] = static::$app[$name]; } return static::$app[$name]; } }
हमने पुष्टि की है कि एप्लिकेशन बूटस्ट्रैपिंग के दौरान $app
सेट किया गया है। अगला चरण यह जांचना है कि हल किए गए इंस्टेंस को $cached
सत्यापित करके कैश किया जाना चाहिए या नहीं, जो डिफ़ॉल्ट रूप से true होता है। अंत में, हम एप्लिकेशन कंटेनर से इंस्टेंस को पुनः प्राप्त करते हैं, हमारे मामले में, यह static::$app['router']
स्ट्रिंग router
से बंधी कोई भी क्लास प्रदान करने के लिए कहने जैसा है।
अब, आप सोच रहे होंगे कि हम $app
एक ऐरे की तरह क्यों एक्सेस करते हैं, जबकि यह एप्लीकेशन कंटेनर का एक इंस्टेंस है, यानी एक ऑब्जेक्ट । खैर, आप सही कह रहे हैं! हालाँकि, एप्लीकेशन कंटेनर ArrayAccess
नामक एक PHP इंटरफ़ेस को लागू करता है, जो ऐरे जैसी पहुँच की अनुमति देता है। हम इस तथ्य की पुष्टि करने के लिए इस पर एक नज़र डाल सकते हैं:
<?php namespace Illuminate\Container; use ArrayAccess; // <- this guy use Illuminate\Contracts\Container\Container as ContainerContract; class Container implements ArrayAccess, ContainerContract { // ... }
तो, resolveFacadeInstance()
वास्तव में router
स्ट्रिंग से बंधा एक इंस्टेंस लौटाता है, विशेष रूप से, \Illuminate\Routing\Router
। मुझे कैसे पता चला? Route
फ़ेसेड पर एक नज़र डालें; अक्सर, आपको एक PHPDoc @see
मिलेगा जो इस फ़ेसेड के बारे में संकेत देता है कि यह फ़ेसेड क्या छुपाता है या, अधिक सटीक रूप से, हमारे विधि कॉल किस क्लास के लिए प्रॉक्सी होंगे।
अब, __callStatic
विधि पर वापस आते हैं।
<?php namespace Illuminate\Support\Facades; use RuntimeException; // ... abstract class Facade { // ... public static function __callStatic(string $method, array $args): mixed { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); } }
हमारे पास $instance
है, \Illuminate\Routing\Router
क्लास का एक ऑब्जेक्ट है। हम जाँचते हैं कि क्या यह सेट है (जो, हमारे मामले में, पुष्टि की गई है), और हम सीधे इस पर विधि को लागू करते हैं। तो, हम अंत में पाते हैं।
// Facade.php return $instance->get('/', Closure());
और अब, आप पुष्टि कर सकते हैं कि get()
\Illuminate\Routing\Router
वर्ग में मौजूद है।
<?php namespace Illuminate\Routing; use Illuminate\Routing\Route; use Illuminate\Contracts\Routing\BindingRegistrar; use Illuminate\Contracts\Routing\Registrar as RegistrarContract; // ... class Router implements BindingRegistrar, RegistrarContract { // ... public function get(string $uri, array|string|callable|null $action = null): Route { return $this->addRoute(['GET', 'HEAD'], $uri, $action); } }
बस इतना ही! क्या अंत में यह मुश्किल नहीं था? संक्षेप में, एक फ़ेसेड एक स्ट्रिंग लौटाता है जो कंटेनर से बंधा होता है। उदाहरण के लिए, hello-world
HelloWorld
क्लास से बंधा हो सकता है। जब हम फ़ेसेड पर एक अपरिभाषित विधि को स्थिर रूप से लागू करते हैं, उदाहरण के लिए HelloWorldFacade
, __callStatic()
कदम बढ़ाता है।
यह अपने getFacadeAccessor()
विधि में पंजीकृत स्ट्रिंग को कंटेनर के भीतर जो भी बाउंड है, उसे हल करता है और उस पुनर्प्राप्त इंस्टेंस के लिए हमारी कॉल को प्रॉक्सी करता है। इस प्रकार, हम (new HelloWorld())->method()
के साथ समाप्त होते हैं। यही इसका सार है! अभी भी आपके लिए क्लिक नहीं किया? तो चलिए अपना मुखौटा बनाते हैं!
मान लीजिए हमारे पास यह वर्ग है:
<?php namespace App\Http\Controllers; class HelloWorld { public function greet(): string { return "Hello, World!"; } }
लक्ष्य HelloWorld::greet()
लागू करना है। ऐसा करने के लिए, हम अपने क्लास को एप्लिकेशन कंटेनर से जोड़ेंगे। सबसे पहले, AppServiceProvider
पर जाएँ।
<?php namespace App\Providers; use App\Http\Controllers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind('hello-world', function ($app) { return new HelloWorld; }); } // ... }
अब, जब भी हम अपने एप्लिकेशन कंटेनर (या बॉक्स, जैसा कि मैंने पहले उल्लेख किया है) से hello-world
अनुरोध करते हैं, तो यह HelloWorld
का एक उदाहरण लौटाता है। क्या बचा है? बस एक मुखौटा बनाएँ जो स्ट्रिंग hello-world
लौटाता है।
<?php namespace App\Http\Facades; use Illuminate\Support\Facades\Facade; class HelloWorldFacade extends Facade { protected static function getFacadeAccessor() { return 'hello-world'; } }
यह सब होने के बाद, हम इसका उपयोग करने के लिए तैयार हैं। चलिए इसे अपने web.php.
<?php use App\Http\Facades; use Illuminate\Support\Facades\Route; Route::get('/', function () { return HelloWorldFacade::greet(); // Hello, World! });
हम जानते हैं कि HelloWorldFacade
मुखौटे पर greet()
मौजूद नहीं है, __callStatic()
ट्रिगर होता है। यह एप्लिकेशन कंटेनर से एक स्ट्रिंग (हमारे मामले में hello-world
) द्वारा दर्शाए गए क्लास को खींचता है। और हमने AppServiceProvider
में यह बाइंडिंग पहले ही बना ली है; हमने इसे HelloWorld
का एक इंस्टेंस प्रदान करने का निर्देश दिया है जब भी कोई hello-world
अनुरोध करता है। नतीजतन, कोई भी कॉल, जैसे कि greet()
, HelloWorld
के उस पुनर्प्राप्त इंस्टेंस पर काम करेगा। और बस।
बधाई हो! आपने अपना खुद का मुखौटा बना लिया है!
अब जब आपको फ़ेसेड की अच्छी समझ हो गई है, तो एक और जादुई ट्रिक का खुलासा करना बाकी है। कल्पना करें कि आप फ़ेसेड बनाए बिना, रियल-टाइम फ़ेसेड का उपयोग करके HelloWorld::greet()
को कॉल कर पाएँ।
चलो देखते हैं:
<?php use Facades\App\Http\Controllers; // Notice the prefix use Illuminate\Support\Facades\Route; Route::get('/', function () { return HelloWorld::greet(); // Hello, World! });
नियंत्रक के नामस्थान में Facades
उपसर्ग लगाकर, हम पहले जैसा ही परिणाम प्राप्त करते हैं। लेकिन, यह निश्चित है कि HelloWorld
नियंत्रक में greet()
नामक कोई स्थिर विधि नहीं है! और Facades\App\Http\Controllers\HelloWorld
आखिर कहाँ से आता है? मैं समझता हूँ कि यह कुछ जादू की तरह लग सकता है, लेकिन एक बार जब आप इसे समझ लेते हैं, तो यह काफी सरल है।
आइए हम \Illuminate\Foundation\Bootstrap\RegisterFacades
पर करीब से नज़र डालें, जिसकी हमने पहले जाँच की थी, यह $app:
<?php namespace Illuminate\Foundation\Bootstrap; use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\AliasLoader; use Illuminate\Foundation\PackageManifest; use Illuminate\Support\Facades\Facade; class RegisterFacades { public function bootstrap(Application $app): void { Facade::clearResolvedInstances(); Facade::setFacadeApplication($app); AliasLoader::getInstance(array_merge( $app->make('config')->get('app.aliases', []), $app->make(PackageManifest::class)->aliases() ))->register(); // Interested here } }
आप अंत में देख सकते हैं कि register()
विधि लागू की गई है। आइए अंदर झांकें:
<?php namespace Illuminate\Foundation; class AliasLoader { // ... protected $registered = false; public function register(): void { if (! $this->registered) { $this->prependToLoaderStack(); $this->registered = true; } } }
$registered
वैरिएबल को शुरू में false
पर सेट किया जाता है। इसलिए, हम if
स्टेटमेंट दर्ज करते हैं और prependToLoaderStack()
विधि को कॉल करते हैं। अब, आइए इसके कार्यान्वयन का पता लगाते हैं।
// AliasLoader.php protected function prependToLoaderStack(): void { spl_autoload_register([$this, 'load'], true, true); }
यहीं पर जादू होता है! Laravel spl_autoload_register()
फ़ंक्शन को कॉल कर रहा है, जो एक अंतर्निहित PHP फ़ंक्शन है जो किसी अपरिभाषित क्लास तक पहुँचने का प्रयास करते समय ट्रिगर होता है। यह ऐसी स्थितियों में निष्पादित करने के लिए तर्क को परिभाषित करता है। इस मामले में, Laravel अपरिभाषित कॉल का सामना करने पर load()
विधि को लागू करना चुनता है।
इसके अतिरिक्त, spl_autoload_register()
स्वचालित रूप से अपरिभाषित वर्ग का नाम उस विधि या फ़ंक्शन को भेज देता है जिसे वह कॉल करता है।
आइए load()
विधि का अन्वेषण करें; यह कुंजी होनी चाहिए।
// AliasLoader.php public function load($alias) { if (static::$facadeNamespace && str_starts_with($alias, static::$facadeNamespace)) { $this->loadFacade($alias); return true; } if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } }
हम जाँचते हैं कि क्या $facadeNamespace
सेट है, और यदि जो भी क्लास पास हुई है, हमारे मामले में, Facades\App\Http\Controllers\HelloWorld
$facadeNamespace
में जो भी सेट है, उसके साथ शुरू होती है
तर्क यह जाँचता है कि क्या $facadeNamespace
सेट है और क्या पारित वर्ग, हमारे मामले में Facades\App\Http\Controllers\HelloWorld
(जो अपरिभाषित है), $facadeNamespace.
// AliasLoader.php protected static $facadeNamespace = 'Facades\\';
चूँकि हमने अपने कंट्रोलर के नामस्थान के आगे Facades
लगा दिया है, जो शर्त को पूरा करता है, इसलिए हम loadFacade()
पर आगे बढ़ते हैं
// AliasLoader.php protected function loadFacade($alias) { require $this->ensureFacadeExists($alias); }
यहाँ, विधि को ensureFacadeExists()
से लौटाए गए पथ की आवश्यकता होती है। इसलिए, अगला चरण इसके कार्यान्वयन में गहराई से जाना है।
// AliasLoader.php protected function ensureFacadeExists($alias) { if (is_file($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) { return $path; } file_put_contents($path, $this->formatFacadeStub( $alias, file_get_contents(__DIR__.'/stubs/facade.stub') )); return $path; }
सबसे पहले, यह पता लगाने के लिए जाँच की जाती है कि framework/cache/facade-'.sha1($alias).'.php'
नाम की कोई फ़ाइल मौजूद है या नहीं। हमारे मामले में, यह फ़ाइल मौजूद नहीं है, जो अगले चरण को ट्रिगर करती है: file_put_contents()
। यह फ़ंक्शन एक फ़ाइल बनाता है और उसे निर्दिष्ट $path
पर सहेजता है। फ़ाइल की सामग्री formatFacadeStub()
द्वारा उत्पन्न की जाती है, जो, इसके नाम से देखते हुए, एक स्टब से एक मुखौटा बनाता है। यदि आप facade.stub
निरीक्षण करते हैं, तो आपको निम्नलिखित मिलेगा:
<?php namespace DummyNamespace; use Illuminate\Support\Facades\Facade; /** * @see \DummyTarget */ class DummyClass extends Facade { /** * Get the registered name of the component. */ protected static function getFacadeAccessor(): string { return 'DummyTarget'; } }
परिचित लग रहा है? हमने मूल रूप से यही मैन्युअली किया था। अब, formatFacadeStub()
Facades\\
उपसर्ग को हटाने के बाद डमी सामग्री को हमारे अपरिभाषित वर्ग से बदल देता है। यह अपडेट किया गया मुखौटा तब संग्रहीत किया जाता है। परिणामस्वरूप, जब loadFacade()
फ़ाइल की आवश्यकता होती है, तो यह सही तरीके से करता है, और इसे निम्न फ़ाइल की आवश्यकता होती है:
<?php namespace Facades\App\Http\Controllers; use Illuminate\Support\Facades\Facade; /** * @see \App\Http\Controllers\HelloWorld */ class HelloWorld extends Facade { /** * Get the registered name of the component. */ protected static function getFacadeAccessor(): string { return 'App\Http\Controllers\HelloWorld'; } }
और अब, सामान्य प्रवाह में, हम एप्लिकेशन कंटेनर App\Http\Controllers\HelloWorld
स्ट्रिंग से बंधे किसी भी इंस्टेंस को वापस करने के लिए कहते हैं। आप सोच रहे होंगे कि हमने इस स्ट्रिंग को किसी भी चीज़ से नहीं बांधा, हमने अपने AppServiceProvider
भी नहीं छुआ। लेकिन याद रखें कि मैंने शुरुआत में एप्लिकेशन कंटेनर के बारे में क्या बताया था?
भले ही बॉक्स खाली हो, यह इंस्टेंस लौटाएगा , लेकिन एक शर्त के साथ, क्लास में कंस्ट्रक्टर नहीं होना चाहिए। अन्यथा, यह नहीं जानता कि इसे आपके लिए कैसे बनाया जाए। हमारे मामले में, हमारे HelloWorld
क्लास को निर्माण के लिए किसी तर्क की आवश्यकता नहीं है। इसलिए, कंटेनर इसे हल करता है, इसे लौटाता है और सभी कॉल इसके लिए प्रॉक्सी हो जाती हैं।
रीयल-टाइम फ़ेसेड को फिर से समझना: हमने अपने क्लास के आगे Facades
लगा दिया है। एप्लिकेशन बूटस्ट्रैपिंग के दौरान, Laravel spl_autoload_register()
रजिस्टर करता है, जो तब ट्रिगर होता है जब हम अपरिभाषित क्लास को कॉल करते हैं। यह अंततः load()
विधि की ओर ले जाता है। load()
अंदर, हम जाँचते हैं कि क्या मौजूदा अपरिभाषित क्लास के आगे Facades
लगा है। यह मेल खाता है, इसलिए Laravel इसे लोड करने की कोशिश करता है।
चूँकि मुखौटा मौजूद नहीं है, इसलिए यह इसे स्टब से बनाता है और फिर फ़ाइल की आवश्यकता होती है। और वाह! आपके पास एक नियमित मुखौटा है, लेकिन यह एक को तुरंत बनाया गया था। बहुत बढ़िया, है न?
यहाँ तक पहुँचने के लिए बधाई! मैं समझता हूँ कि यह थोड़ा भारी हो सकता है। बेझिझक वापस जाएँ और उन अनुभागों को फिर से पढ़ें जो आपके लिए ठीक से क्लिक नहीं हुए। अपने IDE के साथ अनुवर्ती कार्रवाई भी मदद कर सकती है। लेकिन अरे, अब कोई काला जादू नहीं, अच्छा लग रहा होगा, कम से कम मुझे पहली बार ऐसा ही लगा!
और याद रखें, अगली बार जब आप किसी विधि को स्टेटिकली कॉल करेंगे, तो हो सकता है कि ऐसा न हो 🪄