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