求教一个继承类方案,新建文件重写类,不影响系统自身文件的更新如何实现的?

kspade

是这样的最近我在二开一个cms系统,我发现他可以新建一个文件自己写各种功能函数方法,而在调用系统模型方法时,自己新建的文件里面写的方法也可以被调用

我可能描述得不太对,下面我说一下大概的逻辑

就是一个开源系统
系统某些功能函数文件路径为:/cms/Model/Member.php

<?php 
namespace Phpcmf\Model;

class Member extends \Phpcmf\Model {
        public function author($uid) { 
        if (!$uid) {
            return dr_lang('游客');
        } 
        return $this->username($uid);
    }
}

此文件是系统文件 - 不要修改当cms核心更新时会被覆盖

二次开发时,支持复制出去修改 增加 功能函数方法,操作方法:
1、新建文件:/My/Member.php
2、新写方法体:

<?php 
namespace My\Model;
// 重写类
class Member extends \Phpcmf\Model\Member{
    //开始写你的方法
    public function login_TTT($uid) { 
        return $uid.'登录成功';
    }
}

就是这样 当我调用:Phpcmf\Model\Member 时 author 和 login_TTT 都可以使用。

请问一下这样是如何实现的啊?我试图用webman 也这样实现但是没搞懂我应该怎么写才会让我自己新建的文件被加载进去。

我想的是我想做一个开源系统,核心是由我维护并自动更新的,使用者可以自己新建文件 开发各种功能,当我更新核心时,他们所二开的那些功能不受影响!
513 3 0
3个回答

nitron

看不明白,B extends A之后,A可以使用B新加的方法?
你说的是My\Model\Member可以使用author和login_TTT,还是Phpcmf\Model\Member?

  • kspade 2023-08-02

    A:Phpcmf\Model\Member 是系统定义的各种功能方法
    B:My\Model\Member 是二开自己定义的各种方法

    当你在中controller 中使用时 use Phpcmf\Model\Member 后:

    可以调用A B 2个文件中的所有方法 这个意思

  • kspade 2023-08-02

    参考:https://www.xunruicms.com/doc/1277.html 我想知道它是如何实现的

  • nitron 2023-08-02

    你这个文档,说的不就是继承么???我怎么感觉你理解错他的意思了?
    就类似Workerman的Request类你想新增方法,就继承这个Request类后新增方法就好,Workerman更新也不会影响你使用的方式

  • liudada1204 2023-08-02

    他的意思应该是Phpcmf\Model\Member可以调用author和login_TTT,也就是用父类调用子类的方法

  • nitron 2023-08-02

    @liudada1204 我知道,但是这个文档说的不就是继承么,没说父类调用子类啊

  • liudada1204 2023-08-02

    @nitron 是的,文档应该就是你说的那意思

  • kspade 2023-08-02

    webman 中应当如何做?

    我的意思:
    比如目录:/plugin/user/app/api/
    下面的某个文件(testa.php )

    然后我在根目录下新建一个目录+文件:ceshi\testb.php:
    class Test extends \plugin\user\app\api\Testa {

    }

    在写项目调用时,我在controller下面比如新建个文件Abccontroller :
    use plugin\user\app\api\testa
    就可以调用到
    \plugin\user\app\api\testa.php 和 ceshi\testb.php 这2个文件里面的所有方法

    而实际测试只能调用到:\plugin\user\app\api\testa.php 内的方法 并不能调用到:ceshi\testb.php 里面自己定义的方法

  • nitron 2023-08-02

    那就照下面北月+muyu说的,那个可以,不过限制太大,extends出来的model换个目录就不行了,除非你加入多个目录

  • kspade 2023-08-02

    我试过了。使用体验太差了。当有错误或者返回某些前置的code时 取不到直接就跳过了

  • 10bang 2023-08-02

    你这个Phpcmf也不行吧?确定测试过?要用他框架Phpcmf/Service去调用才行吧?

  • kspade 2023-08-04

    的确如楼上兄台所言

    好像是通过Service 的
    最终调用方法为:
    \Phpcmf\Service::M('member')->login_TTT(1);

    请教一下webman 如何想实现如此,有没有教程 案例,我没搞懂这是怎么实现的!我写了继承方法但是压根没作用

  • nitron 2023-08-04

    看北月的回答,看了下他那个代码,一样的道理

    // dayrui/Fcms/Core/Service.php    390行开始 
            if (!isset(static::$instances[$_cname]) or !is_object(static::$instances[$_cname])) {
                require_once $classFile;
                // 自定义继承类
                if ($extendFile && is_file($extendFile)) {
                    if ($namespace && is_file($appFile)) {
                        require_once $appFile;
                        $newClassName = '\\Phpcmf\\Model\\'.ucfirst($namespace).'\\'.$className;
                    } else {
                        require $extendFile;
                        $newClassName = '\\My\\Model\\'.$className;
                    }
                } else {
                    $newClassName = '\\Phpcmf\\Model\\'.$className;
                    // 多个应用引用同一个类名称时的区别
                    if ($namespace) {
                        $newClassName2 = '\\Phpcmf\\Model\\'.ucfirst($namespace).'\\'.$className;
                        if (class_exists($newClassName2)) {
                            static::$instances[$_cname] = new $newClassName2();
                            return static::$instances[$_cname];
                        }
                    }
                }
    
                static::$instances[$_cname] = new $newClassName();
            }

    \Phpcmf\Service::M($name = '', $namespace = '')
    不带命名空间就扫描My目录下对应的Model,带了就扫码指定命名空间的Model

    结论:就是继承,明显是不是什么父类调用子类的方法,跟北月说的方式大差不差

  • kspade 2023-08-04

    webman 的话 我是应该把它写到:app/functions.php 还是每个pulgin 下面server 定义一个,我就是没想到好方法,我想每个plugin下的应用核心由我开发更新,同时允许用户在根目录:diy/同plugin应用名/ 下面新建文件自定义开发,这样我负责升级plugin 应用下面核心,这样当我更新时直接覆盖,从而不会影响用户自己二开的代码

