我正在努力使每一天都变得新鲜。现在,我想在不包含和创建新的类对象的情况下使函数成为动态加载帮助器
在代码点火器中,看起来像
$this->load->helper('syslog_helper');
现在我们可以使用
syslog_helper->some_function()
文件是自动包含的,对象是自动创建的,我们可以使用它们
问题是:如何使用纯PHP进行相同的思考?
答案 0 :(得分:3)
赞
创建一个具有名为helper的方法的类名称加载,如果您希望使用$ syslog_helper对其进行访问,则加载需要能够调用原始类,因此在创建实例时将$ this传递为其构造函数的一部分。然后,主类应使用神奇的__set方法等
要加载的帮助程序类:
class syslog_helper{
}
Loader类:
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一样填充了我们的帮助程序类。
所以它按以下顺序流动:
$this->load = new loader($this);
bar()
,这将是控制器中的请求功能,例如URL路由到的功能。load
属性(加载程序的实例)调用其helper方法,并传递helper类的名称作为字符串。 Helper方法应该需要类文件,然后创建该类的实例。 new $class
Controler->data[$helper]
Controler->data[$helper]
或我们刚刚创建的帮助程序的实例。我只是弥补了这一点,但是我确信CI是相似的。您可以查看Controllers的父类,等等。看看它们是如何做到的。
希望如此...
您可以对上述代码进行一次简单的改进
我认为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
。如果我们不给它们起别名,那么第二个调用将简单地覆盖控制器中的属性,只剩下一个实例。
因此您可以在上面看到,我们用基本上一行的代码增加了很多灵活性。大的改进不必太复杂。
显然,您应该再充实一点。通过添加更多内容,例如对不存在的类(文件)进行错误检查,__unset
和__isset
魔术方法等……但是,这是您想要的基本功能。
类似地,您可以添加model
和library
方法,唯一的区别是位置。为此,我可能会使用神奇的__call
方法,而不是执行相同功能的3个函数。
实施load->模型,load->库和load-> helper
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
这里重要的是,当我们从控制器中的$this->load->helper('syslog_helper');
调用biz()
时,我们得到的是之前创建的帮助程序的相同实例。您可以这样说是因为我添加到帮助器的公共属性保留了我们在bar()
中设置的值。实际上,您可以在代码中的任何位置调用此函数,并获得其中存储有相同数据的相同实例,例如,这样做更容易(更短)。
如果您在多个类中需要相同的帮助程序,这很有用,而不是创建多个实例即可重新使用它们。我不确定CI是否会立即执行此操作...大声笑
在这种情况下,我认为将它们作为一个单例进行是可以接受的,如果您需要一个新副本,则可以对其进行别名,然后将其作为一个独立实例。
我要补充的最后一件事是CI可能不会将控制器实例传递给加载程序类。这是因为CI从路由中调用了控制器,因此它已经可以使用Controller的实例。而且由于CI是单例,因此很可能可以在加载程序内部使用$CI = get_instance();
来访问它,因此无需按照我在CI框架中展示的方式传递它。基本上,他们只是以不同的方式访问相同的数据。
干杯!