我每天都在努力创造新的东西。现在我想使函数成为动态加载助手而不包含并创建新的类对象
在代码点火器中,看起来像
$this->load->helper('syslog_helper');
现在我们可以使用
syslog_helper->some_function()
文件是自动的,包括,对象是自动创建的,我们可以使用它们
问题是:我如何使用纯PHP做同样的想法?
像这样
创建一个类名 load,其中包含一个名为 helper 的方法,如果您希望它可以通过 $syslog_helper 访问,则 load 需要能够调用原始类,以便在创建实例时将$this作为其构造函数的一部分传递。那么主类应该使用魔术__set方法等
要加载的帮助程序类:
class syslog_helper{
}
装载机类:
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function helper($class){
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once HELPER_DIR.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$class = new $class;
}
}
基本控制器类
class foo{
public $data = [];
public $load;
public function __construct(){
//create the loader instance, pass an instance of the controller (this)
$this->load = new loader($this);
}
public function bar(){
//call the loader's helper() method with the name of the helper class
$this->load->helper('syslog_helper');
print_r($this->syslog_helper);
}
public function __set($key, $value){
//create a dynamic property
$this->data[$key] = $value;
}
public function __get($key){
//get a dynamic property
return $this->data[$key];
}
}
称之为:
(new foo)->bar();
输出:
syslog_helper Object
(
)
沙盒
正如你在上面看到的,$this->syslog_helper
就像 CI 一样填充了我们的帮助程序类。
所以它按以下顺序流动:
- $foo = new foo- 创建控制器的实例,分配加载器类(带有对控制器的反向引用)
$this->load = new loader($this);
- $foo->bar()- 调用
bar()
这将是控制器中的请求函数,例如 URL 路由到的内容。 - $foo->load->helper('syslog_helper') - 使用
load
属性(加载程序实例)调用其帮助程序方法,将帮助程序类的名称作为字符串传递。帮助程序方法应需要类文件,然后创建该类的实例。new $class
- $this->obj->$class = 新$class;- 然后将该实例分配给一个动态属性,该属性的名称与传入的内容相同
- $this->obj->$class- 触发控制器的__set魔术方法,将辅助程序的实例存储在
Controler->data[$helper]
- $foo->syslog_helper()- 触发控制器的__get魔术方法,返回
Controler->data[$helper]
或我们刚刚创建的帮助程序的实例。
我只是编造的,但我相信 CI 是相似的。 您可以查看控制器等的父类。看看他们是如何做到的。
希望这是有道理的...
您可以对上述代码进行一个简单的改进
我认为 CI 这样做是为了允许属性的别名......喜欢这个:
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function helper($class, $alias = null){
//if no alias default to the class name
if(!$alias) $alias = $class;
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.'helpers/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$alias = new $class;
}
}
现在,如果我们在控制器的 bar 方法中执行此操作:
class foo{
public function bar(){
//call the loader's helper() method with the name of the helper class
$this->load->helper('syslog_helper');
print_r($this->syslog_helper);
$this->load->helper('syslog_helper', 'syslog_helper_2');
print_r($this->syslog_helper_2);
}
输出:
syslog_helper Object
(
)
syslog_helper Object
(
)
您现在有 2 个帮助程序实例,一个名为syslog_helper
,另一个名为syslog_helper_2
。如果我们不为它们设置别名,第二次调用将简单地覆盖控制器中的属性,只留下一个实例。
所以你可以在上面看到,我们基本上用 1 行代码增加了很大的灵活性。 重大改进不一定很复杂。
沙盒
显然,您应该对此进行更多充实。通过添加更多的东西,比如对不存在的类(文件)进行错误检查,__unset
和__isset
魔术方法等......但是,这是您想要的基本功能。
同样,您可以添加、model
和library
方法,唯一真正的区别是位置。 为此,我可能会使用魔术__call
方法,而不是 3 个执行相同操作的函数。
实现加载>模型、加载>库和加载>帮助程序
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function __call($method, $args){
//type => folder
$allowed = ['helper'=>'helpers','model'=>'models', 'library'=>'libraries'];
if(!isset($allowed[$method])) throw new Exception('Unknown method '.$method);
if(!isset($args[0])) throw new Exception('Missing required argument for method '.$method);
$class = $args[0];
$alias = isset($args[1]) ? $args[1] : $class;
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.$allowed[$method].'/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$alias = new $class;
}
}
沙盒
实现单例
单例基本上是在将来的调用中重用该类的相同实例,您可以通过对加载器进行更多更改来实现这一点:
class syslog_helper{
public $test;
}
class loader{
protected $obj;
protected static $instances = [];
public function __construct($obj){
$this->obj = $obj;
}
public function __call($method, $args){
//type => folder
$allowed = ['helper'=>'helpers','model'=>'models', 'library'=>'libraries'];
if(!isset($allowed[$method])) throw new Exception('Unknown method '.$method);
if(!isset($args[0])) throw new Exception('Missing required argument for method '.$method);
$class = $args[0];
$alias = isset($args[1]) ? $args[1] : $class;
//if this is the first time we instantiated [$method][$alias] save it
if(!isset(static::$instances[$method][$alias])){
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.$allowed[$method].'/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
static::$instances[$method][$alias] = new $class;
}
//return the saved static instance
$this->obj->$alias = static::$instances[$method][$alias];
}
}
class foo{
public $data = [];
public $load;
public function __construct(){
$this->load = new loader($this);
}
public function bar(){
$this->load->helper('syslog_helper');
print_r('bar::Test before: '.$this->syslog_helper->test."n");
$this->syslog_helper->test = 'test';
print_r('bar:Test after: '.$this->syslog_helper->test."n");
}
public function biz(){
$this->load->helper('syslog_helper');
print_r('biz:Test: '.$this->syslog_helper->test."n");
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key){
return $this->data[$key];
}
}
$foo = new foo;
$foo->bar();
$foo->biz();
输出:
bar::Test before:
bar:Test after: test
biz:Test: test
沙盒
这里重要的是,当我们从控制器中的biz()
调用$this->load->helper('syslog_helper');
时,我们将获得之前创建的帮助程序的相同实例。 您可以说出这一点,因为我添加到帮助程序的公共属性保留了我们在bar()
中设置的值。 您实际上可以在代码中的任何位置调用它,并获得具有相同数据存储在其中的相同实例,为了示例而言,这样做更容易(更短)。
如果您在多个类中需要相同的帮助程序,而不是创建多个实例,您可以重用它们,这将非常有用。 我不确定 CI 是否在我的头顶上这样做......哈哈
在这种情况下,我认为将它们作为单例来做是可以接受的,如果您需要一个新副本,您可以为其别名,然后这将是一个独立的实例。
我应该补充的最后一件事是,CI 可能不会将控制器实例传递给加载器类。 这是因为 CI 从路由调用控制器,因此它已经拥有控制器的实例可供使用。 由于 CI 是一个单例,它可能可以通过加载器内部的$CI = get_instance();
访问,因此无需按照我在 CI 框架中展示的方式传递它。 基本上,他们只是以不同的方式访问相同的数据。
干杯!