## 练习1:Hello,ThinkPHP >[info] 任务:在安装完成后来完成第一个实例代码,要求运行后显示`Hello,ThinkPHP!`。 ### 实现一:路由实现 针对这个需求的最简单实现,是在路由定义文件(`route/route.php`)开头添加如下代码: ~~~ Route::get('/', function () { return 'Hello,ThinkPHP!'; }); ~~~ 然后直接访问(这里使用了上一个练习的内容提到的`Vhost`访问) ~~~ http://tp5.com ~~~ 页面输出: ~~~ Hello,ThinkPHP! ~~~ 是不是很简单?直接通过路由定义返回页面输出内容(更多的路由用法我们后面还会陆续提到,暂且不表)。接下来,我们来看第二种实现代码,通过标准的控制器来实现。 ### 实现二:控制器实现 进入`CMD`命令行,切换到应用根目录(也就是`tp5`目录),执行下面的指令: ~~~ php think make:controller index/Hello --plain ~~~ 会自动生成一个控制器文件,位于: ~~~ application/index/controller/Hello.php ~~~ 在编辑器打开,可以看到已经生成了一个空的控制器(类)文件 ~~~ <?php namespace app\index\controller; use think\Controller; class Hello extends Controller { // } ~~~ > 后面再提到控制器的时候,你的第一反应就是这个`controller`目录下面的某个类,而且从类名就能直观看出控制器名,从命名空间可以找到所在位置。 我们在控制器类里面添加一个`index`方法(必须使用`public`类型,一般我们称为操作方法,它是URL访问的最小单元),内容如下: ~~~ <?php namespace app\index\controller; use think\Controller; class Hello extends Controller { // 显示欢迎页面 public function index() { return 'Hello,ThinkPHP!'; } } ~~~ > 今后提到控制器的某个操作,你的第一反应就是去找这个控制器下面的某个方法。 然后我们直接在浏览器中访问 ~~~ http://tp5.com/index/hello ~~~ 页面输出: ~~~ Hello,ThinkPHP! ~~~ 至此,我们完成了一个标准的控制器输出`Hello,ThinkPHP`的实现代码。 ## 拓展讨论 ### 为何用`return`而不用`echo` 我们看到控制器的操作方法都是直接`return`的,而没有使用我们习惯的`echo`方法输出。你会发现使用`echo`输出的话,最终的结果其实是一样的,但为什么不建议使用呢?   确实,如果你要输出的数据本身就是字符串的话,使用`echo`的结果是一样的,但ThinkPHP的控制器操作方法支持不同的响应输出(甚至需要设置不同的头信息),对于不同的响应输出会调用不同的`Response`子类,例如`JSON`、`XML`等,你可以直接`return`一个数组或者对象数据,然后交给`Response`来处理转换,甚至你可以直接return一个`Response`对象。还有一种情况是,你可以统一使用系统提供的钩子对响应输出进行额外的处理,而如果你使用了`echo`直接输出,将无法享受这些功能特性。 >[info] 关于更多的`Response`响应处理的知识,我们后面还会陆续提到,暂且不用深究,但请保持操作方法统一`return`的习惯(除非在操作方法中间进行调试的时候可以使用`echo`或者`dump`)。 ### 更优雅的URL 是否觉得 ~~~ http://tp5.com/index/hello ~~~ 这个访问地址有点太长了,似乎不够优雅,没关系,我们马上来改进下。 打开路由定义文件`route/route.php`,添加一行代码如下: ~~~ Route::get('hello','Hello/index'); ~~~ 现在你可以优雅的访问 ~~~ http://tp5.com/hello ~~~ 输出同样的内容。 ### 添加URL变量 我们希望欢迎页面能显示输入的名称,而不是ThinkPHP,就需要用到URL变量,我们分别针对上述两种实现来进行调整。 如果是第一种,我们修改路由定义如下: ~~~ Route::get('hello/:name', function ($name) { return 'Hello,' . $name . '!'; }); ~~~ >[info] 你暂时不用深究路由传递参数的原理,你只需要记得使用`:name`就表示URL中输入的变量,然后可以在闭包中调用该同名变量。 直接访问 ~~~ http://tp5.com/hello/张三 ~~~ 页面输出结果: ~~~ Hello,张三! ~~~ 你可以尝试更改URL地址`hello`后面的名称来查看输出结果。 如果是第二种实现,首先把路由定义改成 ~~~ Route::get('hello/:name','Hello/index'); ~~~ 然后对`Hello`控制器类的`index`方法稍加调整。 ~~~ <?php namespace app\index\controller; use think\Controller; class Hello extends Controller { // 显示欢迎页面 public function index($name='ThinkPHP') { return 'Hello,' . $name . !'; } } ~~~ >[info] 没有比这更神奇的事情了,路由地址中的变量`name`可以很神奇的传递到`index`方法中。 直接访问 ~~~ http://tp5.com/hello/李四 ~~~ 页面输出结果: ~~~ Hello,李四! ~~~ ### 添加类库后缀 为了尽量避免关键字的冲突,我们可以统一开启类库后缀功能,在应用根目录下的`config`目录下找到`app.php`文件,打开后更改下面的配置参数: ~~~ // 开启应用类库后缀 'class_suffix' => true, ~~~ 开启后,你所有的类库都要统一加上你的目录名称(首字母大写)作为类库名的后缀(同时千万别忘了,文件名也要同时调整成和类名一样),例如: ~~~ <?php namespace app\index\controller; use think\Controller; class HelloController extends Controller { // 显示欢迎页面 public function index() { return 'Hello,ThinkPHP!'; } } ~~~ 在你需要创建一个新控制器的时候,可以使用下面的命令: ~~~ php think make:controller index/TestController --plain ~~~ 会自动生成一个控制器文件,位于: ~~~ application/index/controller/TestController.php ~~~ >[danger] 后面的练习如果没有特殊说明,都不开启应用类库后缀。