PHP में नेस्टेड या इनर क्लास


111

मैं अपनी नई वेबसाइट के लिए एक उपयोगकर्ता वर्ग का निर्माण कर रहा हूं , हालांकि इस बार मैं इसे थोड़ा अलग तरीके से बनाने के लिए सोच रहा था ...

सी ++ , जावा और यहां तक ​​कि रूबी (और शायद अन्य प्रोग्रामिंग भाषाएं) मुख्य वर्ग के अंदर नेस्टेड / इनर वर्ग के उपयोग की अनुमति दे रही हैं, जो हमें कोड को अधिक ऑब्जेक्ट-उन्मुख और संगठित करने की अनुमति देता है।

PHP में, मैं कुछ ऐसा करना चाहूंगा:

<?php
  public class User {
    public $userid;
    public $username;
    private $password;

    public class UserProfile {
      // some code here
    }

    private class UserHistory {
      // some code here
    }
  }
?>

क्या यह PHP में संभव है? मैं इसे कैसे प्राप्त कर सकता हूं?


अपडेट करें

यदि यह असंभव है, तो क्या भविष्य के PHP संस्करण नेस्टेड वर्गों का समर्थन कर सकते हैं?


4
PHP में यह असंभव
यूजीन

आप इसका विस्तार कर सकते हैं User, उदाहरण: public class UserProfile extends Userऔर public class UserHestory extends User
डेव चेन

आप एक अमूर्त उपयोगकर्ता वर्ग के साथ भी शुरू कर सकते हैं, फिर इसे विस्तारित कर सकते हैं। php.net/manual/en/language.oop5.abstract.php
मैथ्यू ब्लैंकेर्ट

@DaveChen मैं फैली हुई कक्षाओं से परिचित हूँ, लेकिन मैं एक बेहतर OOP समाधान की तलाश में हूँ :( Thx।
Lior Elrom

4
विस्तार करना समसामयिकी के समान नहीं है ... जब आप विस्तार करते हैं तो आप उपयोगकर्ता वर्ग का दोहराव 3 बार प्राप्त करते हैं (उपयोगकर्ता के रूप में, UserProfile के रूप में, और UserHistory के रूप में)
Tomer W

जवाबों:


136

परिचय:

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

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

OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);

उनका उपयोग करने के लिए कई सम्मोहक कारण हैं:

  • यह तार्किक रूप से समूहन वर्गों का एक तरीका है जो केवल एक ही स्थान पर उपयोग किया जाता है।

यदि कोई वर्ग केवल एक अन्य वर्ग के लिए उपयोगी है, तो उस कक्षा में इसे संबंधित और एम्बेड करना तर्कसंगत है और दोनों को एक साथ रखना है।

  • यह एन्कैप्सुलेशन को बढ़ाता है।

दो शीर्ष स्तर की कक्षाओं पर विचार करें, ए और बी, जहां बी को ए के सदस्यों तक पहुंच की आवश्यकता है जो अन्यथा निजी घोषित किया जाएगा। कक्षा बी को कक्षा ए के भीतर छिपाकर, ए के सदस्यों को निजी घोषित किया जा सकता है और बी उन्हें एक्सेस कर सकते हैं। इसके अलावा, बी खुद को बाहरी दुनिया से छिपाया जा सकता है।

  • नेस्टेड कक्षाएं अधिक पठनीय और बनाए रखने योग्य कोड को जन्म दे सकती हैं।

एक नेस्टेड क्लास आमतौर पर माता-पिता वर्ग से संबंधित होती है और एक साथ एक "पैकेज" बनाती है

PHP में

आप नेस्टेड वर्गों के बिना PHP में समान व्यवहार कर सकते हैं ।

यदि आप सभी को प्राप्त करना चाहते हैं, तो संरचना / संगठन है, जैसा कि Package.OuterClass.InnerClass, PHP के नामस्थान पर मुकदमा हो सकता है। आप एक ही फ़ाइल में एक से अधिक नामस्थान भी घोषित कर सकते हैं (हालाँकि, मानक ऑटोलडिंग विशेषताओं के कारण, यह उचित नहीं हो सकता है)।

namespace;
class OuterClass {}

namespace OuterClass;
class InnerClass {}

