首頁 >頭條 >圖解ThinkPHP5框架(二):應用運作流程與生命週期

圖解ThinkPHP5框架(二):應用運作流程與生命週期

天蓬老师
天蓬老师原創
2017-05-18 11:45:055655瀏覽

基於最新ThinkPHP5.0.8制定,原搞是xmind心智圖製作,如果覺得圖片看不太清楚,可以下載xmind源文件,用xmind軟體打開。

另外,心智圖中使用的源碼,全部在導圖備註中,只有下載了xmind源文件,才可以看到。

想自學ThinkPHP5開發的同學,一定不要錯過這個福利。正在從事ThinkPHP5專案開發的程式設計師,可以收藏,以備查詢

一、心智圖:

圖解ThinkPHP5框架(二):應用運作流程與生命週期

二、框架架構整體描述文字版:

框架架構

1 架構總覽

1.1 基於MVC設計模式

1.1.1 Model:模型

#1.1.2 View:檢視

1.1.3 Controller:控制器

#1.2 基於MVC的URL存取路由

1.2.1 http://網域/入口檔案/模組/控制器/操作/參數/值...

##1.3 入口檔案

1.3.1 最常見:index .php

1.3.2 可以為模組綁定其它入口文件,如後:admin.php



1.4 應用App

#1.4.1管理框架生命週期的物件:\think\App類別

1.4.2 由入口檔案呼叫並執行

1.4.3 具有相同的APP_PATH應用程式目錄的,視為同一個應用程式

1.4.4 一個應用程式可以有多個入口,例如前台為index.php,後台為admin.php

1.4.5 應用有自己獨立的設定檔與公用函數檔


1.5 模組Module

1.5.1 一個應用通常是由多個模組組成

1.5.2 一個模組通常是應用程式目錄中一個子目錄: app/index/

1.5.3 模組通常由多個控制器類別檔案組成,可以將這些類別以目錄方式管理

1.5.4 簡單應用,可不建立模組目錄,使用單模組架構,在應用程式設定中關閉多模組支援: 'app_multi_module'=>false

1.5.5 模組也可有自己獨立的設定檔和公用檔案、類別庫檔


1.6 控制器Controller

1.6.1 控制器負責回應使用者的請求,呼叫模型處理,選擇視圖輸出,不應介入業務處理

1.6.2 每個控制器其實就是一個類別檔案: Index.php

1.6.3 5.0開始,控制器不用繼承任何父類別就可以正常運作

<?php
namespace app\index\controller; class Index {
public function index()
   {
      return &#39;hello,thinkphp!&#39;;
   } 
}


1.7 操作Action

1.7.1 一個控制器包含多個操作方法,操作方法是URL存取的最小單元

1.7.2 操作方法如果要求參數,則必須傳入:$_GET或$_POST

