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組織模式實現類似的結果。有時,即使簡單的繼承也是足夠的。