我知道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吗?我该如何实现呢?如果这是不可能的,未来的PHP版本可能支持嵌套类?
嵌套类与其他类相关,有别于外部类。以Java为例:
非静态嵌套类可以访问包含类的其他成员,即使它们被声明为私有。另外,非静态嵌套类需要实例化父类的实例。
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
使用它们有几个令人信服的理由:
>这是一种逻辑分组只在一个地方使用的类的方法。
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.
>它增加封装。
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命名空间可能sufice。你甚至可以在同一个文件中声明多个命名空间(虽然,由于标准的自动加载功能,这可能不可取)。
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中模仿innerClasses是一个好主意。我认为代码不那么干净,可读。此外,可能有其他方法使用一个良好建立的模式,如观察者,装饰者ou组织模式实现类似的结果。有时,即使简单的继承也是足够的。