PHP जेनरिक आज (अच्छी तरह से, लगभग)

यदि आप PHP डेवलपर्स से पूछते हैं कि वे PHP में किस तरह का अवसर देखना चाहते हैं, तो अधिकांश जेनरिक को कॉल करेंगे।


भाषा के स्तर पर सामान्य समर्थन सबसे अच्छा समाधान होगा। लेकिन, उन्हें महसूस करना मुश्किल है । हम आशा करते हैं कि एक दिन देशी समर्थन भाषा का हिस्सा बन जाएगा, लेकिन इसे इंतजार करने में कई साल लगेंगे।


यह लेख दिखाएगा कि कैसे, मौजूदा साधनों का उपयोग करते हुए, कुछ मामलों में न्यूनतम संशोधनों के साथ, हम अभी PHP में जेनरिक की शक्ति प्राप्त कर सकते हैं।


अनुवादक से: मैं जानबूझकर अंग्रेजी के "जेनरिक" से ट्रेसिंग पेपर का उपयोग करता हूं, क्योंकि मैंने संचार में कभी नहीं सुना कि किसी ने इसे "सामान्यीकृत प्रोग्रामिंग" कहा।

सामग्री:



जेनरिक क्या हैं


इस खंड में जेनरिक का संक्षिप्त परिचय दिया गया है।


लिंक पढ़ना:


  • PHP जेनेरिक जोड़ने के लिए RFC
  • फान में सामान्य समर्थन
  • स्तोत्र जेनरिक और टेम्प्लेट

सरलतम उदाहरण


चूँकि वर्तमान में भाषा स्तर पर जेनरिक को परिभाषित करना संभव नहीं है, इसलिए हमें डॉक ब्लॉक में परिभाषित करने के लिए एक और महान अवसर का उपयोग करना होगा।


हम पहले से ही कई परियोजनाओं में इस विकल्प का उपयोग कर रहे हैं। इस उदाहरण पर एक नज़र डालें:


/** * @param string[] $names * @return User[] */ function createUsers(iterable $names): array { ... } 

उपरोक्त कोड में, हम भाषा स्तर पर जो संभव है, करते हैं। हमने $names पैरामीटर को कुछ ऐसी चीज़ों के रूप में परिभाषित किया है जिन्हें सूचीबद्ध किया जा सकता है। हमने यह भी संकेत दिया कि फ़ंक्शन एक सरणी लौटाएगा। यदि पैरामीटर टाइप और रिटर्न वैल्यू मेल नहीं खाते हैं तो PHP एक TypeError को फेंक देगा।


डॉकब्लॉक कोड की समझ में सुधार करता है। $names तार होने चाहिए, और फ़ंक्शन को User ऑब्जेक्ट की एक सरणी वापस करनी होगी। PHP खुद इस तरह की जाँच नहीं करता है। लेकिन PhpStorm जैसे IDE इस नोटेशन को समझते हैं और डेवलपर को चेतावनी देते हैं कि अतिरिक्त अनुबंध का सम्मान नहीं किया गया है। इसके अलावा, स्थैतिक विश्लेषण उपकरण जैसे कि Psalm, PHPStan और Phan फ़ंक्शन से और स्थानांतरित किए गए डेटा की शुद्धता को मान्य कर सकते हैं।


प्रगणित प्रकारों की कुंजियों और मूल्यों को निर्धारित करने के लिए सामान्य ज्ञान


ऊपर एक जेनेरिक का सबसे सरल उदाहरण है। अधिक जटिल तरीकों में मूल्यों के प्रकार के साथ, इसकी कुंजी के प्रकार को निर्दिष्ट करने की क्षमता शामिल है। इसका वर्णन करने का एक तरीका नीचे दिया गया है:


 /** * @return array<string, User> */ function getUsers(): array { ... } 

यह यहां कहता है कि getUsers द्वारा लौटाए गए सरणी में getUsers की स्ट्रिंग कुंजी और मान हैं।


Psalm, PHPStan और Phan जैसे स्टेटिक विश्लेषक इस एनोटेशन को समझते हैं और जाँच करते समय इसे ध्यान में रखते हैं।


निम्नलिखित कोड पर विचार करें:


 /** * @return array<string, User> */ function getUsers(): array { ... } function showAge(int $age): void { ... } foreach(getUsers() as $name => $user) { showAge($name); } 