<?php
namespace app\index\controller; class Index {
public function index() {
return &#39;index&#39;; } public function hello($name) {
return &#39;Hello,&#39;.$name; }
}

1.8 模型Model

1.8.1 完成實際的業務邏輯和資料封裝,並傳回與格式無關的資料

1.8.2 模型支援分層設計:邏輯層/服務層/事件層

1.8.3 模型類別不一定要存取資料庫,只有CURD操作時才連接,是真正的惰性連接

1.9 視圖View

1.9.1 控制器呼叫模型類別傳回的資料,要通視圖類別包裝成不同的格式傳回。

1.9.2 可依需求決定是直接渲染還是以範本輸出

1.9.3 視圖有一系列的範本檔案與控制器中的操作對應

1.9. 4 可以在操作方法中動態設定範本目錄


1.10 命名空間NameSpace

1.10.1 命名空間主要用在框架的類別庫檔案中

1.10.2 命名空間必須符合PSR-4的自動載入機制:空間名稱與類別的路徑對應


2 生命週期

2.1 入口檔案index.php

2.1.1 位置: public/index.php

2.1.2 入口檔案中通常只定義常數和載入框架引導檔案

<?php
// 应用入口文件
// 定义项目路径 
define(&#39;APP_PATH&#39;, __DIR__ . &#39;/../application/&#39;); 
// 加载框架引导文件 
require __DIR__ . &#39;/../thinkphp/start.php&#39;;

2.2 引導檔案:start.php


<?php
namespace think;
// ThinkPHP 引导文件
// 加载基础文件
require __DIR__ . &#39;/base.php&#39;;
// 执行应用
App::run()->send();


#2.2.1 載入base.php

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

define(&#39;THINK_VERSION&#39;, &#39;5.0.5&#39;);
define(&#39;THINK_START_TIME&#39;, microtime(true));
define(&#39;THINK_START_MEM&#39;, memory_get_usage());
define(&#39;EXT&#39;, &#39;.php&#39;);
define(&#39;DS&#39;, DIRECTORY_SEPARATOR);
defined(&#39;THINK_PATH&#39;) or define(&#39;THINK_PATH&#39;, __DIR__ . DS);
define(&#39;LIB_PATH&#39;, THINK_PATH . &#39;library&#39; . DS);
define(&#39;CORE_PATH&#39;, LIB_PATH . &#39;think&#39; . DS);
define(&#39;TRAIT_PATH&#39;, LIB_PATH . &#39;traits&#39; . DS);
defined(&#39;APP_PATH&#39;) or define(&#39;APP_PATH&#39;, dirname($_SERVER[&#39;SCRIPT_FILENAME&#39;]) . DS);
defined(&#39;ROOT_PATH&#39;) or define(&#39;ROOT_PATH&#39;, dirname(realpath(APP_PATH)) . DS);
defined(&#39;EXTEND_PATH&#39;) or define(&#39;EXTEND_PATH&#39;, ROOT_PATH . &#39;extend&#39; . DS);
defined(&#39;VENDOR_PATH&#39;) or define(&#39;VENDOR_PATH&#39;, ROOT_PATH . &#39;vendor&#39; . DS);
defined(&#39;RUNTIME_PATH&#39;) or define(&#39;RUNTIME_PATH&#39;, ROOT_PATH . &#39;runtime&#39; . DS);
defined(&#39;LOG_PATH&#39;) or define(&#39;LOG_PATH&#39;, RUNTIME_PATH . &#39;log&#39; . DS);
defined(&#39;CACHE_PATH&#39;) or define(&#39;CACHE_PATH&#39;, RUNTIME_PATH . &#39;cache&#39; . DS);
defined(&#39;TEMP_PATH&#39;) or define(&#39;TEMP_PATH&#39;, RUNTIME_PATH . &#39;temp&#39; . DS);
defined(&#39;CONF_PATH&#39;) or define(&#39;CONF_PATH&#39;, APP_PATH); // 配置文件目录
defined(&#39;CONF_EXT&#39;) or define(&#39;CONF_EXT&#39;, EXT); // 配置文件后缀
defined(&#39;ENV_PREFIX&#39;) or define(&#39;ENV_PREFIX&#39;, &#39;PHP_&#39;); // 环境变量的配置前缀

// 环境常量
define(&#39;IS_CLI&#39;, PHP_SAPI == &#39;cli&#39; ? true : false);
define(&#39;IS_WIN&#39;, strpos(PHP_OS, &#39;WIN&#39;) !== false);

// 载入Loader类
require CORE_PATH . &#39;Loader.php&#39;;

// 加载环境变量配置文件
if (is_file(ROOT_PATH . &#39;.env&#39;)) {
    $env = parse_ini_file(ROOT_PATH . &#39;.env&#39;, true);
    foreach ($env as $key => $val) {
        $name = ENV_PREFIX . strtoupper($key);
        if (is_array($val)) {
            foreach ($val as $k => $v) {
                $item = $name . &#39;_&#39; . strtoupper($k);
                putenv("$item=$v");
            }
        } else {
            putenv("$name=$val");
        }
    }
}

// 注册自动加载
\think\Loader::register();

// 注册错误和异常处理机制
\think\Error::register();

// 加载惯例配置文件
\think\Config::set(include THINK_PATH . &#39;convention&#39; . EXT);


2.2.1.1 載入系統常數定義

2.2.1.2 載入環境變數定義檔

2.2.1.3 註冊自動載入機制

#2.2.1.3.1 呼叫Loader::registerister (),載入符合規範類別庫及第三方類別庫

2.2.1.3.2 自動載入的內容

2.2.1.3.2.1 註冊系統的自動載入方法\think\Loader: :autoload

2.2.1.3.2.2 註冊系統命名空間定義

2.2.1.3.2.3 載入類別庫映射檔案(如果存在)

2.2.1.3.2.4 如果存在Composer 安裝,則註冊Composer 自動載入

2.2.1.3.2.5 註冊extend 擴充目錄


2.2.1.3.3 類別庫自動載入的偵測順序

2.2.1.3.3.1 是否定義類別庫映射

2.2.1.3.3.2 PSR-4 自動載入偵測

2.2.1.3.3.3 PSR-0 自動載入偵測

2.2.1.4 註冊錯誤與例外處理機制

2.2.1.4.1 執行Error::register() 註冊,由三部分組成

2.2.1.4. 1.1 應用關閉方法: think\Error::appShutdown

2.2.1.4.1.2 錯誤處理方法: think\Error::appError

2.2.1.4.1.3 例外處理方法: think\Error ::appException

2.2.1.5 載入慣例設定檔

2.2.1.5.1 \think\Config::set(include THINK_PATH . 'convention' . EXT);

#2.2.2 執行應用:App::run()->send();

2.2.2.1 App::run()輸入Request物件,傳回Response物件

#

2.2.2.2 Response::send():发送数据到客户端

2.3 应用初始化:App::initCommon()和init()方法

/**
     * 初始化应用
     */
    public static function initCommon()
    {
        if (empty(self::$init)) {
            // 初始化应用
            $config       = self::init();
            self::$suffix = $config[&#39;class_suffix&#39;];

            // 应用调试模式
            self::$debug = Env::get(&#39;app_debug&#39;, Config::get(&#39;app_debug&#39;));
            if (!self::$debug) {
                ini_set(&#39;display_errors&#39;, &#39;Off&#39;);
            } elseif (!IS_CLI) {
                //重新申请一块比较大的buffer
                if (ob_get_level() > 0) {
                    $output = ob_get_clean();
                }
                ob_start();
                if (!empty($output)) {
                    echo $output;
                }
            }

            // 注册应用命名空间
            self::$namespace = $config[&#39;app_namespace&#39;];
            Loader::addNamespace($config[&#39;app_namespace&#39;], APP_PATH);
            if (!empty($config[&#39;root_namespace&#39;])) {
                Loader::addNamespace($config[&#39;root_namespace&#39;]);
            }

            // 加载额外文件
            if (!empty($config[&#39;extra_file_list&#39;])) {
                foreach ($config[&#39;extra_file_list&#39;] as $file) {
                    $file = strpos($file, &#39;.&#39;) ? $file : APP_PATH . $file . EXT;
                    if (is_file($file) && !isset(self::$file[$file])) {
                        include $file;
                        self::$file[$file] = true;
                    }
                }
            }

            // 设置系统时区
            date_default_timezone_set($config[&#39;default_timezone&#39;]);

            // 监听app_init
            Hook::listen(&#39;app_init&#39;);

            self::$init = true;
        }
        return Config::get();
    }

    /**
     * 初始化应用或模块
     * @access public
     * @param string $module 模块名
     * @return array
     */
    private static function init($module = &#39;&#39;)
    {
        // 定位模块目录
        $module = $module ? $module . DS : &#39;&#39;;

        // 加载初始化文件
        if (is_file(APP_PATH . $module . &#39;init&#39; . EXT)) {
            include APP_PATH . $module . &#39;init&#39; . EXT;
        } elseif (is_file(RUNTIME_PATH . $module . &#39;init&#39; . EXT)) {
            include RUNTIME_PATH . $module . &#39;init&#39; . EXT;
        } else {
            $path = APP_PATH . $module;
            // 加载模块配置
            $config = Config::load(CONF_PATH . $module . &#39;config&#39; . CONF_EXT);
            // 读取数据库配置文件
            $filename = CONF_PATH . $module . &#39;database&#39; . CONF_EXT;
            Config::load($filename, &#39;database&#39;);
            // 读取扩展配置文件
            if (is_dir(CONF_PATH . $module . &#39;extra&#39;)) {
                $dir   = CONF_PATH . $module . &#39;extra&#39;;
                $files = scandir($dir);
                foreach ($files as $file) {
                    if (strpos($file, CONF_EXT)) {
                        $filename = $dir . DS . $file;
                        Config::load($filename, pathinfo($file, PATHINFO_FILENAME));
                    }
                }
            }

            // 加载应用状态配置
            if ($config[&#39;app_status&#39;]) {
                $config = Config::load(CONF_PATH . $module . $config[&#39;app_status&#39;] . CONF_EXT);
            }

            // 加载行为扩展文件
            if (is_file(CONF_PATH . $module . &#39;tags&#39; . EXT)) {
                Hook::import(include CONF_PATH . $module . &#39;tags&#39; . EXT);
            }

            // 加载公共文件
            if (is_file($path . &#39;common&#39; . EXT)) {
                include $path . &#39;common&#39; . EXT;
            }

            // 加载当前模块语言包
            if ($module) {
                Lang::load($path . &#39;lang&#39; . DS . Request::instance()->langset() . EXT);
            }
        }
        return Config::get();
    }

 

2.3.1.1 定位模块目录

2.3.1.2 加载初始化文件

2.3.1.2.1 加载模块配置

2.3.1.2.2 读取数据库配置文件

2.3.1.2.3 读取扩展配置文件

2.3.1.2.4 加载应用状态配置

2.3.1.2.5 加载行为扩展文件

2.3.1.2.6 加载公共文件

2.3.1.2.7 加载当前模块语言包

2.3.2 检测应用调试模式:self::$debug = Env::get('app_debug', Config::get('app_debug'));

2.3.3 注册应用命名空间:self::$namespace = $config['app_namespace'];

2.3.4 加载额外文件:'extra_file_list'

2.3.5 设置系统时区:date_default_timezone_set($config['default_timezone']);

2.3.6 监听app_init:  Hook::listen('app_init');

2.3.7 返回所有配置项:return Config::get();

2.4 URL访问检测:

2.4.1 PATH_INFO:http://serverName/index.php/index/index/hello/val/value

2.4.2 兼容方式:http://serverName/index.php?s=/index/index/hello&val=value

2.5 路由检测:App::routeCheck($request, array $config)与设置路由机制:route($route, $must = false)

/**
     * URL路由检测(根据PATH_INFO)
     * @access public
     * @param  \think\Request $request
     * @param  array          $config
     * @return array
     * @throws \think\Exception
     */
    public static function routeCheck($request, array $config)
    {
        $path   = $request->path();
        $depr   = $config[&#39;pathinfo_depr&#39;];
        $result = false;
        // 路由检测
        $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config[&#39;url_route_on&#39;];
        if ($check) {
            // 开启路由
            if (is_file(RUNTIME_PATH . &#39;route.php&#39;)) {
                // 读取路由缓存
                $rules = include RUNTIME_PATH . &#39;route.php&#39;;
                if (is_array($rules)) {
                    Route::rules($rules);
                }
            } else {
                $files = $config[&#39;route_config_file&#39;];
                foreach ($files as $file) {
                    if (is_file(CONF_PATH . $file . CONF_EXT)) {
                        // 导入路由配置
                        $rules = include CONF_PATH . $file . CONF_EXT;
                        if (is_array($rules)) {
                            Route::import($rules);
                        }
                    }
                }
            }

            // 路由检测(根据路由定义返回不同的URL调度)
            $result = Route::check($request, $path, $depr, $config[&#39;url_domain_deploy&#39;]);
            $must   = !is_null(self::$routeMust) ? self::$routeMust : $config[&#39;url_route_must&#39;];
            if ($must && false === $result) {
                // 路由无效
                throw new RouteNotFoundException();
            }
        }
        if (false === $result) {
            // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
            $result = Route::parseUrl($path, $depr, $config[&#39;controller_auto_search&#39;]);
        }
        return $result;
    }

    /**
     * 设置应用的路由检测机制
     * @access public
     * @param  bool $route 是否需要检测路由
     * @param  bool $must  是否强制检测路由
     * @return void
     */
    public static function route($route, $must = false)
    {
        self::$routeCheck = $route;
        self::$routeMust  = $must;
    }
}

2.5.1 路由到模块/控制器/操作

2.5.2 路由到外部重定向地址;

2.5.3 路由到控制器方法;

2.5.4 路由到闭包函数;

2.5.5 路由到类的方法;

2.6 请求分发与响应输出:Response::send()

2.6.1 控制器的所有操作方法都是 return 返回而不是直接输出

2.6.2 自动转换成 default_return_type 参数配置的格式

2.7 应用结束,写入日志

2.7.1 系统的日志包括用户调试输出的和系统自动生成的日志,统一会在应用结束的时候进行写入操作

2.7.2 日志的写入操作受日志初始化的影响

3 入口文件

3.1 采用单一入口模式进行项目部署(并非唯一入口)

3.2 不同应用对应不同入口,但入口文件内容和功能基本一致

3.3 入口文件内容

<?php
// 定义应用目录 
define(&#39;APP_PATH&#39;, __DIR__ . &#39;/../application/&#39;); 
// 加载框架引导文件 
require __DIR__ . &#39;/../thinkphp/start.php&#39;;

3.3.1 定义应用目录:define('APP_PATH', __DIR__ . '/../application/');

3.3.2 定义系统常量:define('CONF_PATH', __DIR__ . '/../config/');

3.3.3 加载框架引导文件: require __DIR__ . '/../thinkphp/start.php';

3.4 入口文件位于public目录下

3.4.1 这是为了让应用部署更安全

3.4.2 public必须是Web可访问目录

3.4.3 其实文件或目录应该放在非Web访问目录下面


4 URL访问

4.1 URL设计

4.1.1 PATH_INFO:http://index.php/模块/控制器/操作/[参数名/参数值...]

4.1.2 兼容模型:http://index.php?s=/模块/控制器/操作/[参数名/参数值...]

4.1.3 不支持普通URL模式,但传参可以:http://index.php/module/controller/action?id=10

4.1.4 URL默认不区分大小写,都会转为小写的,控制器部分自动转为驼峰法处理

4.2 隐藏入口文件

4.2.1 隐藏入口文件,可优化URL,网站更安全

4.2.2 在入口文件同级:public/.htaccess

4.2.3 Apache配置文件httpd.conf加载:mod_rewrite.so模块,支持URL重写

4.2.4 httpd.conf:AllowOverride None 将None设置为 All


5 模块设计

5.1 默认为多模块,支持单一模块设计 

5.2 模块命名空间均为app为根空间

5.3 模块可以看作是类库的集合:控制器类,模型类

5.4 模块类库:app\模块名\类库\类名

5.5 入口文件中隐藏模块和控制器


由于默认是采用多模块的支持,所以多个模块的情况下必须在URL地址中标识当前模块,如果只有一个模块的 话,可以进行模块绑定,方法是应用的入口文件中添加如下代码:

// 绑定当前访问到index模块 define('BIND_MODULE','index');

绑定后,我们的URL访问地址则变成:

http://serverName/index.php/控制器/操作/[参数名/参数值...]

访问的模块是 index 模块。 如果你的应用比较简单,模块和控制器都只有一个,那么可以在应用公共文件中绑定模块和控制器,如下:

// 绑定当前访问到index模块的index控制器 define('BIND_MODULE','index/index');

设置后,我们的URL访问地址则变成:

http://serverName/index.php/操作/[参数名/参数值...]

访问的模块是 index 模块,控制器是 Index 控制器。

5.5.1 绑定当前访问的模块:define('BIND_MODULE','index');

5.5.2 绑定当前访问的模块和控制器:define('BIND_MODULE','userlogin/getname');

5.6 单一模块设计:'app_multi_module'=>false,

5.6.1 可以把应用目录当作模块目录

5.6.2 模块中的控制器命名空间也要调整

6 命名空间

6.1 命名空间的路径与类库文件的目录一致,可以实现类的自动加载(惰性加载)

6.2 根命名空间:类库包

6.2.1 think:系统核心类库 (think/library/think)

6.2.2 traits:系统trait类库(think/library/traits)

6.2.3 app:应用类库(application)

6.2.4 自定义根命名空间

6.2.4.1 默认加载EXTEND_APTH目录中的类库,目录名为根

我们只需要把自己的类库包目录放入 EXTEND_PATH 目录(默认为 extend ,可配置),就可以自动注册对 应的命名空间,例如:

我们在 extend 目录下面新增一个 my 目录,然后定义一个 \my\Hello 类( 类文件位于 extend/my/Hello.php )如下:

<?php
namespace my;
class Hello
{
  public function  index()
  {
    return &#39;hello tp5&#39;;
  }
}

我们就可以在控制器中,直接实例化和调用:

<?php
namespace app\index\controller;
class Index
{
    public function index()
    {
      $obj = new \my\Hello();
       return   $obj->index();
    }
}

6.2.4.2 可在入口文件中重新定义:define('EXTEND_PATH','../vendor/');

6.2.4.3 手动注册根命名空间

6.2.4.3.1 应用公共文件:common.php中添加如代码:

在应用公共文件中添加下面的代码:

\think\Loader::addNamespace(&#39;my&#39;,&#39;../application/extend/my/&#39;);

如果要同时注册多个根命名空间,可以使用:

\think\Loader::addNamespace([ &#39;my&#39; => &#39;../application/extend/my/&#39;, &#39;org&#39; => &#39;../application/extend/org/&#39;, ]);

6.2.4.3.2 应用配置文件:config.php中添加:

可以直接在应用的配置文件中添加配置,系统会在应用执行的时候自动注册。

&#39;root_namespace&#39; => [ &#39;my&#39; => &#39;../application/extend/my/&#39;, &#39;org&#39; => &#39;../application/extend/org/&#39;, ]

6.3 可以给命名空间创建别名

6.3.1 应用公共文件common.php


7 trait引入

7.1 trait提供了一种代码复用机制,是类的公共方法集,与继承相比,相当于横向扩展了类的功能

7.2 PHP5.4使用load_trait()引入,PHP5.5以上可以直接自动加载

但由于PHP5.4版本不支持 trait 的自动加载,因此如果是PHP5.4版本,必须手动导入 trait 类库,系统 提供了一个助手函数 load_trait ,用于自动加载 trait 类库,例如,可以这样正确引入 trait 类库。

namespace app\index\controller; load_trait(&#39;controller/Jump&#39;); class index {
// 引入traits\controller\Jump
use \traits\controller\Jump; public function index() {
$this->assign(&#39;name&#39;,&#39;value&#39;);
$this->show(&#39;index&#39;); }
}

如果你的PHP版本大于 5.5 的话,则可以省略 load_trait 函数引入 trait 。

namespace app\index\controller; class index {
use \traits\controller\Jump;
public function index()
{
}
 }

可以支持同时引入多个 trait 类库,例如:

namespace app\index\controller; load_trait(&#39;controller/Other&#39;); load_trait(&#39;controller/Jump&#39;); class index
{
use \traits\controller\Other; use \traits\controller\Jump; public function index() { }
}

或者使用

namespace app\index\controller; load_trait(&#39;controller/Other&#39;); load_trait(&#39;controller/Jump&#39;); class index {
use \traits\controller\Other,\traits\controller\Jump;
public function index()
{
} 
}


7.3 trait命名冲突的解决方案

7.3.1 insteadof:冲突时指定使用哪一个trait类

7.3.2 as:将另一个冲突的trait类以别名的方式访问

8 API友好

8.1 数据输出

8.1.1 控制器中数据输出统一用Response类处理,并不直接输出

8.1.2 设置 default_return_type 或者动态设置不同类型的 Response 输出就可以自动进行数据转换

8.1.3 大多数情况下,你只需要在控制器中返回字符串或者数组即可

8.1.4 默认为html,可在配置文件中:'default_return_type'=>'json'

8.2 错误调试


8.2.1 Trace 调试功能支持 Socket 在内的方 式,可以实现远程的开发调试


三、ThinkPHP5框架思维导图下载地址:

圖解ThinkPHP5框架(二):應用運作流程與生命週期2框架架构.xmind.zip

【相关推荐】

1. 图解ThinkPHP5框架(一):基础知识,开发规范与目录结构

2. 图解ThinkPHP5框架(三):配置类Config.php源码解读

3. 2017年最新的10个thinkphp视频教程推荐

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn