本文简要介绍 laravel 中频繁用到的 php 特性与新语法,具体可参考。
组件化开发laravel 进行组件化开发,得益于遵循 psr-4 规范的 composer 工具,其利用命名空间和自动加载来组织项目文件。更多参考:composer 自动加载机制
命名空间命名冲突在团队协作、引入第三方依赖代码时,往往可能会出现类、函数和接口重名的情况。比如:
<?php # google.phpclass user { private $name;}
<?php # mine.php// 引入第三方依赖include 'google.php';class user{ private $name;}$user = new user(); // 命名冲突
因为同时定义了类 user 导致命名冲突:
解决办法从 php 5.3 开始引入,参考 php 手册 能知道命名空间有 2 个作用:避免命名冲突、保持命名简短。比如使用命名空间后:
<?php# google.phpnamespace google;// 模拟第三方依赖class user { private $name = 'google'; public function getname() { echo $this->name . php_eol; }}
<?php# mine.phpnamespace mine;// 导入并命名别名use google as g;// 导入文件使得 google.php 命名空间变为 mine.php 的子命名空间include 'google.php';/* 避免了命名冲突 */class user{ private $name = 'mine'; public function getname() { echo $this->name . php_eol; }}/* 保持了命名简短 */// 如果没有命名空间,为了类名也不冲突,可能会出现这种函数名// $user = new google_user();// zend 风格并不提倡$user = new g\user();// 为了函数名也不冲突,可能会出现这种函数名// $user->google_get_name()$user->getname();$user = new user();$user->getname();
运行:
$ php demo.phpgooglemine
psr 规范其实 namespace 与文件名无关,但按 psr 标准要求:命名空间与文件路径一致 & 文件名与类名一致。比如 laravel 默认生成的 laravel-demo/app/http/controllers/auth/logincontroller.php,其命名空间为 app\http\controllers\auth & 类名为 logincontroller
遵循规范,上边的 mine.php 和 google.php 都应叫 user.php
namespace 操作符与__namespace__ 魔术常量...// $user = new user();$user = new namespace\user(); // 值为当前命名空间$user->getname();echo __namespace__ . php_eol; // 直接获取当前命名空间字符串 // 输出 mine
三种命名空间的导入<?phpnamespace currentnamespace;// 不包含前缀$user = new user(); # currentnamespace\user();// 指定前缀$user = new google\user(); # currentnamespace\google\user();// 根前缀$user = new \google\user(); # \google\user();
全局命名空间如果引用的类、函数没有指定命名空间,则会默认在当在 __namespace__下寻找。若要引用全局类:
<?phpnamespace demo;// 均不会被使用到function strlen() {}const ini_all = 3;class exception {}$a = \strlen('hi'); // 调用全局函数 strlen$b = \credits_group; // 访问全局常量 credits_group$c = new \exception('error'); // 实例化全局类 exception
多重导入与多个命名空间// use 可一次导入多个命名空间use google, microsoft;// 良好实践:每行一个 useuse google;use microsoft;
<?php// 一个文件可定义多个命名空间namespace google { class user {}} namespace microsoft { class user {}} // 良好实践:“一个文件一个类”
导入常量、函数从 php 5.6 开始,可使用 use function 和 use const 分别导入函数和常量使用:
# google.phpconst ceo = 'sundar pichai';function getmarketvalue() { echo '770 billion dollars' . php_eol;}
# mine.phpuse function google\getmarketvalue as thirdmarketvalue;use const google\ceo as third_ceo;thirdmarketvalue();echo third_ceo;
运行:
$ php mine.phpgoogle770 billion dollarssundar pichaiminemine
文件包含手动加载使用 include 或 require 引入指定的文件,(字面理解)需注意 require 出错会报编译错误中断脚本运行,而 include 出错只会报 warning 脚本继续运行。
include 文件时,会先去 php.ini 中配置项 include_path 指定的目录找,找不到才在当前目录下找:
<?php // 引入的是 /usr/share/php/system.phpinclude 'system.php';
自动加载void __autoload(string $class ) 能进行类的自动加载,但一般都使用 spl_autoload_register 手动进行注册:
<?php// 自动加载子目录 classes 下 *.class.php 的类定义function __autoload($class) { include 'classes/' . $class . '.class.php';}// php 5.3 后直接使用匿名函数注册$throw = true; // 注册出错时是否抛出异常$prepend = false; // 是否将当前注册函数添加到队列头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 ('composer\autoload\classloader' === $class) { // 加载当前目录下文件 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 \composer\autoload\classloader(); spl_autoload_unregister(array('composerautoloaderinit8b41a6a', 'loadclassloader')); ... } ...}
这里只提一下,具体 laravel 整体是怎么做自动加载的,后边的文章会细说。
反射参考 php 手册,可简单的理解为在运行时获取对象的完整信息。反射有 5 个类:
reflectionclass // 解析类名reflectionproperty // 获取和设置类属性的信息(属性名和值、注释、访问权限)reflectionmethod // 获取和设置类函数的信息(函数名、注释、访问权限)、执行函数等reflectionparameter // 获取函数的参数信息reflectionfunction // 获取函数信息
比如 reflectionclass 的使用:
<?phpclass 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); // 检查是否可实例化 // interface、abstract class、 __construct() 为 private 的类均不可实例化 if (!$ref->isinstantiable()) { echo [can't instantiable]: ${class}\n; } // 输出属性列表 // 还能获取方法列表、静态常量等信息,具体参考手册 foreach ($ref->getproperties() as $attr) { echo $attr->getname() . php_eol; } // 直接调用类中的方法,个人认为这是反射最好用的地方 $obj = $ref->newinstanceargs(); $obj->intro(); } catch (reflectionexception $e) { // try catch 机制真的不优雅 // 相比之下 golang 的错误处理虽然繁琐,但很简洁 echo '[reflection exception: ]' . $e->getmessage(); }}
运行:
$ php reflect.phpnameage[name]: laruence[age]: 35
其余 4 个反射类参考手册 demo 即可。
后期静态绑定参考 php 手册,先看一个例子:
<?phpclass base{ // 后期绑定不局限于 static 方法 public static function call() { echo '[called]: ' . __class__ . php_eol; } public static function test() { self::call(); // self 取值为 base 直接调用本类中的函数 static::call(); // static 取值为 child 调用者 }}class child extends base{ public static function call() { echo '[called]: ' . __class__ . php_eol; }}child::test();
输出:
$ php late_static_bind.php[called]: base[called]: child
在对象实例化时,self:: 会实例化根据定义所在的类,static:: 会实例化调用它的类。
trait基本使用参考 php 手册,php 虽然是单继承的,但从 5.4 后可通过 trait 水平组合“类”,来实现“类”的多重继承,其实就是把重复的函数拆分成 triat 放到不同的文件中,通过 use 关键字按需引入、组合。可类比 golang 的 struct 填鸭式组合来实现继承。比如:
<?phpclass 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);
运行:
$ php trait.php[message]: trait works[level]: 1
更多参考:我所理解的 php trait
重要性质优先级当前类的函数会覆盖 trait 的同名函数,trait 会覆盖父类的同名函数( use trait 相当于当前类直接覆写了父类的同名函数)
trait 函数冲突同时引入多个 trait 可用 , 隔开,即多重继承。
多个 trait 有同名函数时,引入将发生命名冲突,使用 insteadof 来指明使用哪个 trait 的函数。
重命名与访问控制使用 as 关键字可以重命名的 trait 中引入的函数,还可以修改其访问权限。
其他trait 类似于类,可以定义属性、方法、抽象方法、静态方法和静态属性。
下边的苹果、微软和 linux 的小栗子来说明:
<?phptrait 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 最终登顶,成为第一家市值超万亿美元的企业trait top{ // 处理引入的 trait 之间的冲突 use apple, microsoft { apple::getceo insteadof microsoft; apple::getmarketvalue insteadof microsoft; }}class linux{ use top { // as 关键字可以重命名函数、修改权限控制 getceo as private noceo; } // 引入后必须实现抽象方法 public function madegreatos() { echo '[linux already made]', php_eol; } public function getmarketvalue() { echo '[linux market value]: infinity', php_eol; }}$linux = new linux();// 和 extends 继承一样// 当前类中的同名函数也会覆盖 trait 中的函数$linux->getmarketvalue();// trait 中可以定义静态方法$linux::staticfunc();// 在 trait top 中已解决过冲突,输出库克$linux->getceo();// $linux->noceo(); // uncaught error: call to private method linux::noceo() // trait 中可以定义静态变量$linux->staticvalue();$linux->staticvalue();
运行:
$ php trait.php[linux market value]: infinity[microsoft static function][apple ceo]: tim cook[microsoft static value]: 1[microsoft static value]: 2
总结本节简要提及了命名空间、文件自动加载、反射机制与 trait 等,laravel 正是恰如其分的利用了这些新特性,才实现了组件化开发、服务加载等优雅的特性。
以上就是总结laravel中常用的php语法有哪些的详细内容。