स्टेटिक एनालाइजर showAge कॉल पर एक एरर को एरर के साथ चेतावनी देगा, जैसे: Argument 1 of showAge expects int, string provided


दुर्भाग्य से, लेखन के समय, PhpStorm पता नहीं कैसे।


अधिक परिष्कृत जेनरिक


हम जेनेरिक के विषय में तल्लीन करना जारी रखते हैं। एक वस्तु पर विचार करें जो एक स्टैक है :


 class Stack { public function push($item): void { ... } public function pop() { ... } } 

स्टैक किसी भी प्रकार की वस्तु को स्वीकार कर सकता है। लेकिन क्या होगा अगर हम स्टैक को केवल User ऑब्जेक्ट्स तक सीमित करना चाहते हैं?


भजन और फान निम्नलिखित टिप्पणियों का समर्थन करते हैं:


 /** * @template T */ class Stack { /** * @param T $item */ public function push($item): void; /** * @return T */ public function pop(); } 

डॉकब्लॉक का उपयोग अतिरिक्त प्रकार की जानकारी देने के लिए किया जाता है, उदाहरण के लिए:


 /** @var Stack<User> $userStack */ $stack = new Stack(); Means that $userStack must only contain Users. 

भजन, जब निम्नलिखित कोड का विश्लेषण:


 $userStack->push(new User()); $userStack->push("hello"); 

Argument 1 of Stack::push expects User, string(hello) provided. के त्रुटि Argument 1 of Stack::push expects User, string(hello) provided. साथ लाइन 2 के बारे में शिकायत करेंगे Argument 1 of Stack::push expects User, string(hello) provided.


PhpStorm वर्तमान में इस एनोटेशन का समर्थन नहीं करता है।


वास्तव में, हमने जेनेरिक के बारे में जानकारी का केवल एक हिस्सा कवर किया है, लेकिन फिलहाल यह पर्याप्त है।


भाषा समर्थन के बिना जेनरिक को कैसे लागू किया जाए


आपको निम्नलिखित चरण पूरे करने होंगे:


  • सामुदायिक स्तर पर, डॉक ब्लॉकों में सामान्य मानकों को परिभाषित करें (उदा। नए PSR, या PSR-5 पर वापस जाएं)
  • अपने कोड में डॉकब्लॉक एनोटेशन जोड़ें
  • विसंगतियों को खोजने के लिए वास्तविक समय स्थैतिक विश्लेषण करने के लिए इन सम्मेलनों को समझने वाले आईडीई का उपयोग करें।
  • त्रुटियों को पकड़ने के लिए CI चरणों में से एक के रूप में स्थैतिक विश्लेषण उपकरण (जैसे भजन) का उपयोग करें।
  • तीसरे पक्ष के पुस्तकालयों के लिए टाइप जानकारी पारित करने के लिए एक विधि को परिभाषित करें।

मानकीकरण


फिलहाल, PHP समुदाय ने अनौपचारिक रूप से इस जेनेरिक प्रारूप को अपनाया है (वे अधिकांश उपकरणों द्वारा समर्थित हैं और उनका अर्थ अधिकांश के लिए स्पष्ट है):


 /** * @return User[] */ function getUsers(): array { ... } 

हालाँकि, हमें इस तरह के सरल उदाहरणों से समस्या है:


 /** * @return array<string, User> */ function getUsers(): array { ... } 

भजन इसे समझता है, और जानता है कि कुंजी में किस प्रकार और लौटे हुए सरणी के मान हैं।


लिखने के समय, PhpStorm को यह समझ में नहीं आता है। इस प्रविष्टि का उपयोग करते हुए, मैं PhpStorm द्वारा की पेशकश की वास्तविक समय स्थैतिक विश्लेषण की शक्ति को याद करता हूं।


नीचे दिए गए कोड पर विचार करें। PhpStorm यह नहीं समझता है कि $user प्रकार का है और $name स्ट्रिंग प्रकार का है:


 foreach(getUsers() as $name => $user) { ... } 

अगर मैंने भजन को एक स्थिर विश्लेषण उपकरण के रूप में चुना, तो मैं निम्नलिखित लिख सकता हूं:


 /** * @return User[] * @psalm-return array<string, User> */ function getUsers(): array { ... } 

