创建自己的加载助手函数(如在CodeIgniter中)



我每天都在努力创造新的东西。现在我想使函数成为动态加载助手而不包含并创建新的类对象

在代码点火器中,看起来像 $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 一样填充了我们的帮助程序类。

所以它按以下顺序流动:

  1. $foo = new foo- 创建控制器的实例,分配加载器类(带有对控制器的反向引用)$this->load = new loader($this);
  2. $foo->bar()- 调用bar()这将是控制器中的请求函数,例如 URL 路由到的内容。
  3. $foo->load->helper('syslog_helper') - 使用load属性(加载程序实例)调用其帮助程序方法,将帮助程序类的名称作为字符串传递。帮助程序方法应需要类文件,然后创建该类的实例。new $class
  4. $this->obj->$class = 新$class;- 然后将该实例分配给一个动态属性,该属性的名称与传入的内容相同
  5. $this->obj->$class- 触发控制器的__set魔术方法,将辅助程序的实例存储在Controler->data[$helper]
  6. $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魔术方法等......但是,这是您想要的基本功能。

同样,您可以添加、modellibrary方法,唯一真正的区别是位置。 为此,我可能会使用魔术__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 框架中展示的方式传递它。 基本上,他们只是以不同的方式访问相同的数据。

干杯!

相关内容

最新更新