php init()和__construct() 区别

本文详细介绍了PHP中__construct()与init()的区别及使用场景,并通过具体代码示例展示了如何实现单例模式。

区别

__construct()

__construct(): 是PHP内置的构造函数,实例化之前 PHP 解析引擎自动调用,做一些初始化的工作或者外部服务器检测的工作。在实例化对象之前需要做的工作都写在这里

init() :

init()方法一般都是自己定义的,可以随便写名字,一般会在__construct() 中写 调用。功能和__construct()差不多但是需要自己写调用, 其实就是普通方法,只是这个方法大家一般默认都是用来初始化东西的。

protected function __construct

如果将 __construct 定义为 protected 那么就不能直接实例化了,需要添加一个额外的入口,这个入口一般是静态方法public static function instance

<?php


class Test {



	public function init(array $config) {
		echo "3: init..... \n";
		var_dump($config,);
	}

	protected function __construct(array $config = array()) {
		echo "2: __construct ..... \n";
		phpversion() > "5.3" || exit("版本过低,请升级版本");
		$this->init($config);
	}

	public function instance($config = array()) {
		echo "1: instance ..... \n";
		return new self($config);
	}


}

$config = array("test"=>1);

$test = Test::instance($config);

var_dump($test);

如果不需要参数只需要将所有的参数$config去掉都好了

结果

1: instance .....
2: __construct .....
3: init.....
array(1) {
  'test' =>
  int(1)
}
class Test#1 (0) {
}

单例模式

class Singleton  
{  
    /** 
     * Description:(1)静态变量,保存全局实例,跟类绑定,跟对象无关 
     *             (2)私有属性,为了避免类外直接调用 类名::$instance,防止为空 
     */  
    private static $instance;  
      
    /** 
     * Description:数据库连接句柄 
     */  
    private $db;  
      
    /** 
     * Description:私有化构造函数,防止外界实例化对象 
     */  
    private static function __construct()  
    {  
    }  
      
    /** 
     * Description:私有化克隆函数,防止外界克隆对象 
     */  
    private function __clone()  
    {  
    }  
      
    /** 
     * Description:静态方法,单例访问统一入口 
     * @return Singleton:返回应用中的唯一对象实例 
     */  
    public static function GetInstance()  
    {  
        if (!(self::$instance instanceof self))  
        {  
            self::$instance = new self();  
        }  
        return self::$instance;  
    }  
      
    /** 
     * Description:获取数据库的私有方法的连接句柄 
     */  
    public function GetDbConnect()  
    {  
        return $this->db;  
    }  
}  
  • 需要一个保存类的唯一实例的静态成员变量(通常$instance为私有变量)
  • 构造函数和克隆函数必须声明为私有的,为了防止外部程序new类从而失去单例模式意义
  • 必须提供一个访问这个实例的公共静态方法,从而返回唯一实例的一个引用

转载于:https://my.oschina.net/solate/blog/733518