भजन यह सब समझता है।


PhpStorm को पता है कि $user वेरिएबल User । लेकिन, वह अभी भी यह नहीं समझता है कि सरणी कुंजी एक स्ट्रिंग को संदर्भित करता है। फान और PHPStan विशिष्ट भजन एनोटेशन को नहीं समझते हैं। इस कोड में वे जो अधिकतम समझते हैं वह PhpStorm में समान है: $user का प्रकार


आप तर्क दे सकते हैं कि PhpStorm को केवल एग्रीमेंट array<keyType, valueType> स्वीकार करना चाहिए। मैं आपसे सहमत नहीं हूं, क्योंकि मेरा मानना ​​है कि मानकों का यह ताना-बाना भाषा और समुदाय का काम है, और साधनों को केवल उनका पालन करना चाहिए।


मुझे लगता है कि ऊपर वर्णित समझौते को PHP समुदाय के अधिकांश लोगों द्वारा गर्मजोशी से प्राप्त किया जाएगा। जेनेरिक में रुचि रखने वाला। हालांकि, पैटर्न की बात आते ही चीजें बहुत अधिक जटिल हो जाती हैं। न तो PHPStan और न ही PhpStorm वर्तमान में टेम्पलेट्स का समर्थन करते हैं। भजन और फान के विपरीत। उनका उद्देश्य समान है, लेकिन यदि आप गहराई से खुदाई करते हैं, तो आप महसूस करेंगे कि कार्यान्वयन थोड़ा अलग हैं।


प्रस्तुत विकल्पों में से प्रत्येक एक प्रकार का समझौता है।


सीधे शब्दों में कहें, जेनेरिक रिकॉर्ड प्रारूप पर एक समझौते की आवश्यकता है:


  • वे डेवलपर्स के जीवन में सुधार करते हैं। डेवलपर्स अपने कोड में जेनरिक जोड़ सकते हैं और इससे लाभ उठा सकते हैं।
  • डेवलपर्स उन उपकरणों का उपयोग कर सकते हैं जिन्हें वे सबसे अच्छा पसंद करते हैं और आवश्यकतानुसार (उपकरण) उनके बीच स्विच करते हैं।
  • उपकरण निर्माता इन बहुत से उपकरण बना सकते हैं, समुदाय के लाभों को समझ सकते हैं और इस डर से नहीं कि कुछ बदल जाएगा, या उन पर "गलत दृष्टिकोण" का आरोप लगाया जाएगा।

उपकरण का समर्थन


जेनरिक की जाँच के लिए भजन की सभी आवश्यक कार्यक्षमता है। फन थोड़े जैसा, वैसा भी।


मुझे यकीन है कि जैसे ही समुदाय एक प्रारूप समझौते के साथ आएगा, PhpStorm जेनरिक का परिचय देगा।


3 पार्टी कोड समर्थन


जेनेरिक पहेली का अंतिम भाग तीसरे पक्ष के पुस्तकालयों के लिए समर्थन जोड़ रहा है।


उम्मीद है, जैसे ही जेनेरिक परिभाषा मानक दिखाई देगा, अधिकांश पुस्तकालय इसे लागू करेंगे। हालांकि, यह तुरंत नहीं होगा। कुछ पुस्तकालयों का उपयोग किया जाता है, लेकिन उनके पास सक्रिय समर्थन नहीं है। जेनेटिक्स में प्रकारों को मान्य करने के लिए स्थैतिक विश्लेषणकर्ताओं का उपयोग करते समय, यह महत्वपूर्ण है कि ये सभी कार्य जो इन जेनेरिक स्वीकार करते हैं या वापस परिभाषित किए जाते हैं।


यदि आपका प्रोजेक्ट तीसरे पक्ष के पुस्तकालयों पर निर्भर करता है, जिसमें सामान्य समर्थन नहीं है तो क्या होगा?


सौभाग्य से, यह समस्या पहले से ही हल हो गई है, और स्टब फ़ंक्शन समाधान हैं। भजन, फान और PhpStorm स्टब्स का समर्थन करते हैं।


स्टब्स सामान्य फाइलें हैं जिनमें फ़ंक्शंस और विधियों के हस्ताक्षर होते हैं, लेकिन उन्हें लागू नहीं करते हैं। स्टब्स में डॉक ब्लॉक जोड़कर, स्थैतिक विश्लेषण उपकरण को अतिरिक्त जानकारी मिलती है, जिसकी उन्हें आवश्यकता होती है। उदाहरण के लिए, यदि आपके पास इस तरह के संकेत और जेनरिक के बिना एक स्टैक वर्ग है।


 class Stack { public function push($item) { /* some implementation */ } public function pop() { /* some implementation */ } } 

आप एक स्टब फ़ाइल बना सकते हैं जिसमें समान विधियां हैं, लेकिन डॉक ब्लॉकों के अतिरिक्त और कार्यों के कार्यान्वयन के बिना।


 /** * @template T */ class Stack { /** * @param T $item * @return void */ public function push($item); /** * @return T */ public function pop(); } 

जब स्टैटिक विश्लेषक स्टैक क्लास को देखता है, तो यह स्टब से टाइप जानकारी लेता है, वास्तविक कोड से नहीं।


बस स्टब्स के कोड को साझा करने की क्षमता (उदाहरण के लिए, संगीतकार के माध्यम से) बेहद उपयोगी होगी, क्योंकि किए गए कार्य को साझा करने की अनुमति देगा।


आगे के कदम


समुदाय को समझौतों से दूर जाने और मानकों को निर्धारित करने की आवश्यकता है।


शायद सबसे अच्छा विकल्प एक सामान्य PSR होगा?


या हो सकता है कि मुख्य स्थैतिक विश्लेषक, PhpStorm, अन्य आईडीई और PHP (विकास के लिए) में शामिल लोगों में से कोई भी एक मानक को विकसित कर सकता है जो हर कोई उपयोग करेगा।


जैसे ही मानक प्रकट होता है, हर कोई मौजूदा पुस्तकालयों और परियोजनाओं के लिए जनक को जोड़ने में मदद करेगा, जिससे पुल अनुरोध बनेंगे। और जहां यह संभव नहीं है, डेवलपर्स स्टब्स लिख और साझा कर सकते हैं।


जब सब कुछ हो जाता है, तो हम कोड लिखते समय जेनरिक की जाँच करने के लिए PhpStorm जैसे उपकरणों का उपयोग कर सकते हैं। हम एक सुरक्षा गारंटी के रूप में हमारे CI के हिस्से के रूप में स्थैतिक विश्लेषण उपकरण का उपयोग कर सकते हैं।


जेनरिक को PHP (अच्छी तरह से, लगभग) में भी लागू किया जा सकता है।


प्रतिबंध


कई सीमाएँ हैं। PHP एक गतिशील भाषा है जो आपको कई "जादुई" चीजें करने की अनुमति देती है, जैसे कि ये । यदि आप बहुत अधिक PHP जादू का उपयोग करते हैं, तो ऐसा हो सकता है कि स्थिर विश्लेषक सिस्टम में सभी प्रकारों को सटीक रूप से नहीं निकाल सकता है। यदि कोई भी प्रकार अज्ञात है, तो उपकरण सभी मामलों में जेनेरिक का सही ढंग से उपयोग नहीं कर पाएंगे।


हालांकि, इस विश्लेषण का मुख्य अनुप्रयोग आपके व्यापारिक तर्क को मान्य करना है। यदि आप स्वच्छ कोड लिखते हैं, तो आपको बहुत अधिक जादू का उपयोग नहीं करना चाहिए।


आप सिर्फ जीभ में जेनेरिक क्यों नहीं जोड़ते?


यह सबसे अच्छा विकल्प होगा। PHP में ओपन सोर्स कोड होता है, और कोई भी आपको सोर्स को क्लोन करने और जेनरिक को लागू करने के लिए परेशान नहीं करता है!


अगर मुझे जेनरिक की आवश्यकता नहीं है तो क्या होगा?


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


इस जगह पर पढ़ने वाले सभी को धन्यवाद। मुझे पीएम की आपकी टिप्पणी पर खुशी होगी।

UPD : टिप्पणियों में ghost404 ने नोट किया कि संस्करण 0.12.x से PHPStan भजन व्याख्याओं को समझता है और विज्ञान का समर्थन करता है

Source: https://habr.com/ru/post/hi456466/


All Articles