PHP简单实现动态加载模块

本文介绍了PHP如何实现动态加载模块,以扩展系统功能而不修改核心代码。通过示例代码,展示了ModuleRunner类如何初始化并管理模块,利用反射API确保模块类遵循Module接口,并通过setter函数设置模块属性。此外,还探讨了动态加载的优势,例如在社交网站等系统中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态加载模块简介

  • 动态

    可以理解成运行时按需加载代码(不是运行时编译)

  • 加载模块

    加载模块这种形式可以用于对已有系统的扩展(在不改动系统核心代码的情况下)。
    比如,你做了一个社交网站,想要为你的网站增加新的功能,如要要去重新改动原来的代码,一是麻烦,而是可能引起新的问题。
    不过如果你实现了模块话加载,那很大程度上就只需要写一个新的模块就行了,而不用去改动原来的代码。
    现在也有很多这种模式的系统,比如discuz,thinksns,opensns等等…


实现代码

这里先给出完整代码再作分析


<?php

/**
* Created by PhpStorm.
* User: 奔跑的MT
* Date: 17-1-21
* Time: 下午2:11
*/
class Person
{
   public $name;

   function __construct($name)
   {
       $this->name = $name;
   }
}


interface Module
{
   function execute();
}

class FtpModule implements Module
{

   function setHost($host)
   {
       print "FtpModule::setHost(): $host\n";
   }

   function setUser($user)
   {
       print "FtpModule::setUser(): $user\n";
   }

   function execute()
   {
       print "execute: FtpModule\n";
   }
}

class PersonModule implements Module
{
   function setPerson(Person $person)
   {
       print "PersonModule::setPerson(): ($person->name)\n";
   }

   function execute()
   {
       print "execute: PersonModule\n";
   }
}


class ModuleRunner
{
   private $configData = array(
       "PersonModule" => array('person' => 'bob'),
       "FtpModule" => array('host' => 'baidu.com',
           'user' => 'anon'
       ),
   );

   private $modules = array();


   function init()
   {
       $interface = new ReflectionClass('Module');
       foreach ($this->configData as $modulename => $params) {
           $module_class = new ReflectionClass($modulename);
           if (!$module_class->isSubclassOf($interface)) {
               throw new Exception("unknown module type: $modulename");
           }
           $module = $module_class->newInstance();
           foreach ($module_class->getMethods() as $method) {
               $this->handleMethod($module, $method, $params);
           }
           array_push($this->modules, $module);
       }
   }

   function handleMethod(Module $module, ReflectionMethod $method, $params)
   {
       $name = $method->getName();
       $args = $method->getParameters();

       if (count($args) != 1 || substr($name, 0, 3) != "set")
           return false;

       $property = strtolower(substr($name, 3));
       if (!isset($params[$property])) {
           return false;
       }

       $arg_class = $args[0]->getClass();
       if (empty($arg_class))
           $method->invoke($module, $params[$property]);
       else
           $method->invoke($module, $arg_class->newInstance($params[$property]));
   }

   /* 测试一下 */
   function execute()
   {
       foreach ($this->modules as $module) {
           call_user_func_array(array($module, 'execute'), array());
       }
   }
}

$runner = new ModuleRunner();
$runner->init();
$runner->execute();

代码分析

  • Person类

    就相当于是后面PersonModule的模型类,在这里是可有可无的

  • Module接口

    用来规范所以模块的。
    里面定义了一个execute函数,目的是统一模块入口。

  • FtpModule类

    Ftp模块类,在这里只是为了演示,没有特别意义
    这里的setter函数,即setHost和setUser也是模块规范之一,后面会解释
    execute函数便是Ftp模块的入口

  • PersonModule类

    Person模块类
    具体和FtpModule类是差不多的,就不重复叙述了

  • 核心:ModuleRunner类

    ModuleRunner类在这里相当于模块的驱动,所有模块都在这个类里面完成初始化,以便其他地方使用

    • $configData数组

      保存模块信息和配置,也可以用其他形式来保存,比如json或者xml

    • $modules数组

      保存模块实例的数组

    • init函数

      首先通过反射API来获取Module接口,目的是为了判断模块类有没有实现Module接口,也是规范模块的手段之一
      然后循环获取configData数组里面的模块配置:模块名以及相应的属性和参数。
      之后判断模块类是否实现了Module接口(isSubclassOf函数),如果没有实现则抛出异常,
      如果实现了,便获取模块类的一个实例
      然后通过handleMethod函数处理模块类中的setter函数来设置模块类的属性
      最后把模块类实例加入$modules数组。
      至此,init函数就完成了。

    • handleMethod函数

      handleMethod函数的参数是模块类实例,模块类中函数的实例,函数的参数
      首先获取函数的名字和需要的参数
      再判断函数是否合法,即是否只需要一个参数或者函数是setxxx函数
      然后通过$property = ….来获取相应的属性名,接着判断这个属性是否设置
      这些检查都通过后便可以开始调用setxx函数来设置模型的属性了
      不过还有一个要注意的,如果setxx函数的参数是一个对象,那么我们肯定要先实例化相应的类
      所以最后的一个判断便是干这个工作的

完结

至此,上述代码的运行流程便分析完了,最后的一个execute函数只是我用来测试的一个函数

我的返回结果是:


PersonModule::setPerson(): (bob)

FtpModule::setHost(): baidu.com

FtpModule::setUser(): anon

execute: PersonModule

execute: FtpModule

当然,实现模块化的方式有很多,这里是利用了PHP提供的反射API

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值