phpMVC ﹏ヽ暗。殇╰゛Y 2022-06-04 09:40 120阅读 0赞 一、 Introduction 1. 项目开发常见流程 需求调研,人员,项目经理;成果,需求说明书。 软件设计书,人员,项目经理;成果,详细设计说明书,通常就是开发过程中的主要参考技术文档。 界面设计,人员,界面工程师;成果,设计效果图。 静态网页制作,人员,前端工程师;成果,静态网页。 动态网页开发,人员,程序员;成果,基本可用的软件初级产品。 测试,人员,测试工程师;成果,可用的符合需求的产品。 上线运营,人员,运维工程师;成果,发现问题,提出新需求,提出改进建议。 2. 关于显示和逻辑相分离的开发思想 就是显示页面和页面内的计算需求分别在不同的文件,通过调用的方式以实现显示需求的效果的思想。 <?php //先计算数据 $t = date("Y-m-d H:i:s"); //然后载入一个静态网页,并在其中输出和显示计算的数据 include './time.html'; ?> <!DOCTYPE html> <head> <style type="text/css"> .div1{ color:red; size:15px; } </style> </head> <body> <div class=div1> 时间为:<?php echo $t; ?> </div> </body> </html> 3. 模版技术 比如,显示当前时间,并可以由用户来选择使用不同的风格进行显示。 基本做法是获取数据的逻辑基本保持不变,但是表示数据的文件可以有多个。只需在php文件中,根据用户的选择,以决定使用哪个模版文件。 <?php //先计算数据 $t = date("Y-m-d H:i:s"); //然后载入一个静态网页,并在其中输出和显示计算的数据 if(!empty($_GET['bg'])){ $bg = $_GET['bg']; }else{ $bg = "red"; } $file = "./time-" . $bg . ".html"; include $file; ?> 显示页面 <!DOCTYPE html> <head> <style type="text/css"> .div1{ color:red; size:15px; } .b{ background-color:#bb0000; } p a{color:black} </style> </head> <body class="b"> <p align="left"> <ahref="time1.php?bg=red">红景</a> <ahref="time1.php?bg=green">绿景</a> <ahref="time1.php?bg=blue">蓝景</a> </p> <div class=div1> 时间为:<?php echo $t; ?> </div> </body> </html> 还有另外两个类似的模版页面。 二、 MVC思想 Mvc,是model、view、controller三个词的首字母,意思是模型、视图、控制,是一种应用于浏览器/服务器的开发思想,旨在清晰化程序设计、简化开发流程、优化后续维护。以服务流程解释就是用户浏览器发出请求,请求到达控制层,也就是一个php文件,然后,先调用模型,实际也是一个php文件,通过模型获取数据,然后,载入视图,一般是html文件,最终将数据显示给用户浏览器。 详细解释 控制器,controller,是php文件,由浏览器直接请求。它需要做两件核心工作,根据需求,决定需要什么数据,并去调用模型文件,去获取该数据;根据需求,决定需要将数据显示在哪个视图文件中。 模型,model,是php文件,不能直接请求,只能被载入而发挥作用。它的核心工作是根据控制器的要求去生产数据。 视图,view,是一个伪html文件,伪是因为里面有简单的php代码,也不应有浏览器直接请求。它的作用是结合html和css,显示相应的数据。 1. 一个渗透mvc思想的案例 创建一个用户注册网页 模型层 <?php require "./MysqlUtil.class.php"; class UserModel{ function GetAllUser(){ $config = array( 'host' =>"localhost", 'port' =>3306, 'username'=> "root", 'password'=> "123456", 'names' =>"utf8", 'database'=> "php1", ); $m =MysqlUtil::GetInstance($config); $sql = "select* from stu1"; $rec = $m->GetRows($sql); return $rec; } function GetUserCount(){ $config = array( 'host' =>"localhost", 'port' =>3306, 'username'=> "root", 'password'=> "123456", 'names' =>"utf8", 'database'=> "php1", ); $m =MysqlUtil::GetInstance($config); $sql = "selectcount(*) from stu1"; $rec = $m->GetOneData($sql); return $rec; } } ?> 控制层,也可以叫控制器 <?php require './UserModel.class.php'; $obj_user = new UserModel(); $data1 = $obj_user ->GetAllUser(); $data2 = $obj_user ->GetUserCount(); include './showAllUser_view.html'; ?> 视图层 <!DOCTYPE html> <head> </head> <body> 用户列表: <?php //一般不采用在php中嵌入html的形式 /* echo "<table border='1'>"; echo"<tr>"; echo"<td>用户名</td>"; echo"<td>年龄</td>"; echo"<td>学历</td>"; echo"<td>爱号</td>"; echo"<td>来自</td>"; echo "<td>注册时间</td>"; echo"<td>操作</td>"; echo"</tr>"; foreach($data1 as $key=>$rec){ echo"<tr>"; echo"<td>" . $rec['username'] . "</td>"; echo"<td>" . $rec['age'] . "</td>"; echo"<td>{$rec['xueli']}</td>"; echo"<td>{$rec['fav']}</td>"; echo"<td>{$rec['home']}</td>"; echo"<td>{$rec['dt']}</td>"; echo "<td><ahref='mysql5.php?id={$rec['id']}' onclick = 'return queren()'>删除</a></td>"; echo"</tr>"; } echo "</table>"; */ //而是采用在html中嵌入php的方式,因为php是脚本语言,html是前端人员做的,他们使用的都是纯html开发的,而不是php, //后端人员都在html基础上在需要显示数据的地方嵌入php语言以显示数据的。如果完全采用php中嵌入html,对于前端人员是不合适的,也不是高效的。 ?> <table border='1'> <tr> <td>用户名</td> <td>年龄</td> <td>学历</td> <td>爱号</td> <td>来自</td> <td>注册时间</td> <td>操作</td> </tr> <?php foreach($data1 as$key =>$rec){ ?> <tr> <td><?phpecho $rec['username']; ?></td> <td><?phpecho $rec['age']; ?></td> <td><?phpecho $rec['xueli']; ?></td> <td><?phpecho $rec['fav']; ?></td> <td><?phpecho $rec['home']; ?></td> <td><?phpecho $rec['dt']; ?></td> <td><ahref="?id=<?php echo $rec['id']; ?>" onclick = 'returnqueren()'>删除</a></td> </tr> <?php } ?> </table> <br /> 当前用户总数:<?php echo $data2;?> </body> <script> function queren(){ var s =window.confirm("确认删除?"); return s; } </script> </html> 2. 模型层 用于处理数据的存取操作,比如表的增删改查。通常是根据控制器的要求,以返回合适的数据。有时控制器需要传递过来相应的数据,才能获取对应的结果数据,比如,获取id为4的用户的信息,此时控制器就需要传递4这个数据。 1) 模型层的典型代码模式 class 模型类名{ function 方法1(参数列表){……} function 方法2(参数列表){……} function 方法3(参数列表){……} …… } 每个方法都是为了获取某种数据,有的方法可能需要一些参数,这些方法通常需要和数据库打交道,需要mysqlUtil工具类及相关的数据库连接数据。 2) 控制器中调用模型层获取数据的典型做法 require ‘模型层类文件’; $obj = new 模型对象(); $data = $obj -> 某个方法(); 3) 基础模型类 <?php require './MysqlUtil.class.php'; class BaseModel{ //用于存储数据库工具类的实例 protected $db = null; function __construct(){ $config = array( 'host' =>"localhost", 'port' =>3306, 'username'=> "root", 'password'=> "123456", 'names' =>"utf8", 'database'=> "php1", ); $this ->db =MysqlUtil::GetInstance($config); } } ?> 在模型类中使用基础模型类 <?php require "./BaseModel.php"; class UserModel extends BaseModel{ function GetAllUser(){ $sql = "select* from stu1"; $rec = $this ->db->GetRows($sql); return $rec; } function GetUserCount(){ $sql = "selectcount(*) from stu1"; $rec = $this ->db->GetOneData($sql); return $rec; } } ?> 4) 实现模型类的单例 模型类本身不变,不保证其本身是单例的,但是通过单例工厂,去获得模型类的实例,就是单例的。设计一个单例工厂类,通过这个单例工厂类,去获取模型类的对象。 <?php class ModelFactory{ static $all_model =array(); static functionMF($model_name){ if(!isset(static::$all_model[$model_name]) || !(static::$all_model[$model_name]instanceof $model_name)){ static::$all_model[$model_name]= new $model_name(); } returnstatic::$all_model[$model_name]; } } ?> 5) 整个模型层的类库结构图 基础模型类(利用mysql工具类连接数据库,属性常用的名字为dao,data access object,数据访问对象)。 服务型模型类继承基础模型类。 单例工厂类,用于生产单例的模型类。 3. 控制器 获取请求数据,根据请求数据,以决定,调用哪个模型以获取什么数据,载入哪个视图文件以显示该数据。 //显示添加用户视图 if(!empty($_GET['act']) && $_GET['act'] == 'add'){ include './form_view.html'; return null; } //向数据库添加用户 if(!empty($_GET['act']) && $_GET['act'] == 'InsertUser'){ $username =$_POST['username']; $password =$_POST['password']; $age = $_POST['age']; $xueli = $_POST['xueli']; $fav = $_POST['fav']; $home = $_POST['home']; $aihao = array_sum($fav); $rec = $obj_user->InsertUser($username, $password, $age, $xueli, $aihao, $home); echo "添加用户成功"; echo "<ahref='?'>返回</a>"; return $rec; } 将控制器的执行代码进行封装,根据控制的功能封装到功能对应的方法中: <?php require './UserModel.class.php'; require './ModelFactory.class.php'; //$obj_user = new UserModel(); class UserController{ function DetailAction(){ $obj_user = ModelFactory::MF('UserModel'); $rec = $obj_user->FindUserById($_GET['id']); include'./userInfo.html'; } function AddAction(){ $obj_user =ModelFactory::MF('UserModel'); include'./form_view.html'; } functionInsertUserAction(){ $obj_user = ModelFactory::MF('UserModel'); $username =$_POST['username']; $password =$_POST['password']; $age =$_POST['age']; $xueli =$_POST['xueli']; $fav =$_POST['fav']; $home =$_POST['home']; $aihao =array_sum($fav); $rec = $obj_user->InsertUser($username, $password, $age, $xueli, $aihao, $home); echo "添加用户成功"; echo "<ahref='?'>返回</a>"; } function DelAction(){ $obj_user =ModelFactory::MF('UserModel'); $obj_user->DeleteUserById($_GET['id']); echo "删除成功"; echo "<ahref='?'>返回</a>"; } function IndexAction(){ $obj_user =ModelFactory::MF('UserModel'); $data1 = $obj_user->GetAllUser(); $data2 = $obj_user->GetUserCount(); include'./showAllUser_view.html'; } functionShowUpdateAction(){ $obj_user =ModelFactory::MF('UserModel'); $rec = $obj_user->FindUserById($_GET['id']); include'./form_view.html'; } functionUpdateUserAction(){ $obj_user =ModelFactory::MF('UserModel'); $id = $_POST['id']; $username =$_POST['username']; $password =$_POST['password']; $age =$_POST['age']; $xueli =$_POST['xueli']; $fav =$_POST['fav']; $home =$_POST['home']; $aihao =array_sum($fav); $rec = $obj_user->UpdateUserById($id, $username, $password, $age, $xueli, $aihao, $home); echo "修改成功"; echo "<a href='?'>返回</a>"; } } $ctrl = new UserController(); $act = !empty($_GET['act']) ? $_GET['act'] : 'Index'; $action = $act . "Action"; $ctrl ->$action(); //使用可变方法代替以下逻辑判断 /* //查看详情 if(!empty($_GET['act']) && $_GET['act'] == 'detail'){ DetailAction(); } //显示添加用户视图 else if(!empty($_GET['act']) && $_GET['act'] == 'add'){ AddAction(); } //向数据库添加用户 else if(!empty($_GET['act']) && $_GET['act'] == 'InsertUser'){ InsertAction(); } //删除用户 else if(!empty($_GET['act']) && $_GET['act'] == 'del'){ DelAction(); }else{ IndexAction(); } //*/ /* $o2 = ModelFactory::MF('UserModel'); var_dump($obj_user);echo "<br />"; var_dump($o2); //*/ ?> 控制器的划分,通常,一个项目中,会有很多的功能,通常会将一些相关功能,合在一起,称为一个模块,并使用一个控制器去表达这个模块中的各个功能,其实就是方法。 一个控制器,就是一个类,一个控制器中,就只包含了一些方法。这些方法,被称为动作,因为每个方法,就对应了用户的某个操作。习惯上,所有的动作都以Action这个词为结尾。这些动作,将会对应网页上的连接或跳转或提交动作中的act参数的值。 实际应用中,在网页的连接或跳转或提交的时候,act=XXX会写成:a=XXX。一种习惯写法。 基础控制器类,一个项目中,有多个控制器,每个控制器是一个类文件,每个控制器中都有各自的一些功能,但是它们常常有些共同的工作或事情,如,设定编码,因为是由控制器来决定显示什么数据,也就应该由其来决定使用什么编码;页面的简短信息的显示,以及跳转功能。这些共同的工作可以一个基础控制器来完成,以便低耦合,高内聚。 <?php class BaseController{ function __construct(){ header("content-type:text/html;charset=utf-8"); } //显示一定的提示文件,然后,自动跳转(可以设定停留的时间描述) function GotoUrl($msg,$url, $time){ echo"<p>$msg</p>"; echo "<ahref='$url'>返回</a>"; echo "<br/>页面将在{$time}秒之后自动跳转"; header("refresh:$time;url=$url");//自动定时跳转 } } ?> 4. 视图层 视图层的功能是展示页面的静态内容,以及相关的变量数据。数据分为,普通标量数据,比如echo $v1;;数组数据,比如foreach($arr as $key =>$value)\{……\}或单独输出,echo $arr\[‘id’\];对象数据,比如echo $o1->p1;echo $o2 ->p2;。 三、 关于MVC项目的其他常见做法 1. 请求分发器 又叫前端控制器,是根据请求类型,分别将请求发送到对应的控制器类的控制器。目的在于降低功能的耦合度,进一步提高内聚性。功能是根据传过来的c请求数据,决定作用使用哪个控制,可以设置默认值,根据传过来的a请求数据,决定使用哪个动作,可以设置默认值。 <?php $c = !empty($_GET['c'])?$_GET['c']:'User'; require './' . $c . 'Model.class.php'; require './ModelFactory.class.php'; require './BaseController.class.php'; require './' . $c . 'Controller.class.php'; $controller = $c . 'Controller'; $ctrl = new $controller(); $act = !empty($_GET['act']) ? $_GET['act'] : 'Index'; $action = $act . "Action"; $ctrl ->$action(); //使用可变方法代替以下逻辑判断 ?> 2. 目录结构的设定 为了管理的方便,通常将mvc项目的文件,根据功能分类存放在对应的文件夹,类似: <table> <tbody> <tr> <td style="vertical-align:top;"> <p>项目一级目录</p> </td> <td style="vertical-align:top;"> <p>下级目录</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Index.php</p> </td> <td> <p> </p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Controllers</p> </td> <td style="vertical-align:top;"> <p>XXXController.php</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Models</p> </td> <td style="vertical-align:top;"> <p>XXXModel.class.php</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Framework</p> </td> <td style="vertical-align:top;"> <p>MysqlUtil.php、BaseModel.class.php……</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Views</p> </td> <td style="vertical-align:top;"> <p>ShowAllUsers.html、Form_view.html……</p> </td> </tr> </tbody> </table> 3. 关于后台的初步创建 一般网站从客户界面和管理界面划分,包括前台和后台两部分。如果创建后台后,为了管理方便,项目的目录结构可以调整为: <table> <tbody> <tr> <td style="vertical-align:top;"> <p>项目一级目录</p> </td> <td style="vertical-align:top;"> <p>下级目录</p> </td> <td colspan="2" style="vertical-align:top;"> <p>下级目录</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Index.php</p> </td> <td colspan="3"> <p> </p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Application</p> </td> <td style="vertical-align:top;"> <p>front</p> </td> <td style="vertical-align:top;"> <p>Controllers</p> </td> <td style="vertical-align:top;"> <p>XXXController.php</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Models</p> </td> <td style="vertical-align:top;"> <p>XXXModel.class.php</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Views</p> </td> <td style="vertical-align:top;"> <p>ShowAllUsers.html、Form_view.html……</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>back</p> </td> <td style="vertical-align:top;"> <p>Controllers</p> </td> <td style="vertical-align:top;"> <p>XXXController.php</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Models</p> </td> <td style="vertical-align:top;"> <p>XXXModel.class.php</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Views</p> </td> <td style="vertical-align:top;"> <p>AdminIndex.html、AdminForm_view.html……</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>Framework</p> </td> <td style="vertical-align:top;"> <p>MysqlUtil.php、BaseModel.class.php……</p> </td> <td colspan="2"> <p> </p> </td> </tr> </tbody> </table> 加入后台模块后,前端控制器类也做相应的调整,例子如: <?php require './Framework/MysqlUtil.class.php'; require "./Framework/BaseModel.class.php"; require './Framework/ModelFactory.class.php'; require './Framework/BaseController.class.php'; $p = !empty($_GET['p'])?$_GET['p']:'front'; $c = !empty($_GET['c'])?$_GET['c']:'User'; require "./Application/$p/Models/" . $c . 'Model.class.php'; require "./Application/$p/Controllers/" . $c . 'Controller.class.php'; $controller = $c . 'Controller'; $ctrl = new $controller(); $act = !empty($_GET['a']) ? $_GET['a'] : 'Index'; $action = $act . "Action"; $ctrl ->$action(); //使用可变方法代替以下逻辑判断 ?> 为了开发的方便,在主页可以设计进入后台的连接,实际生产中不会这样做。 <a href="?p=back&c=Admin&a=login">进入后台</a> 4. 基础常量的设定 在mvc中,会使用到很多相对固定的目录路径,使用一个常量来表示这些固定的路径,可以起到简化代码的作用。 更重要的意义是保证在一个脚本周期内不会被有意或无意的更改。 常量一般定义在前端控制器类中。 比如定义和使用了常量后的前端控制器: <?php $p = !empty($_GET['p'])?$_GET['p']:'front'; $c = !empty($_GET['c'])?$_GET['c']:'User'; define("PLAT", $p); define("DS", DIRECTORY_SEPARATOR ); //DIRECTORY_SEPARATOR表示目录分隔符,只有2个:‘\’(windows系统),‘/’(linux系统) define("ROOT", __DIR__ . DS); //__DIR__表示当前mvc框架的根目录 define("APP", ROOT . 'Application' . DS); //application的完整路径 define("FRAMEWORK", ROOT . 'Framework' . DS); //框架基础类所在的路径 define("PLAT_PATH", APP . PLAT . DS); //平台所在目录 define("CTRL_PATH", PLAT_PATH . "Controllers" .DS); //当前控制器所在目录 define("MODEL_PATH", PLAT_PATH . "Models" .DS); //当前模型所在目录 define("VIEW_PATH", PLAT_PATH . "Views" . DS); //当前视图所在目录 require FRAMEWORK . 'MysqlUtil.class.php'; require FRAMEWORK . 'BaseModel.class.php'; require FRAMEWORK . 'ModelFactory.class.php'; require FRAMEWORK . 'BaseController.class.php'; require MODEL_PATH . $c . 'Model.class.php'; require CTRL_PATH . $c . 'Controller.class.php'; $controller = $c . 'Controller'; $ctrl = new $controller(); $act = !empty($_GET['a']) ? $_GET['a'] : 'Index'; $action = $act . "Action"; $ctrl ->$action(); //使用可变方法代替以下逻辑判断 ?> 5. 自动加载的实现 在前端控制器中,加载文件时,对于类的文件,如果一次性全部加载,那么可能会造成资源的浪费。采用类的自动加载机制,可以使得在使用类的时候自动加载,以避免内存资源的浪费。比如: //自动加载有利于有效利用资源 function __autoload($class){ $base_class =array("MysqlUtil", "BaseModel", "ModelFactory","BaseController"); if(in_array($class,$base_class)){ require FRAMEWORK .$class . '.class.php'; //加载基础模型类 }else if(substr($class, -5)=== "Model"){ //如果后5个字母是Model,则加载Model类 require MODEL_PATH .$class . '.class.php'; }else if(substr($class,-10) === "Controller"){ //如果后10个字母是Controller,则加载Controller类 require CTRL_PATH .$class . '.class.php'; } } 6. 目录的访问权限控制 在项目的目录中,一般只需要对外开放主页的访问,其他目录的文件不应该对外开放访问。 配置方法: 在apache的conf/extra/httpd-vhosts.conf文件中, 将项目所在的文件夹的权限配置的一段: <Directory"D:/php/test1> Options Indexes FollowSymLinks AllowOverride none Require all granted #DirectoryIndex hello.php </Directory> 修改为: <Directory"D:/php/test1> Options Indexes FollowSymLinks #AllowOverride设置是否分布式权限配置文件(.htaccess) AllowOverride All Require all granted #DirectoryIndex hello.php </Directory> 重启apache,然后在项目文件目录下,那些不允许外界通过浏览器访问的文件目录下,即application目录和Frameword目录下,都放置这个文件.htaccess。文件的内容为:Deny from All 四、 认识ECshop 安装ECShop ECShop是一款B2C独立网站系统,适合快速构建个性化网上商店。下载一套ecshop,然后解压放在和apache、php等安装软件同级的目录下。在apache的httpd-vhost.conf文件中,创建一个站点作为esshop的站点,比如: #ECShop电子商城 <VirtualHost *:80> ServerName www.ecshop.com DocumentRoot "D:\php\amp\ecshop\upload " <Directory"D:\php\amp\ecshop\upload "> Options Indexes FollowSymLinks AllowOverride none Require all granted DirectoryIndex index.php </Directory> </VirtualHost> 然后将域名在hosts文件中做映射,然后,就可以访问这个站点了。 然后,按照网站提示的步骤,检查环境,对于不支持的项目,需要在php软件包的php.ini文件中打开相应的模块,比如不支持GD版本,那么就打开extension=php\_gd2.dll。 然后,下一步,配置系统。填写配置信息。然后,点击安装。然后,打开www. Phpmyadmin.com网页会看到创建ecshop的数据库。 然后,就可以访问esshop的前台和后台页面。 五、 使用mvc框架实现登录界面和功能 首先,设计登录账户表。 create tableadmin_user( id int auto_increment primary key, admin_name varchar(20) unique key, admin_pass varchar(48) not null, login_times int comment '登录次数', last_login_time datetime comment '最后登录时间' ) charset = utf8; 然后,插入几条管理员身份的数据,以便测试。 然后,设计一个登录界面。 注意表单提交的路径设计为:?p=back&c=Admin&a=CheckLogin 编写控制器类: <?php class AdminController extends BaseController{ function LoginAction(){ include VIEW_PATH .'adminlogin.html'; } functionCheckLoginAction(){ $u =$_POST['username']; $p =$_POST['password']; $model =ModelFactory::MF('AdminModel'); $result = $model->CheckAdmin($u, $p); if($result ===true){ echo "登录成功"; }else{ //失败后,提示并跳转到登录界面 $this->GotoUrl("登录失败","?p=back&c=Admin&a=Login", 2); } } } ?> 编写模型类: <?php class AdminModel extends BaseModel{ function CheckAdmin($u,$p){ $sql = "selectcount(*) as c from admin_user where admin_name='$u' andadmin_pass=md5('$p');"; //echo $sql; $result = $this->dao ->GetOneData($sql); //echo $result; if($result == 1){ //登录成功后,应该去修改登录次数和最后登录时间 $sql ="update admin_user set login_times = login_times+1, last_login_time =now() where admin_name = '$u'; "; $this->dao ->exec($sql); return true; }else{ return false; } } } ?>
相关 入门PHPmvc设计模式 历史的开发模式(我也尝试过) ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM r囧r小猫/ 2022年12月10日 11:26/ 0 赞/ 101 阅读
相关 phpMVC 一、 Introduction 1. 项目开发常见流程 需求调研,人员,项目经理;成果,需求说明书。 软件设计书,人员,项目经理;成果,详 ﹏ヽ暗。殇╰゛Y/ 2022年06月04日 09:40/ 0 赞/ 120 阅读
还没有评论,来说两句吧...