總結(jié)Laravel中常用的PHP語法有哪些

前言

laravel框架因?yàn)槠浣M件化的設(shè)計(jì)并恰當(dāng)使用設(shè)計(jì)模式,使得框架本身簡潔易擴(kuò)展。區(qū)別于 thinkphp 那種整合式功能的框架(功能要么全用要么全不用),laravel 使用 composer 工具進(jìn)行 package 的管理,想加功能直接添加組件即可。比如你寫爬蟲使用頁面采集組件: composer require jaeger/querylist

本文簡要介紹 Laravel 中頻繁用到的 PHP 特性與新語法,具體可參考。

組件化開發(fā)

Laravel 進(jìn)行組件化開發(fā),得益于遵循 PSR-4 規(guī)范的 composer 工具,其利用命名空間和自動加載來組織項(xiàng)目文件。更多參考:composer 自動加載機(jī)制

命名空間

命名沖突

在團(tuán)隊(duì)協(xié)作、引入第三方依賴代碼時,往往可能會出現(xiàn)類、函數(shù)和接口重名的情況。比如:

<?php     # google.php class User  {     private $name; }
<?php     # mine.php // 引入第三方依賴 include 'google.php';  class User {     private $name; }  $user = new User();    // 命名沖突

因?yàn)橥瑫r定義了類 User 導(dǎo)致命名沖突:

立即學(xué)習(xí)PHP免費(fèi)學(xué)習(xí)筆記(深入)”;

總結(jié)Laravel中常用的PHP語法有哪些

解決辦法

從 PHP 5.3 開始引入,參考 PHP 手冊 能知道命名空間有 2 個作用:避免命名沖突、保持命名簡短。比如使用命名空間后:

<?php # google.php Namespace Google;  // 模擬第三方依賴 class User {     private $name = 'google';      public function getName() {         echo $this->name . PHP_EOL;     } }
<?php # mine.php namespace Mine;  // 導(dǎo)入并命名別名 use Google as G;  // 導(dǎo)入文件使得 google.php 命名空間變?yōu)?mine.php 的子命名空間 include 'google.php';  /* 避免了命名沖突 */ class User {     private $name = 'mine';      public function getName() {         echo $this->name . PHP_EOL;     } }  /* 保持了命名簡短 */ // 如果沒有命名空間,為了類名也不沖突,可能會出現(xiàn)這種函數(shù)名 // $user = new Google_User(); // Zend 風(fēng)格并不提倡 $user = new GUser();  // 為了函數(shù)名也不沖突,可能會出現(xiàn)這種函數(shù)名 // $user->google_get_name() $user->getName();  $user = new User(); $user->getName();

運(yùn)行:

$ php demo.php google mine

PSR 規(guī)范

其實(shí) namespace 與文件名無關(guān),但按 PSR 標(biāo)準(zhǔn)要求:命名空間與文件路徑一致 & 文件名與類名一致。比如 Laravel 默認(rèn)生成的 laravel-demo/app/http/Controllers/Auth/LoginController.php,其命名空間為 AppHttpControllersAuth & 類名為 LoginController

遵循規(guī)范,上邊的 mine.php 和 google.php 都應(yīng)叫 User.php

namespace 操作符與__NAMESPACE__ 魔術(shù)常量

... // $user = new User(); $user = new namespaceUser();    // 值為當(dāng)前命名空間 $user->getName();  echo __NAMESPACE__ . PHP_EOL;    // 直接獲取當(dāng)前命名空間字符串    // 輸出 Mine

三種命名空間的導(dǎo)入

<?php namespace CurrentNameSpace;  // 不包含前綴 $user = new User();        # CurrentNameSpaceUser();  // 指定前綴 $user = new GoogleUser();    # CurrentNameSpaceGoogleUser();  // 根前綴 $user = new GoogleUser();    # GoogleUser();

全局命名空間

如果引用的類、函數(shù)沒有指定命名空間,則會默認(rèn)在當(dāng)在 __NAMESPACE__下尋找。若要引用全局類:

<?php namespace Demo;  // 均不會被使用到 function strlen() {} const INI_ALL = 3; class Exception {}  $a = strlen('hi');         // 調(diào)用全局函數(shù) strlen $b = CREDITS_GROUP;          // 訪問全局常量 CREDITS_GROUP $c = new Exception('error');   // 實(shí)例化全局類 Exception

多重導(dǎo)入與多個命名空間

// use 可一次導(dǎo)入多個命名空間 use Google,     Microsoft;  // 良好實(shí)踐:每行一個 use use Google; use Microsoft;
<?php // 一個文件可定義多個命名空間 namespace Google {     class User {} }          namespace Microsoft {     class User {} }     // 良好實(shí)踐:“一個文件一個類”

導(dǎo)入常量、函數(shù)

從 PHP 5.6 開始,可使用 use function 和 use const 分別導(dǎo)入函數(shù)和常量使用:

# google.php const CEO = 'Sundar Pichai'; function getMarketValue() {     echo '770 billion dollars' . PHP_EOL; }
# mine.php use function GooglegetMarketValue as thirdMarketValue; use const GoogleCEO as third_CEO;  thirdMarketValue(); echo third_CEO;

運(yùn)行:

$ php mine.php google 770 billion dollars Sundar Pichaimine Mine

文件包含

手動加載

使用 include 或 require 引入指定的文件,(字面理解)需注意 require 出錯會報(bào)編譯錯誤中斷腳本運(yùn)行,而 include 出錯只會報(bào) warning 腳本繼續(xù)運(yùn)行。

include 文件時,會先去 php.ini 中配置項(xiàng) include_path 指定的目錄找,找不到才在當(dāng)前目錄下找:

總結(jié)Laravel中常用的PHP語法有哪些

<?php      // 引入的是 /usr/share/php/System.php include 'System.php';

自動加載

void?__autoload(String?$class?) ?能進(jìn)行類的自動加載,但一般都使用 spl_autoload_register 手動進(jìn)行注冊:

<?php  // 自動加載子目錄 classes 下 *.class.php 的類定義 function __autoload($class) {     include 'classes/' . $class . '.class.php'; }  // PHP 5.3 后直接使用匿名函數(shù)注冊 $throw = true;        // 注冊出錯時是否拋出異常 $prepend = false;    // 是否將當(dāng)前注冊函數(shù)添加到隊(duì)列頭  spl_autoload_register(function ($class) {     include 'classes/' . $class . '.class.php'; }, $throw, $prepend);

在 composer 生成的自動加載文件 laravel-demo/vendor/composer/autoload_real.php ?中可看到:

class ComposerAutoloaderInit8b41a {     private Static $loader;      public static function loadClassLoader($class)     {         if ('ComposerAutoloadClassLoader' === $class) {             // 加載當(dāng)前目錄下文件             require __DIR__ . '/ClassLoader.php';         }     }           public static function getLoader()     {         if (null !== self::$loader) {             return self::$loader;         }              // 注冊自己的加載器         spl_autoload_register(array('ComposerAutoloaderInit8b41a6', 'loadClassLoader'), true, true);         self::$loader = $loader = new ComposerAutoloadClassLoader();         spl_autoload_unregister(array('ComposerAutoloaderInit8b41a6a', 'loadClassLoader'));          ...      }       ... }

這里只提一下,具體 Laravel 整體是怎么做自動加載的,后邊的文章會細(xì)說。

反射

參考 PHP 手冊,可簡單的理解為在運(yùn)行時獲取對象的完整信息。反射有 5 個類:

ReflectionClass     // 解析類名 ReflectionProperty     // 獲取和設(shè)置類屬性的信息(屬性名和值、注釋、訪問權(quán)限) ReflectionMethod     // 獲取和設(shè)置類函數(shù)的信息(函數(shù)名、注釋、訪問權(quán)限)、執(zhí)行函數(shù)等 ReflectionParameter    // 獲取函數(shù)的參數(shù)信息 ReflectionFunction    // 獲取函數(shù)信息

比如 ReflectionClass 的使用:

<?php  class User {     public $name;     public $age;      public function __conStruct($name = 'Laruence', $age = 35) {         $this->name = $name;         $this->age  = $age;     }      public function intro() {         echo '[name]: ' . $this->name . PHP_EOL;         echo '[age]: '  . $this->age  . PHP_EOL;     } }  reflect('User');  // ReflectionClass 反射類使用示例 function reflect($class) {     try {         $ref = new ReflectionClass($class);         // 檢查是否可實(shí)例化         // interface、abstract class、 __construct() 為 private 的類均不可實(shí)例化         if (!$ref->isInstantiable()) {             echo "[can't instantiable]: ${class}n";         }          // 輸出屬性列表         // 還能獲取方法列表、靜態(tài)常量等信息,具體參考手冊         foreach ($ref->getProperties() as $attr) {             echo $attr->getName() . PHP_EOL;         }          // 直接調(diào)用類中的方法,個人認(rèn)為這是反射最好用的地方         $obj = $ref->newInstanceArgs();         $obj->intro();     } catch (ReflectionException $e) {             // try catch 機(jī)制真的不優(yōu)雅             // 相比之下 golang 的錯誤處理雖然繁瑣,但很簡潔         echo '[reflection exception: ]' . $e->getMessage();     } }

運(yùn)行:

$ php reflect.php name age [name]: Laruence [age]: 35

其余 4 個反射類參考手冊 demo 即可。

后期靜態(tài)綁定

參考 PHP 手冊,先看一個例子:

<?php  class Base {         // 后期綁定不局限于 static 方法     public static function call() {         echo '[called]: ' . __CLASS__ . PHP_EOL;     }      public static function test() {         self::call();        // self   取值為 Base  直接調(diào)用本類中的函數(shù)         static::call();        // static 取值為 Child 調(diào)用者     } }  class Child extends Base {     public static function call() {         echo '[called]: ' . __CLASS__ . PHP_EOL;     } }   Child::test();

輸出:

$ php late_static_bind.php [called]: Base [called]: Child

在對象實(shí)例化時,self:: 會實(shí)例化根據(jù)定義所在的類,static:: 會實(shí)例化調(diào)用它的類。

trait

基本使用

參考 PHP 手冊,PHP 雖然是單繼承的,但從 5.4 后可通過 trait 水平組合“類”,來實(shí)現(xiàn)“類”的多重繼承,其實(shí)就是把重復(fù)的函數(shù)拆分成 triat 放到不同的文件中,通過 use 關(guān)鍵字按需引入、組合。可類比 Golang 的 struct 填鴨式組合來實(shí)現(xiàn)繼承。比如:

<?php  class DemoLogger {     public function log($message, $level) {         echo "[message]: $message", PHP_EOL;         echo "[level]: $level", PHP_EOL;     } }  trait Loggable {     protected $logger;      public function setLogger($logger) {         $this->logger = $logger;     }      public function log($message, $level) {         $this->logger->log($message, $level);     } }  class Foo {         // 直接引入 Loggable 的代碼片段     use Loggable; }  $foo = new Foo; $foo->setLogger(new DemoLogger); $foo->log('trait works', 1);

運(yùn)行:

$ php trait.php [message]: trait works [level]: 1

更多參考:我所理解的 PHP Trait

重要性質(zhì)

優(yōu)先級

當(dāng)前類的函數(shù)會覆蓋 trait 的同名函數(shù),trait 會覆蓋父類的同名函數(shù)( use trait 相當(dāng)于當(dāng)前類直接覆寫了父類的同名函數(shù))

trait 函數(shù)沖突

同時引入多個 trait 可用 , 隔開,即多重繼承。

多個 trait 有同名函數(shù)時,引入將發(fā)生命名沖突,使用 insteadof 來指明使用哪個 trait 的函數(shù)。

重命名與訪問控制

使用 as 關(guān)鍵字可以重命名的 trait 中引入的函數(shù),還可以修改其訪問權(quán)限。

其他

trait 類似于類,可以定義屬性、方法、抽象方法、靜態(tài)方法和靜態(tài)屬性。

下邊的蘋果、微軟和 linux 的小栗子來說明:

<?php  trait Apple {     public function getCEO() {         echo '[Apple CEO]: Tim Cook', PHP_EOL;     }      public function getMarketValue() {         echo '[Apple Market Value]: 953 billion', PHP_EOL;     } }   trait MicroSoft {     public function getCEO() {         echo '[MicroSoft CEO]: Satya Nadella', PHP_EOL;     }      public function getMarketValue() {         echo '[MicroSoft Market Value]: 780 billion', PHP_EOL;     }      abstract public function MadeGreatOS();      static public function staticFunc() {         echo '[MicroSoft Static Function]', PHP_EOL;     }      public function staticValue() {         static $v;         $v++;         echo '[MicroSoft Static Value]: ' . $v, PHP_EOL;     } }   // Apple 最終登頂,成為第一家市值超萬億美元的企業(yè) trait Top {     // 處理引入的 trait 之間的沖突     use Apple, MicroSoft {         Apple::getCEO insteadof MicroSoft;         Apple::getMarketValue insteadof MicroSoft;     } }   class Linux {     use Top {             // as 關(guān)鍵字可以重命名函數(shù)、修改權(quán)限控制         getCEO as private noCEO;     }      // 引入后必須實(shí)現(xiàn)抽象方法     public function MadeGreatOS() {         echo '[Linux Already Made]', PHP_EOL;     }      public function getMarketValue() {         echo '[Linux Market Value]: Infinity', PHP_EOL;     } }  $linux = new Linux(); // 和 extends 繼承一樣 // 當(dāng)前類中的同名函數(shù)也會覆蓋 trait 中的函數(shù) $linux->getMarketValue();  // trait 中可以定義靜態(tài)方法 $linux::staticFunc();  // 在 trait Top 中已解決過沖突,輸出庫克 $linux->getCEO(); // $linux->noCEO();        // Uncaught Error: Call to private method Linux::noCEO()   // trait 中可以定義靜態(tài)變量 $linux->staticValue(); $linux->staticValue();

運(yùn)行:

$ php trait.php [Linux Market Value]: Infinity [MicroSoft Static Function] [Apple CEO]: Tim Cook [MicroSoft Static Value]: 1 [MicroSoft Static Value]: 2

總結(jié)

本節(jié)簡要提及了命名空間、文件自動加載、反射機(jī)制與 trait 等,Laravel 正是恰如其分的利用了這些新特性,才實(shí)現(xiàn)了組件化開發(fā)、服務(wù)加載等優(yōu)雅的特性。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊11 分享