PHP中的嵌套或内部类

我正在为我的新网站构build一个用户类 ,但是这次我正在考虑将它构build得有点不同。

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

在PHP中,我想要这样做:

<?php public class User { public $userid; public $username; private $password; public class UserProfile { // Some code here } private class UserHistory { // Some code here } } ?> 

在PHP中可能吗? 我怎样才能做到这一点?


UPDATE

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

介绍:

嵌套类与其他类相关,与外部类稍有不同。 以Java为例:

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

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

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

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

如果一个阶级只对另外一个阶级有用,那么把这个阶级联系起来并将其embedded到这个阶级中是合乎逻辑的,而把这两者联系起来。

  • 它增加了封装。

考虑两个顶级类A和B,其中B需要访问A的成员,否则这些成员将被声明为私有。 通过在类A中隐藏类B,可以将A的成员声明为私有,并且B可以访问它们。 另外,B本身可以被外界隐藏起来。

  • 嵌套类可以导致更易读和可维护的代码。

嵌套类通常涉及它的父类,一起形成一个“包”

在PHP中

您可以在没有嵌套类的PHP中具有类似的行为。

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

 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(); } } } 

testing

 $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中的innerClasses是一个好主意。 我认为代码不太干净可读。 此外,可能有其他方法可以使用一个已经很好build立的模式,例如Observer,Decorator或Composition Pattern来获得类似的结果。 有时候,即使简单的inheritance也是足够的。

真实的嵌套类与public / protected / private可访问性在2013年提出的PHP 5.6作为一个RFC,但没有做到(没有投票,自2013年以来没有更新 – 截至2016/12/29 ):

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

 class foo { public class bar { } } 

至less,匿名类进入PHP 7

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

从这个RFC页面:

未来的范围

这个补丁所做的改变意味着命名的嵌套类更容易实现(稍微)。

所以,我们可能会在未来版本中获得嵌套类,但还没有确定。

不能在PHP中做到这一点。 但是,有function的方法来完成这一点。

有关更多详细信息,请查看这篇文章: 如何做一个PHP嵌套类或嵌套方法?

这种实现方式称为stream畅接口: http : //en.wikipedia.org/wiki/Fluent_interface

你不能用PHP做。 PHP支持“include”,但是你甚至不能在类定义里面做。 这里没有很多很棒的select。

这并不直接回答你的问题,但是你可能会对“命名空间”感兴趣,这是PHP OOP的\ top \中非常丑陋的\ syntax \ hacked \: http : //www.php.net/manual/en/language .namespaces.rationale.php

自PHP 5.4版开始,您可以通过reflection强制创build具有私有构造函数的对象。 它可以用来模拟Java嵌套类。 示例代码:

 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"; 

我想我写了一个优雅的解决scheme,通过使用命名空间来解决这个问题。 在我的情况下,内部类不需要知道他的父类(如Java中的静态内部类)。 作为一个例子,我做了一个名为“User”的类和一个名为“Type”的子类,在我的例子中用作用户types(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; ?> 

根据Xenon对AnılÖzselgin的回答的评论,匿名类已经在PHP 7.0中实现了,它和嵌套类很接近。 以下是相关的RFC:

嵌套类(状态:撤销)

匿名类(状态:在PHP 7.0中实现)

原始post的例子,这是你的代码看起来像:

 <?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->... } 

再见,自动完成。 即使您使用如下模式编码接口(SOLID中的I),情况也是如此:

 <?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定义的任何方法),否则你将不会得到任何types的提示。 你的财产本质上是隐藏在你的IDE中的,如果你依靠你的IDE来完成自动完成,代码嗅探和重构,那么你的生活将变得非常艰难。

它正在等待投票作为RFC https://wiki.php.net/rfc/anonymous_classes