PHP 中的嵌套类或内部类 [英] Nested or Inner Class in PHP

查看:34
本文介绍了PHP 中的嵌套类或内部类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的新网站构建一个用户类,但是这次我想以稍微不同的方式构建它...

I'm building a User Class for my new website, however this time I was thinking to build it little bit differently...

C++Java 甚至 Ruby(可能还有其他编程语言)都允许在主类中使用嵌套/内部类,这使我们能够使代码更加面向对象和组织.

C++, Java and even Ruby (and probably other programming languages) are allowing the use of nested/inner classes inside the main class, which allows us to make the code more object-oriented and organized.

在 PHP 中,我想做这样的事情:

In PHP, I would like to do something like so:

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

    public class UserProfile {
      // some code here
    }

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

这在 PHP 中可行吗?我怎样才能实现它?

Is that possible in PHP? How can I achieve it?

更新

如果不可能,未来的 PHP 版本会支持嵌套类吗?

If it's impossible, will future PHP versions might support nested classes?

推荐答案

Intro:

嵌套类与其他类的关系与外部类略有不同.以Java为例:

Intro:

Nested classes relate to other classes a little differently than outer classes. Taking Java as an example:

非静态嵌套类可以访问封闭类的其他成员,即使它们被声明为私有.此外,非静态嵌套类需要实例化父类的实例.

Non-static nested classes have access to other members of the enclosing class, even if they are declared private. Also, non-static nested classes require an instance of the parent class to be instantiated.

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

使用它们有几个令人信服的理由:

There are several compelling reasons for using them:

  • 这是一种对仅在一个地方使用的类进行逻辑分组的方法.

如果一个类只对另一个类有用,那么合乎逻辑将其关联并嵌入到该类中,并将两者保持在一起.

If a class is useful to only one other class, then it is logical to relate and embed it in that class and keep the two together.

  • 它增加了封装性.
  • 考虑两个顶级类 A 和 B,其中 B 需要访问否则将被声明为私有的 A 成员.通过隐藏类B 在类 A 中,A 的成员可以声明为私有,B 可以访问他们.此外,B 本身可以对外界隐藏.

    Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.

    • 嵌套类可以使代码更具可读性和可维护性.
    • 嵌套类通常与它的父类相关并一起形成一个包"

      A nested class usually relates to it's parent class and together form a "package"

      在 PHP 中

      您可以在没有嵌套类的情况下在 PHP 中拥有类似的行为.

      如果您只想实现结构/组织,如 Package.OuterClass.InnerClass,PHP 命名空间可能就足够了.您甚至可以在同一个文件中声明多个命名空间(尽管由于标准的自动加载功能,这可能是不可取的).

      If all you want to achieve is structure/organization, as Package.OuterClass.InnerClass, PHP namespaces might sufice. You can even declare more than one namespace in the same file (although, due to standard autoloading features, that might not be advisable).

      namespace;
      class OuterClass {}
      
      namespace OuterClass;
      class InnerClass {}
      

      如果您希望模拟其他特征,例如成员可见性,则需要更多的努力.

      If you desire to emulate other characteristics, such as member visibility, it takes a little more effort.

      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 PackageMyParentPublicChild();
                  //instantiate protected child inside parent
                  $this->protectedChild = new PackageMyParentProtectedChild();
              }
      
              public function test() {
                  echo "Call from parent -> ";
                  $this->publicChild->protectedMethod();
                  $this->protectedChild->protectedMethod();
      
                  echo "<br>Siblings<br>";
                  $this->publicChild->callSibling($this->protectedChild);
              }
          }
      }
      
      namespace PackageMyParent
      {
          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 PackageMyParent();
      $parent->test();
      $pubChild = new PackageMyParentPublicChild();//create new public child (possible)
      $protChild = new PackageMyParentProtectedChild(); //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 中模拟 innerClasses 是一个好主意.我认为代码不太干净和可读.此外,可能还有其他方法可以使用完善的模式(例如观察者、装饰者或组合模式)来实现类似的结果.有时,即使是简单的继承也足够了.


      NOTE:

      I really don't think trying to emulate innerClasses in PHP is such a good idea. I think the code is less clean and readable. Also, there are probably other ways to achieve similar results using a well established pattern such as the Observer, Decorator ou COmposition Pattern. Sometimes, even simple inheritance is sufficient.

      这篇关于PHP 中的嵌套类或内部类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