我正在为我的新网站建立一个用户类,不过这次我想要以不同的方式构建它......
我知道 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版本是否可能支持嵌套类?
答案 0 :(得分:122)
嵌套类与其他类的关系与外部类略有不同。以Java为例:
非静态嵌套类可以访问封闭类的其他成员,即使它们被声明为私有。此外,非静态嵌套类需要实例化父类的实例。
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
使用它们有几个令人信服的理由:
如果一个类只对另一个类有用,那么它是合乎逻辑的 将它们联系起来并将其嵌入到那个班级中并将两者结合在一起。
考虑B需要访问的两个顶级类A和B. 否则将被宣布为私人的A成员。通过隐藏类 A类中的B,A的成员可以被声明为私有,B可以访问 他们。此外,B本身可以隐藏在外面。
嵌套类通常与它的父类相关,并且一起形成“包”
您可以在PHP中使用类似的行为而不使用嵌套类。
如果要实现的只是结构/组织,就像Package.OuterClass.InnerClass一样,PHP命名空间可能会受到影响。您甚至可以在同一个文件中声明多个名称空间(尽管由于标准的自动加载功能,这可能是不可取的)。
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是个好主意。我认为代码不那么干净和可读。此外,可能还有其他方法可以使用完善的模式来实现类似的结果,例如Observer,Decorator ou COmposition Pattern。有时,即使是简单的继承就足够了。
答案 1 :(得分:19)
2013年,PHP 5.6作为RFC提出了具有public
/ protected
/ private
可访问性的真正嵌套类,但没有成功(没有投票,自2013年以来没有更新 - 截至2016/12/29 ):
https://wiki.php.net/rfc/nested_classes
class foo {
public class bar {
}
}
至少,匿名类使其成为PHP 7
https://wiki.php.net/rfc/anonymous_classes
从此RFC页面:
未来范围
此修补程序所做的更改意味着命名嵌套类更容易实现(稍微一点)。
因此,我们可能会在未来的某个版本中获得嵌套类,但尚未确定。
答案 2 :(得分:12)
你不能在PHP中执行此操作。但是,有功能性方法可以实现此目的。
有关详细信息,请查看此帖子: How to do a PHP nested class or nested methods?
这种实现方式称为流畅界面:http://en.wikipedia.org/wiki/Fluent_interface
答案 3 :(得分:4)
从PHP 5.4版开始,您可以通过反射强制创建具有私有构造函数的对象。它可以用于模拟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";
答案 4 :(得分:3)
你不能用PHP做到这一点。 PHP支持“include”,但你甚至不能在类定义中做到这一点。这里没有很多很棒的选择。
这不能直接回答你的问题,但你可能会对“命名空间”感兴趣,这是一个非常丑陋的\ syntax \ hacked \ on \ top \ of PHP OOP: http://www.php.net/manual/en/language.namespaces.rationale.php
答案 5 :(得分:2)
根据Xenon对AnılÖzselgin的回答的评论,匿名类已在PHP 7.0中实现,它与您现在可以获得的嵌套类非常接近。以下是相关的RFC:
Nested Classes (status: withdrawn)
Anonymous Classes (status: implemented in PHP 7.0)
原始帖子的一个示例,这就是您的代码的样子:
<?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
}
}
}
?>
但是,这有一个非常令人讨厌的警告。如果使用PHPStorm或NetBeans等IDE,然后将此类方法添加到User
类:
public function foo() {
$this->profile->...
}
...再见自动完成。即使您使用如下模式编码接口(I中的SOLID),情况也是如此:
<?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
),否则您将不会获得任何类型的提示。您的属性基本上是“隐藏”在您的IDE中,如果您依赖IDE进行自动完成,代码嗅闻和重构,那么生活将变得非常困难。
答案 6 :(得分:1)
它正在等待投票作为RFC https://wiki.php.net/rfc/anonymous_classes
答案 7 :(得分:1)
我认为我通过使用命名空间为这个问题写了一个优雅的解决方案。在我的例子中,内部类不需要知道他的父类(就像Java中的静态内部类)。作为一个例子,我创建了一个名为&#39; User&#39;和一个名为&#39; Type&#39;的子类,在我的示例中用作用户类型(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(如何调用&#39;子类&#39;的示例)
<?php
require_once("User.php");
//calling a subclass reference:
echo "Value of user type Admin: ".User\Type::ADMIN;
?>
答案 8 :(得分:0)
您可以像这样在PHP 7中:
class User{
public $id;
public $name;
public $password;
public $Profile;
public $History; /* (optional declaration, if it isn't public) */
public function __construct($id,$name,$password){
$this->id=$id;
$this->name=$name;
$this->name=$name;
$this->Profile=(object)[
'get'=>function(){
return 'Name: '.$this->name.''.(($this->History->get)());
}
];
$this->History=(object)[
'get'=>function(){
return ' History: '.(($this->History->track)());
}
,'track'=>function(){
return (lcg_value()>0.5?'good':'bad');
}
];
}
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();
答案 9 :(得分:-4)
将每个类放入单独的文件中并“需要”它们。
user.php的
<?php
class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
require_once('UserProfile.php');
require_once('UserHistory.php');
$this->profile = new UserProfile();
$this->history = new UserHistory();
}
}
?>
UserProfile.php
<?php
class UserProfile
{
// Some code here
}
?>
UserHistory.php
<?php
class UserHistory
{
// Some code here
}
?>