繁体   English   中英

在 PHP 中,是否可以在不调用类的构造函数的情况下创建 class 的实例?

[英]In PHP, is it possible to create an instance of an class without calling class's constructor?

无论如何,是否可以在不调用其构造函数的情况下创建 php class 的实例?

我有 Class A,在创建它的实例时传递文件,在 Class A 的构造函数中打开文件。

现在在 Class A 中,有 function 我需要调用但不需要传递文件,因此不需要使用打开文件的构造函数功能,因为我没有传递文件。

所以我的问题是,是否可以通过任何方式创建 PHP class 的实例而不调用其构造函数?

请注意,我无法使用 function 中的一些 class 属性来生成 function static。

在你的情况下,我建议考虑重新设计你的代码,这样你就不需要做这样的事情,但回答你的问题: 是的,它是可能的

您可以使用ReflectionClass ,它的方法是在PHP 5.4中引入的newInstanceWithoutConstructor方法然后,在不调用其构造函数的情况下创建类的实例非常容易:

$reflection = new ReflectionClass("ClassName");
$instance = $reflection->newInstanceWithoutConstructor(); //That's it!

注意:以下解决方案适用于PHP 5.3及更低版本。 从PHP 5.4开始,您也可以通过Reflection进行此操作,如本页其他地方所示

这确实是可能的。

PHPUnit_Framework_MockObject_Generator修改

1  $myClass = unserialize(
2      sprintf(
3          'O:%d:"%s":0:{}',
4          strlen('MyClass'), 'MyClass'
5      )
6  );

请记住,这样的代码在PHPUnit这样的框架中都是好的和合理的。 但是如果你的生产代码中必须有这样的代码,你可能会做一些非常奇怪的事情。


既然你要求解释:

序列化Object时,您将获得该对象的字符串表示形式。 例如

echo serialize(new StdClass) // gives O:8:"stdClass":0:{}

O表示对象。 8是类名的字符串长度。 "stdClass"显然是类名。 序列化对象具有0属性集( 稍后会更多 ),由空花括号表示。 :只是分隔符。

可以使用unserialize函数将每个序列化字符串重新创建为其原始“实时”值。 这样做会绕过构造函数。 就像Charles正确地指出了魔法方法__wakeup()如果被定义就会被调用(就像序列化时会调用__sleep() )。

在第3行中,您会看到一个准备与sprintf一起使用的字符串(第2行)。 如您所见,类名的字符串长度为%d ,类名以%s给出。 这是告诉sprintf它应该使用第4行传递给它的第一个参数作为数字而第二个参数作为字符串使用。 因此,sprintf调用的结果是

'O:7:"MyClass":0:{}'

您可以将第4行中出现的“MyClass”替换为所需的类名,以创建要实例化的类的序列化字符串,而无需调用控制器。

然后,该字符串被反序列化为第1行中的MyClass实例,绕过构造函数。 反序列化的实例将包含它的类的所有方法以及任何属性。 如果MyClass中有属性,则这些属性将具有其默认值,除非您向序列化的虚拟字符串添加不同的值。

那已经是它了。 没什么太神奇的了。

始终会调用类构造函数。 不过,有几种方法可以解决这个问题。

第一种方法是在构造函数中为参数提供默认值,并且只有在这些参数设置时才对这些参数执行某些操作。 例如:

class MyClass {
    public __construct($file = null) {
        if ($file) {
            // perform whatever actions need to be done when $file IS set
        } else {
            // perform whatever actions need to be done when $file IS NOT set
        }
        // perform whatever actions need to be done regardless of $file being set
    }
}

另一个选择是扩展您的类,使子类的构造函数不调用父类的构造函数。

class MyParentClass {
    public __construct($file) {
        // perform whatever actions need to be done regardless of $file being set
    }
}

class MyChildClass extends MyParentClass {
    public __construct() {
        // perform whatever actions need to be done when $file IS NOT set
    }
}

将你需要的函数设置为静态不是更好 - 改变类A以便它有另一个不带任何结构的构造函数


如果一个类具有不访问类中任何非静态属性或函数的函数,则可以将其设置为静态。

class A{
    public function __construct($arg1){
    }

    public static function foo(){
        //do something that doesn't involve the class properties
    }
}

然后可以在不必构造类的情况下调用它

//the constructor will not be called as we haven't created an instance of A
A::foo();

静态函数和非静态函数之间的区别在于静态函数不能访问也是静态函数的类属性。 因此,如果在foo()你有任何使用$this->代码,你就无法使它成为静态的。

这样的事情行得通吗?

class User{

  private $db;

  public function __construct(Database $db){
    $this->db = $db;
  }

  public function getUser(){
    return $this->db->query("select * from user where id = '123' limit 1");
  }

  public static function getInstance(){
    $db = new Database();
    return new User($db);
  }
}

class Other{

  public function getUser(){
    $class = User::getInstance();
    return $class->getUser();
  }
}

你可以使方法静态并从类上下文而不是对象上下文中调用它。

在代码中它看起来像这样:

class A {
  public static function func($txt) {
    print($txt);
  }
}

A::func('test');

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM