博客列表 >PHP基础-composer常用命令/用composer快速搭建MVC框架

PHP基础-composer常用命令/用composer快速搭建MVC框架

岂几岂几
岂几岂几原创
2020年05月24日 02:22:201126浏览

PHP基础-composer常用命令/用composer快速搭建MVC框架

1. composer 项目结构

composer 项目的基本结构可以总结为 1+2 结构.

  1. 一个目录: vendor目录, composer.jsoncomposer.lock指定下载的组件/文件,都下载到该目录中. 目录中还有一个自动加载器autoload.php. 可以使用它加载vendor目录中的所有组件类.

  2. 两个文件: composer.jsoncomposer.lock.

    • composer.json文件: composer的配置文件, 可以用它指定要下载的组件/及其版本(可以不指定具体版本, 只指定版本范围).

    • composer.lock文件: 用于锁定要下载的组件版本, 用于再次下载(如更换/拷贝运行环境)相关组件/时, 下载其指定的具体版本.

    • composer.lock 文件和composer.json 文件, 在不同的命令中使用的优先级不一样.

2. composer 常用命令

2.1 search命令

在 composer 的”应用市场”中搜索组件/项目. 也可以在 packagist.org 中输入组件/项目名搜索, 效果一样. 较少用, 一般都是在市场网站直接搜.

已搜索”jQuery”为例:

  1. $ composer search components/jquery
  2. components/jqueryui jQuery UI is a curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library. Whether you're building highly interactive web applications or you just need to add a date picker to a form control, jQuery UI is the perfect choice.
  3. components/jquery jQuery JavaScript Library
  4. contao-components/jquery-ui jQuery UI integration for Contao Open Source CMS
  5. contao-components/jquery jQuery integration for Contao Open Source CMS
  6. components/jquery-cookie A simple, lightweight jQuery plugin for reading, writing and deleting cookies
  7. components/jquery-migrate APIs and features removed from jQuery core
  8. components/jquery_ns_autogrow Automatically adjust textarea height based on user input. Non-sucking version.
  9. components/jquery-smallipop Tooltip Popup for jQuery
  10. components/jquery-htmlclean HTML Clean plug-in for jQuery
  11. components/jquery-rdfquery jQuery rdfQuery
  12. components/jqueryui-theme-bootstrap Bootstrap Theme for jQuery UI
  13. componentsv/querybasic

2.2 init命令

以交互的方式创建项目结构. 使用命令: composer init. 执行完后, 项目根目录中会生成composer.json文件.

上图的实例, 因为没有在项目初始化时指定项目依赖, 所以生成的composer.json文件中的require配置项值为空.

创建好项目后, 就可以为项目添加依赖组件/资源包了. 有两种添加组件/资源包的方式: install命令和require命令.

2.3 install命令

当开发者以编辑composer.json文件的require属性的方式为当前项目指定依赖组件/资源包时, 使用composer install命令来执行组件/资源包文件下载.

  • 先在composer.json文件的require属性值中添加以来组件/资源包信息, 包括其大版本号.
    以添加 jQuery(大版本号为 v3.5)为例:

    1. "require": {
    2. "components/jquery" : "^3.5",
    3. }
  • 在项目根目录使用终端(这里使用的是 Git Bash)执行composer install命令, composer 检查项目中是否存在composer.lock文件

    1. 若存在, 则下载安装composer.lock文件指定版本的组件/资源包, 并把该版本所属的大版本号及组件/资源包信息写入到composer.json文件中.
    2. 若不存在, 则根据composer.json文件中指定的依赖组件/资源包开始下载指定的大版本号中最新版本的 jQuery 包文件到/vendor目录中; 创建并在composer.lock文件中写入组件/资源包信息和下载的具体版本号.

    终端中:

    命令执行完成后:

2.4 update命令

当开发者需要更新依赖组件/资源包的版本时, 执行composer update命令, 该命令会查找并下载composer.json文件指定的组件/资源包大版本号中的最新版本. 并把最新版本号信息写入composer.lock文件中.

2.5 require命令

require命令用于安装/下载一个组件/资源包. 命令使用的方式有:

  • composer require 机构名/资源名: 把包文件直接下载到当前目录;

  • composer require 机构名/资源名 子目录名: 把包文件下载到指定的当前目录的子目录中.

require命令下载的文件将被存放到vendor目录中; 并在composer.json文件写入下载的组件信息及其大版本号, 在composer.lock文件写入下载的组建信息及其具体版本号. 若composer项目的 1+2 结构中有部分缺失的, require 命令会创建缺失的部分.

require命令, install命令, update命令的区别

  • composer install: 如果存在composer.lock, 就安装这个文件中指定的版本的组件, 若没有composer.lock文件, 则安装composer.json文件中指定的大版本中最新的组件, 然后创建composer.lock文件, 并把安装好的包及其版本等信息写入到composer.lock

  • composer update: 主要是检查composer.json中指定的大版本的组件是否有更新, 若有, 则更新, 并把最新的版本信息写入到composer.lock文件中.

  • composer require 机构名/组件名: 会下载相关组件包到 vendor 目录中, 并把下载的组件及其大版本号写入到composer.json文件中, 再把下载的组件及其详细小版本号写入到composer.lock文件中.

2.6 create-project命令

创建项目命令, 使用composer create-project 项目名 子目录名[可选]命令创建.

一般在 packagist.org 中搜索到的项目的详情页, 都有创建该项目(下载该项目框架)的命令

示例: 把 laravel 项目安装/下载到当前目录的子目录demo_project中: composer create-project laravel/laravel demo_project, 命令输完后回车开始下载.

2.7 更新需自动加载器加载的空间映射

compser dump-autoload: 将composer.json配置中的”autoload”配置项中的空间映射生效. 命令简写: composer dump.

2.8 杂项操作

  • composer -V: 查看 composer 版本信息.

  • composer selfupdate: 升级 composer 到最新版本.

  • composer show: 查看当前composer.json中指定的组件的名称和描述(简单描述).

  • composer show 机构名/组件名: 查看composer.json中指定的某个组件的详细信息.

3. 用composer快速搭建MVC框架

3.1 操作思路

  • 在项目根目录创建入口文件index.php

  • 创建/app/controller, /app/view, /app/model三个目录, 创建对应的文件: PlayerController.php, player/list.php, PlayerModel.php

    • 此时可以先测试入口文件是否能访问到这些类.
  • 把 composer 项目的 1+2 结构, 以及autoload.php文件一起创建出来.

      1. 在根目录创建composer.json文件, 里面写一对花括号{}.
      1. 在根目录执行composer installcomposer update命令, 完成 composer 项目 1+2 结构, 以及autoload.php文件的创建.
      • 注意: 因为composer.json文件中的内容是空的, 所以 1+2 结构中的composer.lock文件并没有创建.
  • composer.json文件中注册需要自动加载的命名空间及其对应的物理路径

      1. composer.json中添加需要自动加载器加载的类的配置, 如, 使我们自己的控制器, 视图和模型都能自动被加载(require).
    1. {
    2. "autoload": {
    3. "//": "psr-4约定: 意思是遵循命名空间和文件路径一致的约定",
    4. "psr-4": {
    5. "//\\": "在这里配置命名空间和目录之间的关系, key=命名空间; value=目录路径",
    6. "controller\\": "app/controller"
    7. }
    8. }
    9. }
      1. 编辑完成后执行composer dump-autoload命令, 让刚刚编辑的内容生效.
  • 修改index.php, 加载vendor/autoload.php自动加载器文件.

    • 此时可以测试是否能跑通
  1. <?php
  2. require "vendor/autoload.php";
  3. use controller\PlayerController;
  4. (new PlayerController)->index();
  • 继续编辑composer.json的”autoload”属性,把视图和模型的空间映射也加进去. 然后测试.

    • tips: 视图目录安排, 模仿 ThinkPHP, /view/controllerName/viewName这样来安排目录结构.
  • packagist.org网站中搜索第三方模型组件(老师推荐:catfan/medoo), 复制安装命令composer require catfan/medoo到项目根目录中执行.

    • 源文件: /vendor/catfan/medoo/src/Medoo.php
    • Medoo有中文文档, 虽然是个人创建的, 但是很详细. 网址: https://medoo.lvtao.net/
  • 再搜一个叫plates(league/plates)的模板引擎, 执行安装: composer require league/plates. 手册地址: platesphp.com.

    • plates的主文件路径: /vendor/league/plates/src/Engin.php.
  • 在根目录中创建一个core文件夹, 里面存放基础类, 如: BaseModel, BaseView. 使BaseModel继承Medoo类, 然后其他通用Model类都继承自BaseModel类即可; BaseView也类似. 记得要把core文件夹加到composer.json的”autoload”自动扫描配置中.

3.2 代码

1. MVC类

  1. 控制器类: app/controller/PlayerController.php
  1. <?php
  2. namespace controller;
  3. use core\BaseView;
  4. use model\PlayerModel;
  5. class PlayerController {
  6. private $playerModel = null;
  7. private $baseView = null;
  8. public function __construct(PlayerModel $playerModel, BaseView $baseView) {
  9. $this->playerModel = $playerModel;
  10. $this->baseView = $baseView;
  11. }
  12. /**
  13. * 球员分页列表
  14. * $p: 当前页码
  15. * $r: 每页最大球员数量
  16. */
  17. public function index($p = 1, $r = 3) {
  18. // echo __METHOD__;
  19. $count = $this->playerModel->playerCount();
  20. // 如果不是数字页码, 则查询第1页
  21. if(!filter_var($p, FILTER_VALIDATE_INT)){
  22. $p = 1;
  23. }
  24. // 如果不是有效的页码, 小于1的页码, 查询第1页; 大于最大页码的, 查询最后一页
  25. $p < 1 ? 1 : ($p > $count ? $count : $p);
  26. if(!filter_var($r, FILTER_VALIDATE_INT)){
  27. $p = 3;
  28. }
  29. $players = $this->playerModel->players($p, $r);
  30. $view = $this->baseView->render('/player/list', ['players' => $players, 'p' => $p, 'r' => $r, 'count' => $count]);
  31. echo $view;
  32. // require(dirname(__DIR__) . '/view/player/list.php');
  33. }
  34. }
  1. 模型类: app/model/PlayerModel.php
  1. <?php
  2. namespace model;
  3. use core\BaseModel;
  4. /**
  5. * 继承自共同的基类Base Model
  6. */
  7. class PlayerModel extends BaseModel {
  8. // 构造方法调用基类的构造方法, 用来初始化Medoo.
  9. public function __construct()
  10. {
  11. // 调用父类构造方法, 不传参数, 用默认的
  12. parent::__construct();
  13. }
  14. // 返回所有球员数量
  15. public function playerCount() {
  16. return 1234;
  17. // return $this->count('player');
  18. }
  19. // 查询球员分页数据
  20. public function players($page = 1, $pageSize = 3) {
  21. $start = ($page - 1) * $pageSize;
  22. $players = $this->select('player', '*', ['LIMIT' => [$start, $pageSize]]);
  23. return $players;
  24. }
  25. }
  1. 分页类: app/view/common/Pagination.php, 复用之前写好的分页类.
  1. <?php
  2. namespace view\common;
  3. class Pagination
  4. {
  5. // 第一页
  6. private $start = 1;
  7. // 一页可容纳的记录数
  8. private $pageSize = 3;
  9. // 显示的页码个数
  10. private $pageNumSize = 5;
  11. // 显示的页码列表
  12. private $pageNumList = [];
  13. // 总页数
  14. private $pageCount = 0;
  15. // 页码左右偏移量
  16. private $pageNumOffset = 0;
  17. // 当前页码
  18. private $currentPage = 1;
  19. // 记录总数
  20. private $rowCount = 0;
  21. public function __construct(int $rowCount, int $currentPage = 1, int $pageSize = 3, int $pageNumSize = 5)
  22. {
  23. // 初始化各种属性
  24. $this->rowCount = $rowCount;
  25. $this->pageSize = $pageSize;
  26. $this->pageNumSize = $pageNumSize;
  27. $this->pageNumOffset = ($pageNumSize - 1) / 2;
  28. $this->pageCount = ceil(floatval($rowCount) / $pageSize);
  29. /* 当传入的当前页码效于最小页码时,初始化为1;大于最大页码时,初始化为最大页码 */
  30. $this->currentPage = $currentPage < 1 ? 1 : ($currentPage > $this->pageCount ? $this->pageCount : $currentPage);
  31. $this->getPageNumList();
  32. }
  33. /**
  34. * 获取要显示的页码列表
  35. */
  36. private function getPageNumList()
  37. {
  38. // 如果要显示的页码数量>=总页码数量,则显示所有页码。
  39. if ($this->pageCount <= $this->pageNumSize) {
  40. $this->pageNumList = range(1, $this->pageCount, 1);
  41. return;
  42. }
  43. // 起始页码,取“当前页码-页码偏移量”和起始页码的最大值。
  44. $pageNumStart = ($this->currentPage - $this->pageNumOffset) < $this->start ? $this->start : ($this->currentPage - $this->pageNumOffset);
  45. // 结束页码,取“当前页码+页码偏移量”和总页码数的最小值。
  46. $pageNumEnd = ($pageNumStart + $this->pageNumSize - 1) > $this->pageCount ? $this->pageCount : ($pageNumStart + $this->pageNumSize - 1);
  47. // 若结束页码等于总页码,则再计算一次起始页码(避免当前页到结束页码的差值小于页码偏移量的情况)
  48. if ($pageNumEnd === $this->pageCount) {
  49. // 起始页码,取“最大页码-要显示的页码数量+1”和起始页码的最大值。
  50. $pageNumStart = ($this->pageCount - $this->pageNumSize + 1) < $this->start ? $this->start : ($this->pageCount - $this->pageNumSize + 1);
  51. }
  52. // 生成要显示的页码数组
  53. $this->pageNumList = range($pageNumStart, $pageNumEnd, 1);
  54. }
  55. // 拼接字符串,形成分页HTML代码字符串。
  56. public function getPagination()
  57. {
  58. $pageHtml = '<div class="pagination">';
  59. // 首页
  60. $tmpHtml = $this->currentPage === 1 ? '<span class="pageNum disabled">首页</span>'
  61. : sprintf('<a class="pageNum" href="%s?p=1&r=%d">首页</a>', $_SERVER['PHP_SELF'], $this->pageSize);
  62. $pageHtml .= $tmpHtml;
  63. // 上一页
  64. $tmpHtml = $this->currentPage === 1 ? '<span class="pageNum disabled">上一页</span>'
  65. : sprintf('<a class="pageNum" href="%s?p=%d&r=%d">上一页</a>', $_SERVER['PHP_SELF'], $this->currentPage - 1, $this->pageSize);
  66. $pageHtml .= $tmpHtml;
  67. // 间隔符
  68. $tmpHtml = $this->pageNumList[0] >= 2 ? '...' : '';
  69. $pageHtml .= $tmpHtml;
  70. // 页码
  71. foreach ($this->pageNumList as $pageNum) {
  72. $tmpHtml = $pageNum == $this->currentPage ? sprintf('<span class="active">%d</span>', $pageNum)
  73. : sprintf('<a class="pageNum {$pageNum}" href="%s?p=%d&r=%d">%d</a>', $_SERVER['PHP_SELF'], $pageNum, $this->pageSize, $pageNum);
  74. $pageHtml .= $tmpHtml;
  75. }
  76. // 间隔符
  77. $tmpHtml = $this->pageNumList[array_key_last($this->pageNumList)] + 1 <= $this->pageCount ? '...' : '';
  78. $pageHtml .= $tmpHtml;
  79. // 下一页
  80. $tmpHtml = $this->currentPage >= $this->pageCount ? '<span class="pageNum disabled">下一页</span>'
  81. : sprintf('<a class="pageNum" href="%s?p=%d&r=%d">下一页</a>', $_SERVER['PHP_SELF'], $this->currentPage + 1, $this->pageSize);
  82. $pageHtml .= $tmpHtml;
  83. // 末页
  84. $tmpHtml = $this->currentPage >= $this->pageCount ? '<span class="pageNum disabled">末页</span>'
  85. : sprintf('<a class="pageNum" href="%s?p=%d&r=%d">末页</a>', $_SERVER['PHP_SELF'], $this->pageCount, $this->pageSize);
  86. $pageHtml .= $tmpHtml;
  87. // 总页码
  88. $pageHtml .= "<span>共{$this->pageCount}页</span>";
  89. // 页码跳转表单
  90. $tmpHtml = "<form action='{$_SERVER['PHP_SELF']}' method='get'> <input type='text' name='p'><input type='hidden' name='r' value='{$this->pageSize}'><button type='submit'>跳转</button></form>";
  91. $pageHtml .= $tmpHtml;
  92. $pageHtml .= '</div>';
  93. return $pageHtml;
  94. }
  95. // 直接向浏览器输出分页信息
  96. public function echoPagination()
  97. {
  98. echo $this->getPagination();
  99. }
  100. }
  1. 视图模板文件: app/view/player/index.php
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>球员列表</title>
  7. <style>
  8. @import '/player/style/page_style.css';
  9. @import '/player/style/page_style.css';
  10. @import '/player/style/list.css';
  11. </style>
  12. </head>
  13. <?php
  14. // require('/pagination.php');
  15. ?>
  16. <body>
  17. <table cellspacing="0" align="center">
  18. <caption>球员列表</caption>
  19. <thead>
  20. <tr>
  21. <th>ID</th>
  22. <th>姓名</th>
  23. <th>球队</th>
  24. <th>身高(cm)</th>
  25. <th>体重(kg)</th>
  26. <th>位置</th>
  27. <th>创建时间</th>
  28. <th>修改时间</th>
  29. <th>操作</th>
  30. </tr>
  31. </thead>
  32. <tbody>
  33. <?php if (!empty($players) && count($players) > 0) : ?>
  34. <?php foreach ($players as $player) : ?>
  35. <tr>
  36. <td><?php echo($player['id']); ?></td>
  37. <td><?php echo($player['name']); ?></td>
  38. <td><?php echo($player['team']); ?></td>
  39. <td><?php echo($player['height']); ?></td>
  40. <td><?php echo($player['weight']); ?></td>
  41. <td><?php echo($player['position']); ?></td>
  42. <td><?php echo(date('Y-m-d H:i:s', $player['create_time'])); ?></td>
  43. <td><?php echo(date('Y-m-d H:i:s', $player['update_time'])); ?></td>
  44. <td>
  45. <a href="/player/index.php/player/edit?id=<?php echo($player['id']); ?>&pos=<?php echo(urlencode($_SERVER['QUERY_STRING'])); ?>">修改</a>
  46. <a href="/player/index.php/player/del?id=<?php echo($player['id']); ?>&name=<?php echo($player['name']); ?>&pos=<?php echo(urlencode($_SERVER['QUERY_STRING'])); ?>">删除</a>
  47. </td>
  48. </tr>
  49. <?php endforeach; ?>
  50. <?php else : ?>
  51. <tr>
  52. <td colspan="9">啥也没查到...</td>
  53. </tr>
  54. <?php endif ?>
  55. </tbody>
  56. </table>
  57. <?php (new \view\common\Pagination($count, $p, $r, 5))->echoPagination();
  58. ?>
  59. <?php //echo (new Pagination($count, $currentPage, 3, 5));
  60. ?>
  61. </body>
  62. </html>

2. 核心类

  1. 模型基类: core/BaseModel.php
  1. <?php
  2. namespace core;
  3. use Medoo\Medoo;
  4. /**
  5. * 所有模型类的基类, 继承自第三方模型组件Medoo.
  6. */
  7. class BaseModel extends Medoo {
  8. // 数据库配置项
  9. private $options = [
  10. // 必须配置项
  11. 'database_type' => 'mysql',
  12. 'database_name' => 'phpedu',
  13. 'server' => 'localhost',
  14. 'username' => 'root',
  15. 'password' => 'root',
  16. 'charset' => 'utf8',
  17. // 可选参数
  18. // 'port' => 3306,
  19. // 可选,定义表的前缀
  20. /* 'PREFIX' => 'PREFIX_', */
  21. // 连接参数扩展, 更多参考 http://www.php.net/manual/en/pdo.setattribute.php
  22. /* 'option' => [
  23. PDO::ATTR_CASE => PDO::CASE_NATURAL
  24. ] */
  25. ];
  26. /**
  27. * 构造方法, 调用Medoo的构造方法并传参, 初始化Medoo.
  28. */
  29. public function __construct(array $options = [])
  30. {
  31. // 合并数据库连接属性
  32. array_merge($options, $this->options);
  33. // 调用Medoo的构造方法
  34. parent::__construct($this->options);
  35. }
  36. }
  1. 模板引擎马甲类: core/BaseView.php
  1. <?php
  2. namespace core;
  3. use League\Plates\Engine;
  4. /**
  5. * 视图引擎, 负责向视图模板中渲染数据
  6. */
  7. class BaseView extends Engine
  8. {
  9. public function __construct($templateBasePath = "")
  10. {
  11. // 调用父类(Plates)的构造方法, 传入视图模板的根路径
  12. $this->templates = parent::__construct($templateBasePath);
  13. }
  14. }
  1. 路由解析类: core/Route.php
  1. <?php
  2. namespace core;
  3. class Route
  4. {
  5. public static $config = [
  6. 'controller_suffix' => 'Controller',
  7. 'action_suffix' => '',
  8. 'view_suffix' => 'View',
  9. 'model_suffix' => 'Model',
  10. ];
  11. // public function analysis1() {
  12. // dumpbr(require((dirname(__DIR__)) . '/config.php'));
  13. // //dumpbr($config);
  14. // }
  15. /**
  16. * 解析路由,约定:若存在路径信息,则以路径信息的前两位作为controller和action,剩下的路径信息参数跟查询参数合并作为action的
  17. * 入参;若路径信息小于2,则action由查询参数的第一个键值对指定,剩下的查询参数作为action的入参;
  18. */
  19. public static function analysis()
  20. {
  21. // 加载配置文件
  22. // $config = require((dirname(__DIR__)) . '/config.php');
  23. // array_merge(static::$config, $config);
  24. // 处理路径信息
  25. $pathInfoParam = empty($_SERVER['PATH_INFO']) ? [] : static::analysisPathInfo($_SERVER['PATH_INFO']);
  26. // 处理查询字符串
  27. $queryStrParam = empty($_SERVER['QUERY_STRING']) ? [] : static::analysisQueryStr($_SERVER['QUERY_STRING']);
  28. $reqInfo = $pathInfoParam;
  29. // 路径信息和查询字符串解析结果中都没有控制器信息,则不再继续
  30. if (!isset($reqInfo['controller']) && !isset($queryStrParam['c'])) {
  31. echobr('无法处理的URL,未指定控制器');
  32. die;
  33. }
  34. // 否则,优先考虑使用路径信息中的控制器;最后再考虑使用请求参数中指定的控制器(参数名为c)
  35. if (isset($reqInfo['controller'])) {
  36. $reqInfo['controller'] = ucfirst($reqInfo['controller']);
  37. unset($queryStrParam['c']);
  38. } else {
  39. $reqInfo['controller'] = ucfirst($queryStrParam['c']) . static::$config['controller_suffix'];
  40. unset($queryStrParam['c']);
  41. }
  42. // 路径信息和查询字符串解析结果都没有方法信息,则不再继续
  43. if (!isset($reqInfo['action']) && !isset($queryStrParam['a'])) {
  44. // echobr('无法处理的URL,未指定action');die;
  45. $reqInfo['action'] = 'index';
  46. } else {
  47. // 否则,优先考虑使用路径信息中的方法,最后在考虑使用请求参数中指定的方法(参数名为a)
  48. if (isset($reqInfo['action'])) {
  49. unset($queryStrParam['a']);
  50. } else {
  51. $reqInfo['action'] = lcfirst($queryStrParam['a']) . static::$config['action_suffix'];
  52. unset($queryStrParam['a']);
  53. }
  54. }
  55. // printfpre($queryStrParam);
  56. if (count($queryStrParam) > 0) {
  57. $reqInfo['param'] = empty($reqInfo['param']) ? $queryStrParam : array_merge($reqInfo['param'], $queryStrParam);
  58. }
  59. return $reqInfo;
  60. }
  61. private static function analysisPathInfo($pathInfo): array
  62. {
  63. // echobr($pathInfo);
  64. $pathInfoParam = ['param' => []];
  65. $pathInfoArr = explode('/', trim($pathInfo, '/'));
  66. $paramCount = count($pathInfoArr);
  67. // 解析出来,有值,那么弹出第一个作为控制器
  68. if ($paramCount > 0) {
  69. $pathInfoParam['controller'] = lcfirst(array_shift($pathInfoArr)) . static::$config['controller_suffix'];
  70. }
  71. // 值数量超过1个,则再弹出第二个作为方法
  72. if ($paramCount > 1) {
  73. $pathInfoParam['action'] = lcfirst(array_shift($pathInfoArr)) . static::$config['action_suffix'];
  74. }
  75. // 值数量超过3个,则剩下的拼成参数键值对
  76. if ($paramCount > 2) {
  77. for ($index = 0; $index < count($pathInfoArr); $index += 2) {
  78. if (isset($pathInfoArr[$index + 1])) {
  79. $param[$pathInfoArr[$index]] = $pathInfoArr[$index + 1];
  80. }
  81. }
  82. $pathInfoParam['param'] = $param;
  83. }
  84. return $pathInfoParam;
  85. }
  86. private static function analysisQueryStr($queryStr)
  87. {
  88. // echobr($queryStr);
  89. $queryStrParam = [];
  90. parse_str($queryStr, $queryStrParam);
  91. return $queryStrParam;
  92. }
  93. }
  1. 入口文件兼框架运行文件: index.php
  1. <?php
  2. // 加载自动加载其文件
  3. use core\BaseView;
  4. use core\Route;
  5. use controller\PlayerController;
  6. use model\PlayerModel;
  7. require "vendor/autoload.php";
  8. // 解析
  9. $reqInfo = Route::analysis();
  10. // 控制器(以类名作为变量值来创建实例, 需要加上命名空间)
  11. $controller = 'controller\\' . $reqInfo['controller'];
  12. // 加载同名模型
  13. $model = rtrim($controller, 'Controller') . 'Model';
  14. // 方法
  15. $action = $reqInfo['action'];
  16. // 参数
  17. $param = $reqInfo['param'];
  18. // 创建控制器实例
  19. $obj = new $controller(new PlayerModel(), new BaseView('app/view'));
  20. // 执行$obj类的$action方法, 参数是$param
  21. call_user_func_array([$obj, $action], $param);

运行结果:

4. 学习心得

composer像是一个组件/项目市场. 可以借助composer快速的搭建基于框架的项目, 如Laravel项目, TP6项目等; 同样可以借助它加载自己项目中需要的组件, 它还能自动处理组件的依赖问题. 使开发者把重心放到项目的业务逻辑, 而不是项目的搭建.

用composer搭建MVC框架的过程, 就是往自己的项目中加入各种组件的过程.

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议