我第一个问题中,我的base类是这样的: <?php /** * 自定义Controller基类,所有Controller必须继承 * 同时,子类必须实现_init函数 * */ abstract class Kepler_Controller_Base extends Yaf_Controller_Abstract{ //输入参数 protected $_arrInput = array(); //结果数组 protected $_arrResult = array(); //是否需要授权 protected $_authFlag = true; //是否需要IP限制 protected $_ipFlag = false; //子类初始化函数,强制子类实现 abstract public function _init(); /* * 禁止子类重写覆盖init方法 * 在这里初始化一些基本信息 */ final public function init(){ $this->_arrInput = Kepler_Http::Input(); $this->_arrResult = Kepler_Result::Original(); var_dump(class_exists('Yaf_Controller_Abstract')); // 应输出 true var_dump(new ReflectionClass('Yaf_Controller_Abstract')); // 查看类定义位置 var_dump(1); var_dump($this->_authFlag); //先执行初始化函数 $this->_init(); var_dump(4); var_dump($this->_authFlag); die; //再执行钩子 $this->beforeHook(); } /* * 执行action之前钩子 * 比如:校验权限、校验IP等等 */ public function beforeHook() { $this->checkAuth(); $this->checkIp(); } 我的接口类是这样的: <?php class UserController extends Kepler_Controller_Base { // 在构造函数中设置(优先于 init() 执行) public function __construct($request, $response, $view) { parent::__construct($request, $response, $view); $this->_authFlag = false; // 提前设置 } public function _init() { var_dump(2); var_dump($this->_authFlag); //不用校验登陆 $this->_authFlag = false; var_dump(3); var_dump($this->_authFlag); } //登陆提交 public function loginpostAction(){ $model = new User_Service_LoginPost(); $this->_arrResult = $model->execute(); } 但是我请求这个loginpostAction接口时,代码只走了base类,根本就不会走接口类中的public function _init() { var_dump(2); var_dump($this->_authFlag); //不用校验登陆 $this->_authFlag = false; var_dump(3); var_dump($this->_authFlag); }
最新发布
07-24
<think>我们正在分析Yaf框架中控制器初始化方法调用的问题。用户描述的场景是:在自定义基类Kepler_Controller_Base中,init方法调用了抽象方法_init,但子类UserController中实现的_init方法在请求时没有被执行。我们需要找出原因并提供解决方案。 首先,回顾Yaf控制器的生命周期: 1. 当请求到达时,Yaf会实例化对应的控制器(如UserController)。 2. 在控制器实例化后,Yaf会自动调用该控制器的init方法(如果存在)。 在用户描述的代码结构中: - Kepler_Controller_Base 继承自 Yaf_Controller_Abstract。 - Kepler_Controller_Base 是一个抽象类,它包含一个抽象方法 _init。 - Kepler_Controller_Base 的 init 方法(非抽象)中调用了 $this->_init()。 - UserController 继承自 Kepler_Controller_Base,并实现了 _init 方法。 根据上述结构,预期的行为是: 1. 当UserController被实例化后,Yaf会调用其init方法(因为UserController没有覆盖init方法,所以会调用基类Kepler_Controller_Base的init方法)。 2. 基类的init方法中调用$this->_init(),由于$this是UserController的实例,因此应该调用UserController的_init方法。 但用户观察到_init方法没有被执行,可能的原因有: **原因一:Yaf框架的控制器初始化方法命名规范** Yaf框架中,控制器的初始化方法应该是`init`,而不是`_init`。在Yaf的控制器中,如果存在`init`方法,它会在控制器实例化后自动调用。但是,如果基类中已经定义了`init`方法,而子类没有覆盖,那么基类的`init`方法会被执行。然而,这里用户将抽象方法命名为`_init`(带下划线),而基类的`init`方法(不带下划线)调用了`_init`。 问题可能在于:用户可能错误地覆盖了Yaf的初始化方法。Yaf期望的初始化方法是`init`,而用户自定义了一个`_init`方法,但Yaf并不会自动调用`_init`。因此,只有基类的`init`方法被Yaf调用,而基类的`init`方法调用了`_init`,所以子类的`_init`应该被调用。但用户说没有执行,所以我们需要考虑其他原因。 **原因二:子类未正确实现抽象方法** 如果子类UserController没有实现抽象方法`_init`,那么会导致编译错误(无法实例化抽象类)。但用户描述中已经说明子类实现了,所以这个原因可以排除。 **原因三:方法可见性** 在PHP中,子类实现抽象方法时,方法的可见性不能比抽象方法更严格。抽象方法`_init`在基类中可能是`protected`或`public`(抽象类中的抽象方法不能是private)。子类实现时,必须使用相同或更宽松的可见性。如果子类将`_init`方法定义为`private`,则会导致调用失败。但通常我们使用`protected`或`public`,所以需要检查子类中方法的可见性。 **原因四:自动加载问题** 如果子类UserController的文件没有被正确加载,那么当基类调用`$this->_init()`时,由于子类尚未被定义,可能无法找到子类的`_init`方法。但根据问题描述,控制器已经被实例化,说明UserController类已经被加载,所以这个可能性较低。 **原因五:Yaf的控制器初始化流程覆盖** Yaf在实例化控制器后,只会调用`init`方法(不带下划线)。如果用户在基类中定义了`init`方法,并且在该方法中调用`_init`,那么只要子类正确实现了`_init`,就应该被调用。所以,我们需要检查基类中`init`方法的实现子类中`_init`方法的实现。 **可能的问题代码:** 基类Kepler_Controller_Base可能如下: ```php abstract class Kepler_Controller_Base extends Yaf_Controller_Abstract { public function init() { // 调用子类实现的初始化方法 $this->_init(); } abstract protected function _init(); } ``` 子类UserController可能如下: ```php class UserController extends Kepler_Controller_Base { protected function _init() { // 初始化代码 echo "UserController _init called"; } public function indexAction() { // 动作 } } ``` 按照上述代码,当访问UserController的indexAction时,基类的init方法会被调用,然后调用子类的_init方法。所以,理论上应该输出"UserController _init called"。 如果用户没有看到输出,可能的原因有: 1. 子类的`_init`方法被错误地定义为私有(private)?但上面例子是protected,符合要求。 2. 子类中可能意外覆盖了`init`方法?如果子类覆盖了`init`方法,但没有调用`parent::init()`,那么基类的`init`方法就不会执行,也就不会调用`_init`。例如: ```php class UserController extends Kepler_Controller_Base { public function init() { // 覆盖了基类的init方法,但没有调用父类的init // 所以基类的init不会执行,_init也不会被调用 } protected function _init() { // 不会被执行 } } ``` **解决方案:** 1. 检查子类UserController是否覆盖了`init`方法。如果覆盖了,确保在子类的`init`方法中调用`parent::init()`。 2. 确保子类的`_init`方法可见性正确(至少为protected,不能是private)。 3. 检查基类Kepler_Controller_Base中`init`方法的可见性。在Yaf中,控制器的init方法必须是public,因为Yaf框架在外部调用它。所以基类的init方法应该是public。 **总结:** 最可能的原因是子类覆盖了`init`方法但没有调用父类的`init`方法,导致基类的`init`方法没有执行,进而没有调用`_init`。因此,需要检查子类中是否有`init`方法,如果有,必须调用`parent::init()`。 如果子类没有覆盖`init`方法,那么问题可能出在其他地方,比如文件路径错误导致子类文件未正确加载(但控制器能实例化说明文件已加载),或者有语法错误导致方法不可用。 建议用户检查子类UserController的代码,确保没有意外覆盖`init`方法,或者如果覆盖了,必须调用父类的`init`。 另外,为了调试,可以在基类的`init`方法中添加日志,确认是否被调用;在子类的`_init`方法中也添加日志,确认是否被调用。这样可以帮助定位问题。 **相关引用:** 在Yaf手册中,关于控制器的说明:控制器的初始化方法为`init`,在控制器实例化后立即被调用[^1]。</think>### Yaf框架控制器初始化问题分析与解决方案 #### 问题原因分析 在Yaf框架中,控制器初始化方法的调用遵循特定规则,您遇到的基类`init`方法执行而子类`_init`方法未执行的问题,主要由以下原因导致: 1. **方法命名冲突** Yaf框架对控制器初始化方法有严格约定: - 框架**自动调用**的初始化方法必须是`init`(小写) - 您自定义的`_init`(带下划线)方法**不会被框架自动调用**[^1] ```mermaid graph LR A[请求到达] --> B[实例化UserController] B --> C{是否存在 init 方法?} C -->|是| D[执行基类init方法] C -->|否| E[跳过初始化] D --> F[基类init调用$this->_init] F --> G{子类是否实现 _init?} G -->|是| H[执行子类_init] G -->|否| I[报错] ``` 2. **调用链中断** 如果子类覆盖了`init`方法但未调用`parent::init()`,会导致基类初始化链断裂: ```php class UserController extends Kepler_Controller_Base { // 错误示例:覆盖init但未调用父类方法 public function init() { // 此处缺少 parent::init(); } } ``` 3. **可见性错误** 抽象方法`_init`在基类中的声明必须与子类实现**严格匹配**: - 基类声明为`protected abstract function _init();` - 子类实现必须是`protected function _init()`(不能是private) #### 解决方案 ##### 方案1:规范命名(推荐) 修改基类子类方法命名,符合Yaf规范: ```php // 基类 Kepler_Controller_Base.php abstract class Kepler_Controller_Base extends Yaf_Controller_Abstract { // 使用标准init方法(无下划线) public function init() { $this->doInit(); // 调用实际初始化逻辑 } // 定义需要子类实现的抽象方法 abstract protected function doInit(); } // 子类 UserController.php class UserController extends Kepler_Controller_Base { // 实现初始化逻辑 protected function doInit() { // 子类初始化代码 echo "UserController初始化成功!"; } } ``` ##### 方案2:显式调用父类方法 如果必须保留`_init`命名: ```php class UserController extends Kepler_Controller_Base { // 必须显式调用父类init public function init() { parent::init(); // 关键调用 // 其他初始化代码... } protected function _init() { // 子类初始化代码 } } ``` #### 验证步骤 1. **检查方法可见性** 确保所有`_init`方法均为`protected`: ```php // 基类 abstract protected function _init(); // 子类 protected function _init() {...} ``` 2. **启用框架调试模式** 在`application.ini`中添加: ```ini ; 开启错误显示 application.dispatcher.throwException = 1 application.dispatcher.catchException = 0 ; 开启调试模式 yaf.environ = "develop" ``` 3. **添加日志追踪** 在基类中添加调用日志: ```php public function init() { error_log("调用基类init: ".get_class($this)); $this->_init(); } ``` #### 底层原理说明 Yaf框架通过扩展层直接调用控制器的`init`方法[^2]: ```c // yaf_dispatcher.c (简化) zend_class_entry* controller = ... // 获取控制器类 zend_function* init = zend_hash_str_find_ptr(&controller->function_table, "init", 4); if(init) { zend_call_method(..., init, ...); // 直接调用init方法 } ``` 这意味着: 1. 只有命名为`init`的方法会被自动调用 2. 自定义方法(如`_init`)需通过`init`显式调用 3. 方法查找区分大小写(必须全小写`init`) 通过规范命名调用链,即可解决子类初始化方法未执行的问题[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值