北月
  1. 规定重写后的文件必须放到指定目录,比如 ext/model

  2. composer.json 新增一个 psr-4 的自动加载项,"ext\\": "ext" ,执行 composer dump-autoload

  3. 规定不要在 controller 中单独使用 model 类,可以在 controller 基类中写一个方法,或者在助手函数中实现也行,哪里实现不重要,比如就叫:getModelInstance

    function getModelInstance(string $name, string $namespace = 'app\\model')
    {
    // 判断 $name、$namespace是否为空,类是否存在等的一系列操作
    // ....
    // 首字母大小写啥的,你喜欢怎么处理就怎么处理,但是必须规范化,让用户在二次开发的时候必须遵守
    // 传入的时候也是统一的,比如 $name = 'member',$namespace = 'app\model'
    // 你可以这里进行更多的限制,比如说哪些命名空间下的 model 是可以重写的
    // 拼接 model 类名
    $coreModelClassName = $namespace . '\\' . ucfirst($name);
    $extModelClassName = 'ext\\model\\' . ucfirst($name);
    if (class_exists($extModelClassName)) {
        return new $extModelClassName;
    } else {
        return new $coreModelClassName;
    }
    }
  4. controller 中调用传入 model 名,比如你的 CMS 内核有一个会员相关的 model,那么调用的时候是这样子的:

    <?php
    namespace app\controller;
    use support\Request;
    use support\Response;
    class IndexController
    {
    public function index(Request $request): Response
    {
        // 这样子写编辑器没有代码提示,体验极差
        $user = getModelInstance('user');
        echo $user->getUserId(),PHP_EOL;
        echo $user->getUserName(),PHP_EOL;
        return json(['code' => 0, 'msg' => 'ok']);
    }
    }

PS: 其实说到底就是优先使用 ext/model 下的 model 类,更进一步的做法可以封装 logicservice 层,思路就是这样子,其它的可以自己扩展。

  • kspade 2023-08-02

    我试了一下不是这个效果 不是如此
    调用的时候use 调用命名空间后直接:Member::login_TTT(123) 就和平常的一样提示那些都没问题

  • muyu 2023-08-02

    他说这个方法可以实现的,__callStatic函数做代理去尝试实例化ext/model下面的,找不到在实例化默认的

ichynul

框架使用容器技术的话可以替换容器里面实现

  • 暂无评论
🔝