यदि आप अन्य विशेषताओं का अनुकरण करना चाहते हैं, जैसे कि सदस्य दृश्यता, तो यह थोड़ा अधिक प्रयास करता है।

"पैकेज" वर्ग को परिभाषित करना

namespace {

    class Package {

        /* protect constructor so that objects can't be instantiated from outside
         * Since all classes inherit from Package class, they can instantiate eachother
         * simulating protected InnerClasses
         */
        protected function __construct() {}

        /* This magic method is called everytime an inaccessible method is called 
         * (either by visibility contrains or it doesn't exist)
         * Here we are simulating shared protected methods across "package" classes
         * This method is inherited by all child classes of Package 
         */
        public function __call($method, $args) {

            //class name
            $class = get_class($this);

            /* we check if a method exists, if not we throw an exception 
             * similar to the default error
             */
            if (method_exists($this, $method)) {

                /* The method exists so now we want to know if the 
                 * caller is a child of our Package class. If not we throw an exception
                 * Note: This is a kind of a dirty way of finding out who's
                 * calling the method by using debug_backtrace and reflection 
                 */
                $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
                if (isset($trace[2])) {
                    $ref = new ReflectionClass($trace[2]['class']);
                    if ($ref->isSubclassOf(__CLASS__)) {
                        return $this->$method($args);
                    }
                }
                throw new \Exception("Call to private method $class::$method()");
            } else {
                throw new \Exception("Call to undefined method $class::$method()");
            }
        }
    }
}

उदाहरण

namespace Package {
    class MyParent extends \Package {
        public $publicChild;
        protected $protectedChild;

        public function __construct() {
            //instantiate public child inside parent
            $this->publicChild = new \Package\MyParent\PublicChild();
            //instantiate protected child inside parent
            $this->protectedChild = new \Package\MyParent\ProtectedChild();
        }

        public function test() {
            echo "Call from parent -> ";
            $this->publicChild->protectedMethod();
            $this->protectedChild->protectedMethod();

            echo "<br>Siblings<br>";
            $this->publicChild->callSibling($this->protectedChild);
        }
    }
}

namespace Package\MyParent
{
    class PublicChild extends \Package {
        //Makes the constructor public, hence callable from outside 
        public function __construct() {}
        protected function protectedMethod() {
            echo "I'm ".get_class($this)." protected method<br>";
        }

        protected function callSibling($sibling) {
            echo "Call from " . get_class($this) . " -> ";
            $sibling->protectedMethod();
        }
    }
    class ProtectedChild extends \Package { 
        protected function protectedMethod() {
            echo "I'm ".get_class($this)." protected method<br>";
        }

        protected function callSibling($sibling) {
            echo "Call from " . get_class($this) . " -> ";
            $sibling->protectedMethod();
        }
    }
}

परिक्षण

$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)

आउटपुट:

Call from parent -> I'm Package protected method
I'm Package protected method

Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context

ध्यान दें:

मुझे नहीं लगता कि PHP में इनरक्लासेस का अनुकरण करने की कोशिश करना इतना अच्छा विचार है। मुझे लगता है कि कोड कम साफ और पठनीय है। इसके अलावा, ऑब्जर्वर, डेकोरेटर कहां कॉंपोज़ पैटर्न जैसे एक अच्छी तरह से स्थापित पैटर्न का उपयोग करके समान परिणाम प्राप्त करने के लिए संभवतः अन्य तरीके हैं। कभी-कभी, साधारण विरासत भी पर्याप्त होती है।


2
यह भयानक है @Tivie! मैं अपने OOP विस्तार ढांचे में उस समाधान को लागू करने वाला हूँ! (मेरे
गितुब को

21

साथ रियल नेस्टेड वर्गों public/ protected/ privateपहुँच (2013 के बाद से अभी तक कोई मतदान, कोई अद्यतन - पीएचपी 5.6 एक RFC के रूप में के लिए 2013 में प्रस्तावित किया गया था, लेकिन यह नहीं था 2016/12/29 के रूप में ):

https://wiki.php.net/rfc/nested_classes

class foo {
    public class bar {
 
    }
}

कम से कम, अनाम वर्गों ने इसे PHP 7 में बनाया

https://wiki.php.net/rfc/anonymous_classes

इस RFC पेज से:

भविष्य का दायरा

नेस्टेड क्लासेस नाम के इस पैच द्वारा किए गए बदलावों को लागू करना आसान है (एक छोटे से)।

इसलिए, हमें भविष्य के कुछ संस्करणों में नेस्टेड कक्षाएं मिल सकती हैं, लेकिन यह अभी तक तय नहीं है।


12

आप PHP में ऐसा नहीं कर सकते । हालांकि, इसे पूरा करने के लिए कार्यात्मक तरीके हैं ।

अधिक जानकारी के लिए कृपया इस पोस्ट को देखें: PHP नेस्टेड क्लास या नेस्टेड विधियों को कैसे करें?

कार्यान्वयन के इस तरीके को धाराप्रवाह इंटरफ़ेस कहा जाता है: http://en.wikipedia.org/wiki/Fluent_interface


हां, दुर्भाग्य से यह पारंपरिक तरीका है
लायर एलरोम

5

PHP संस्करण 5.4 के बाद से आप प्रतिबिंब के माध्यम से निजी कंस्ट्रक्टर के साथ ऑब्जेक्ट बना सकते हैं। इसका उपयोग जावा नेस्टेड कक्षाओं को अनुकरण करने के लिए किया जा सकता है। उदाहरण कोड:

class OuterClass {
  private $name;

  public function __construct($name) {
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

  public function forkInnerObject($name) {
    $class = new ReflectionClass('InnerClass');
    $constructor = $class->getConstructor();
    $constructor->setAccessible(true);
    $innerObject = $class->newInstanceWithoutConstructor(); // This method appeared in PHP 5.4
    $constructor->invoke($innerObject, $this, $name);
    return $innerObject;
  }
}

class InnerClass {
  private $parentObject;
  private $name;

  private function __construct(OuterClass $parentObject, $name) {
    $this->parentObject = $parentObject;
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

  public function getParent() {
    return $this->parentObject;
  }
}

$outerObject = new OuterClass('This is an outer object');
//$innerObject = new InnerClass($outerObject, 'You cannot do it');
$innerObject = $outerObject->forkInnerObject('This is an inner object');
echo $innerObject->getName() . "\n";
echo $innerObject->getParent()->getName() . "\n";

4

एनल Özselgin के जवाब में क्सीनन की टिप्पणी के अनुसार, PHP 7.0 में अनाम कक्षाएं लागू की गई हैं, जो नेस्टेड कक्षाओं के करीब है जैसा कि आप अभी प्राप्त करेंगे। यहाँ प्रासंगिक RFC हैं:

नेस्टेड क्लासेस (स्थिति: वापस ले ली गई)

बेनामी कक्षाएं (स्थिति: PHP 7.0 में लागू)

मूल पोस्ट के लिए एक उदाहरण, यह वही है जो आपका कोड दिखाई देगा:

<?php
    public class User {
        public $userid;
        public $username;
        private $password;

        public $profile;
        public $history;

        public function __construct() {
            $this->profile = new class {
                // Some code here for user profile
            }

            $this->history = new class {
                // Some code here for user history
            }
        }
    }
?>

यह, हालांकि, बहुत बुरा चेतावनी के साथ आता है। यदि आप किसी IDE जैसे PHPStorm या NetBeans का उपयोग करते हैं, और फिर इस तरह एक विधि को Userकक्षा में जोड़ते हैं :

public function foo() {
  $this->profile->...
}

... अलविदा ऑटो-बाय। यदि आप इस तरह के पैटर्न का उपयोग करते हुए इंटरफेस (I को SOLID) में कोड करते हैं, तो भी यह मामला है:

<?php
    public class User {
        public $profile;

        public function __construct() {
            $this->profile = new class implements UserProfileInterface {
                // Some code here for user profile
            }
        }
    }
?>

जब तक आपकी केवल कॉल करने के लिए $this->profileसे हैं __construct()विधि (या जो भी विधि $this->profileमें परिभाषित किया गया है) तो आप प्रकार हिंट किसी भी प्रकार का नहीं मिलेगा। आपकी संपत्ति आपके आईडीई के लिए अनिवार्य रूप से "छिपी" है, यदि आप ऑटो-पूर्णता, कोड गंध सूँघने, और रिफैक्टरिंग के लिए अपनी आईडीई पर भरोसा करते हैं, तो जीवन को बहुत कठिन बना देता है।


3

आप इसे PHP में नहीं कर सकते। PHP "शामिल" का समर्थन करता है, लेकिन आप एक वर्ग परिभाषा के अंदर भी ऐसा नहीं कर सकते। यहां बहुत सारे शानदार विकल्प नहीं हैं।

यह सीधे आपके प्रश्न का उत्तर नहीं देता है, लेकिन आप "नाम" में दिलचस्पी ले सकते हैं, एक बहुत ही बदसूरत \ _ \ _ \ _ \ _ \ _ \ _ \ _ \ _ \ _ \ _ \ _ \ _ के लिए PHP OOP: http://www.php.net/manual/en/language .namespaces.rationale.php


Namespaces निश्चित रूप से कोड को बेहतर तरीके से व्यवस्थित कर सकता है लेकिन यह नेस्टेड वर्गों जितना शक्तिशाली नहीं है। जवाब के लिए धन्यवाद!
लायर एलरोम

आप इसे "भयानक" क्यों कहते हैं? मुझे लगता है कि यह ठीक है और अच्छी तरह से अन्य वाक्यविन्यास संदर्भों से अलग है।
एमफी

2

यह RFC https://wiki.php.net/rfc/anonymous_classes के रूप में मतदान की प्रतीक्षा कर रहा है


1
मुझे विश्वास नहीं है और अनाम वर्ग नेस्टेड क्लास की कार्यक्षमता प्रदान करेगा।
एरिक जी

1
RFC पेज में यदि आप "नेस्टेड" खोजते हैं तो आप देख सकते हैं कि इसमें सपोर्ट है। जावा तरीके से एक जैसा नहीं है, लेकिन यह समर्थन करता है।
15zselgin

3
PHP 7 में लागू
tralektra

2

मुझे लगता है कि मैंने नामस्थान का उपयोग करके इस समस्या का एक सुरुचिपूर्ण समाधान लिखा है। मेरे मामले में, आंतरिक वर्ग को अपने मूल वर्ग (जैसे जावा में स्थिर आंतरिक वर्ग) को जानने की आवश्यकता नहीं है। एक उदाहरण के रूप में मैंने 'उपयोगकर्ता' नामक एक वर्ग बनाया और 'टाइप' नामक एक उपवर्ग, मेरे उदाहरण में उपयोगकर्ता प्रकार (ADMIN, OTHERS) के लिए एक संदर्भ के रूप में इस्तेमाल किया। सादर।

User.php (उपयोगकर्ता वर्ग फ़ाइल)

<?php
namespace
{   
    class User
    {
        private $type;

        public function getType(){ return $this->type;}
        public function setType($type){ $this->type = $type;}
    }
}

namespace User
{
    class Type
    {
        const ADMIN = 0;
        const OTHERS = 1;
    }
}
?>

Using.php ('उपवर्ग' को कॉल करने का एक उदाहरण)

<?php
    require_once("User.php");

    //calling a subclass reference:
    echo "Value of user type Admin: ".User\Type::ADMIN;
?>

2

आप इस तरह, PHP 7 में कर सकते हैं:

class User{
  public $id;
  public $name;
  public $password;
  public $Profile;
  public $History;  /*  (optional declaration, if it isn't public)  */
  public function __construct($id,$name,$password){
    $this->id=$id;
    $this->name=$name;
    $this->name=$name;
    $this->Profile=(object)[
        'get'=>function(){
          return 'Name: '.$this->name.''.(($this->History->get)());
        }
      ];
    $this->History=(object)[
        'get'=>function(){
          return ' History: '.(($this->History->track)());
        }
        ,'track'=>function(){
          return (lcg_value()>0.5?'good':'bad');
        }
      ];
  }
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();

-6

प्रत्येक कक्षा को अलग-अलग फ़ाइलों में रखें और उन्हें "आवश्यक" करें।

User.php

<?php

    class User {

        public $userid;
        public $username;
        private $password;
        public $profile;
        public $history;            

        public function __construct() {

            require_once('UserProfile.php');
            require_once('UserHistory.php');

            $this->profile = new UserProfile();
            $this->history = new UserHistory();

        }            

    }

?>

UserProfile.php

<?php

    class UserProfile 
    {
        // Some code here
    }

?>

UserHistory.php

<?php

    class UserHistory 
    {
        // Some code here
    }

?>
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.