Hello PHP
讓我們在桌面創(chuàng)建一個 hello.php的文件
<?php
echo "Hello World!";
執(zhí)行php hello.php
Hooray!!! 我們得到了一個輸出 Hello World! 的程序
我們如何通過Http請求輸出"Hello World!"呢?
借助 Http 服務器后裸。例如使用 nginx,監(jiān)聽 http 請求冒滩,當有對 php 文件的請求發(fā)生時微驶,我們將它轉發(fā)給 php-fpm,php-fpm 執(zhí)行我們想要執(zhí)行的 php文件并拿到結果开睡,原路返回
PHP 類
通常情況下因苹,我們需要使用類來執(zhí)行特定的功能,那么上面的程序我們可以改寫為下面這樣篇恒,我們使用類 Hello 來輸出了 Hello World!
<?php
class Hello {
public function index() {
echo "Hello World!";
}
}
(new Hello())->index();
可是如果我們把所有類都寫在一個文件里容燕,這個文件將變得臃腫和難以維護。
<?php
class Hello {
public function index() {
echo "Hello World!";
}
}
(new Hello())->index();
class h2{
function __construct(){
echo "H2!";
}
}
(new h2());
- 所以通常情況下婚度,一個文件中我們只放一個類蘸秘,一個類只負責一件事情,我們的類的調用也通常是在另外的文件中蝗茁。
修改 hello.php
<?php
class Hello {
public function index() {
echo "Hello World!";
}
}
在桌面新建一個 use.php的文件, 我們將使用這個新的 php 文件作為入口調用 hello.php
<?php
$hello = new Hello();
$hello->index();
執(zhí)行php use.php
醋虏,我們得到了一個報錯
PHP Fatal error: Uncaught Error: Class "Hello" not found in /Users/zhangmingsheng/Desktop/use.php:2
Stack trace:
#0 {main}
thrown in /Users/zhangmingsheng/Desktop/use.php on line 2
Fatal error: Uncaught Error: Class "Hello" not found in /Users/zhangmingsheng/Desktop/use.php:2
Stack trace:
#0 {main}
thrown in /Users/zhangmingsheng/Desktop/use.php on line 2
找不到 Class "Hello",這是因為我們在 use.php 中并沒有將 Hello 類加載進來
PHP 類的加載
我們可以使用 include 或者 require 方法來加載 php 文件
include在包含文件不存在時會發(fā)出警告、在多次包含同一個文件時會重復解析和執(zhí)行哮翘;而require在包含文件不存在時會引發(fā)致命錯誤颈嚼、在多次包含同一個文件時只包含一次。使用include_once和require_once可以避免重復包含的問題饭寺。在實際開發(fā)中阻课,我們可以根據(jù)具體需求選擇適合的函數(shù)來使用。
在 use.php中添加 include 語句
<?php
include '/Users/zhangmingsheng/Desktop/hello.php';
$hello = new Hello();
$hello->index();
執(zhí)行php use.php
艰匙,成功輸出Hello World!
命名空間
為防止重名限煞,php 引入了命名空間,默認情況下员凝,所有常量署驻、類和函數(shù)名都放在全局空間下,就和PHP支持命名空間之前一樣。
命名空間通過關鍵字namespace 來聲明旺上。
- 如果一個文件中包含命名空間瓶蚂,它必須在其它所有代碼之前聲明命名空間。
<?php
// 定義代碼在 'MyProject' 命名空間中
namespace MyProject;
使用命名空間:別名/導入 use
<?php
use My\Full\Classname as Another;
// 下面的例子與 use My\Full\NSname as NSname 相同
use My\Full\NSname;
- use導入可以使用 as 添加別名宣吱,如果沒有使用 as窃这,則最后一段路徑名將作為別名
使用命名空間改寫我們的代碼,
修改hello.php
<?php
namespace HelloSpace;
class Hello {
public function index() {
echo "Hello World!";
}
}
修改use.php
<?php
use HelloSpace\Hello;
$hello = new Hello();
$hello->index();
執(zhí)行php use.php
征候,我們得到了一個報錯
PHP Fatal error: Uncaught Error: Class "HelloSpace\Hello" not found in /Users/zhangmingsheng/Desktop/use.php:3
Stack trace:
#0 {main}
thrown in /Users/zhangmingsheng/Desktop/use.php on line 3
Fatal error: Uncaught Error: Class "HelloSpace\Hello" not found in /Users/zhangmingsheng/Desktop/use.php:3
Stack trace:
#0 {main}
thrown in /Users/zhangmingsheng/Desktop/use.php on line 3
找不到 Class "HelloSpace\Hello"
钦听,原來,use 并不能直接用來加載 php 文件倍奢,使用 use 的前提是朴上,use 的目標類文件已經(jīng)被加載,否則就會報錯卒煞,我們仍然需要使用 include 語句來加載 hello.php
添加 include 語句痪宰,一切恢復正常
<?php
include '/Users/zhangmingsheng/Desktop/hello.php';
use HelloSpace\Hello;
$hello = new Hello();
$hello->index();
use 的特殊性
在上面的例子中我們看到,use 并不能直接用來加載 php 文件畔裕,使用 use 的前提是衣撬,類文件已經(jīng)被加載,否則就會報找不到類的錯誤扮饶。
但是 use 又有它的特殊性具练,就是 use 可以作為前向引用,什么意思呢甜无,我們對use.php進行下微調
<?php
use HelloSpace\Hello;
include '/Users/zhangmingsheng/Desktop/hello.php';
$hello = new Hello();
$hello->index();
再次執(zhí)行php use.php
, 成功輸出了Hello World!
啊咧咧扛点?不是說需要先 include 么,為什么這次又可以了呢岂丘?
- 這是因為我們雖然聲明了
use HelloSpace\Hello;
陵究,但是還沒有開始調用 Hello 類,
use 可以提前聲明我要使用 Hello奥帘,只要保證在真正使用 Hello 前铜邮,Hello 類可以完成加載。
所以如果我們再次如下調整寨蹋,猜猜結果會怎樣呢松蒜?
<?php
use HelloSpace\Hello;
$hello = new Hello();
$hello->index();
include '/Users/zhangmingsheng/Desktop/hello.php';
沒錯,我們又獲得了找不到 Class "HelloSpace\Hello"
的報錯
關于use 的前向引用已旧,在 laravel 框架的入口文件也有體現(xiàn)
<?php
//注意這兩個 use 聲明
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Check If The Application Is Under Maintenance
|--------------------------------------------------------------------------
|
| If the application is in maintenance / demo mode via the "down" command
| we will load this file so that any pre-rendered content can be shown
| instead of starting the framework, which could cause an exception.
|
*/
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| this application. We just need to utilize it! We'll simply require it
| into the script here so we don't need to manually load our classes.
|
*/
require __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request using
| the application's HTTP kernel. Then, we will send the response back
| to this client's browser, allowing them to enjoy our application.
|
*/
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
)->send();
$kernel->terminate($request, $response);
我們發(fā)現(xiàn) autoload.php
自動加載文件是在 use 聲明后才require 進來的秸苗,這里就展示了 use 的特殊性。
那既然提到了自動加載评姨,那什么是自動加載呢难述?
PHP自動加載
在編寫面向對象(OOP) 程序時萤晴,很多開發(fā)者為每個類新建一個 PHP 文件吐句。 這會帶來一個煩惱:每個腳本的開頭胁后,都需要包含(include)一個長長的列表(每個類都有個文件)。
spl_autoload_register() 函數(shù)可以注冊任意數(shù)量的自動加載器嗦枢,當使用尚未被定義的類(class)和接口(interface)時自動去加載攀芯。通過注冊自動加載器,腳本引擎在 PHP 出錯失敗前有了最后一個機會加載所需的類文虏。
像 class 一樣的結構都可以以相同方式自動加載侣诺。包括類、接口氧秘、trait 和枚舉年鸳。
在桌面創(chuàng)建自動加載類Autoloader
<?php
class Autoloader{
function __construct(){
spl_autoload_register(array(__CLASS__, 'autoload'));
}
public static function autoload($class){
include '/Users/zhangmingsheng/Desktop/hello.php';
}
}
此類中,我們注冊了自動加載函數(shù)autoload
丸相,當遇到未加載的類的時候搔确,就去 include hello.php
正常情況下,這里會根據(jù)$class
參數(shù)判斷具體要 include 哪個文件灭忠,我們這里做了簡化膳算,只 include hello.php
接著,我們修改 use.php
<?php
include 'autoloader.php';
new Autoloader();
use HelloSpace\Hello;
$hello = new Hello();
$hello->index();
運行 php use.php
弛作,成功輸出 Hello World涕蜂!
我們發(fā)現(xiàn),我們這次并沒有在 use.php
中直接 include hello.php
映琳,而是通過autoloader
自動加載了hello.php
机隙。
將引入 autoloader
的兩條語句注釋,再次運行萨西,報錯黍瞧,找不到 Class "HelloSpace\Hello"
。
這就是php的自動加載機制一個簡單的演示
自動類加載器
-
想想我們之前的自動加載模型原杂,還有哪些地方可以改進印颤?
對,我們不是根據(jù)實際需要的類進行相應類的加載〈┮蓿現(xiàn)在我們來完善它年局,寫一個可用的自動加載器吧
前面我們已經(jīng)簡單講過 namespace,namespace像 java 的 package 一樣咸产,可以防止類名重復引發(fā)問題矢否。但 java 的 package 還有一個附帶的作用,就是 package可以推倒出類文件的目錄脑溢,這樣當我們知道一個類的 package 的時候僵朗,我們也能推斷出如何加載這個類赖欣。那我們 php 的 namespace 是否也可以具有這樣的功能呢?答案是可以的
- 與目錄和文件的關系很像验庙,PHP 命名空間也允許指定層次化的命名空間的名稱顶吮。
因此,命名空間的名字可以使用分層次的方式定義:
<?php
namespace MyProject\Sub\Level; //聲明分層次的單個命名空間
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
我們現(xiàn)在讓我們的程序的命名空間與目錄名對應起來
在桌面創(chuàng)建 HelloSpace
目錄粪薛,將桌面的hello.php
移入其中
<?php
namespace HelloSpace;
class Hello {
public function index() {
echo "Hello World!";
}
}
我們按照命名空間對應目錄名的規(guī)則悴了,修改autoloader.php
<?php
class Autoloader{
function __construct(){
spl_autoload_register(array(__CLASS__, 'autoload'));
}
public static function autoload($class){
$filePath = str_replace('\\', '/', $class);
require_once $filePath . '.php';
}
}
運行 php use.php
,成功輸出 HelloWorld违寿!
- 可是在我們開發(fā)的時候有時候命名空間并不一定和目錄名完全對應湃交,為了兼容這種情況,我們就需要使用 Map藤巢,設置他們的對應關系
修改autoloader.php
使我們可以添加Map映射
<?php
class Autoloader{
protected $base_dir = "/Users/zhangmingsheng/desktop";
protected $maps = [];
function __construct(){
spl_autoload_register(array(__CLASS__, 'autoload'));
}
public function autoload($class){
//完整的類名由命名空間名和類名組成
//得到命名空間名
$pos = strrpos($class, '\\');
$namespace = substr($class, 0, $pos);
//得到類名
$realClass = strtolower(substr($class, $pos + 1));
//根據(jù)命名空間名和類名得到文件路徑
$this->mapLoad($namespace, $realClass);
}
protected function mapLoad($namespace, $realClass){
if(isset($this->maps[$namespace])){
$namespace = $this->maps[$namespace];
}
//處理路徑, 將命名空間中的\替換成/. 由于 namespace 有些帶有\(zhòng)有些不帶有\(zhòng), 所以需要rtrim處理
$namespace = rtrim(str_replace('\\', '/', $namespace),'/').'/';
$filePath = $this->base_dir . '/' . $namespace . $realClass . '.php';
//將該文件包含進來即可
if(file_exists($filePath)){
require_once $filePath;
}else{
echo sprintf("%s文件不存在", $filePath);
}
}
function addMaps($namespace, $path){
if(isset($this->maps[$namespace])){
echo sprintf("%s已經(jīng)存在", $namespace);
return;
}
$this->maps[$namespace] = $path;
}
}
接下來搞莺,我們創(chuàng)建一個新的php文件來驗證我們的 autoloader,
在桌面創(chuàng)建目錄 relationship掂咒,在 relationship 目錄下才沧,創(chuàng)建spare目錄, 在 spare 中創(chuàng)建xiaoming.php
<?php
namespace Spare;
class Xiaoming {
public function sing() {
echo "xiaoming sing!";
}
public function dance() {
echo "xiaoming dance!";
}
}
此時xiaoming.php的目錄名是 relationship/spare/xiaoming.php,而 Xiaoming 類的命名空間是 Spare俏扩。
修改use.php
糜工,添加 map 映射
<?php
include 'autoloader.php';
$auto = new Autoloader();
$auto->addMaps('Spare', 'relationship/spare');
use Spare\Xiaoming;
$xm = new Xiaoming();
$xm->dance();
執(zhí)行 php use.php
成功輸出 xiaoming dance!
至此,我們的自動加載器就完成了
laravel Request的生命周期
PHP 主要的工作是處理 Http 請求录淡,所以 PHP 的開發(fā)框架的核心也都圍繞 Http 請求服務捌木,所以了解請求的生命周期對理解 laravel 框架很有幫助
要說生命,那肯定要從生命的誕生開始嫉戚,我們找到 laravel 的入口文件 index.php, 看看它都做了什么
<?php
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Check If The Application Is Under Maintenance
|--------------------------------------------------------------------------
|
| If the application is in maintenance / demo mode via the "down" command
| we will load this file so that any pre-rendered content can be shown
| instead of starting the framework, which could cause an exception.
|
*/
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| this application. We just need to utilize it! We'll simply require it
| into the script here so we don't need to manually load our classes.
|
*/
require __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request using
| the application's HTTP kernel. Then, we will send the response back
| to this client's browser, allowing them to enjoy our application.
|
*/
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
)->send();
$kernel->terminate($request, $response);
use 語句前面已經(jīng)講過了刨裆,我們往后看
首先定義LARAVEL_START
常量,然后判斷是否處于maintenance
模式彬檀。之后才進入正題:
- 加載
autoload.php
- 加載
app.php
- 初始化
Http\Kernel
- 處理
request
- 處理
response
前面已經(jīng)簡單講述過autoload帆啃,現(xiàn)在我們看下 laravel 的app
到底是什么, app
是一個 service container
,那什么是service container
呢?
這里引用下文檔原文
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.
上面的定義中提到了依賴注入窍帝,那什么是依賴注入呢努潘?可以看下我之前寫的一篇短文,依賴注入(DI),控制反轉(IOC),依賴反轉(依賴倒置/DIP)
短文中顯示的舉例了什么是依賴注入坤学,同時也提到 laravel的app
使用自動加載來隱式的完成了依賴注入的
可能看到這里疯坤,雖然知道了 app
能做什么,但是依然想不明白app
到底做了啥深浮。
- app其實為你提供了一個workspace, 它為你管理依賴關系压怠,從而可以讓你把精力集中在業(yè)務邏輯中。
它就像是炒菜鍋飞苇,使你能專注在如何炒好菜菌瘫,調好味道蜗顽,而不用擔心一不留神,火就把菜燒成了灰燼雨让,而我們炒菜的第一步雇盖,就是要有一個炒菜鍋
有了app
后,我們進入Http\Kernel
的初始化
我們來看下Illuminate\Foundation\Http\Kernel.php