PHP新特性命名空间的详解介绍
1、什么是命名空间
如果你只需要知道现代PHP特性中的一个,那就应该是命名空间。命名空间在PHP5.3.0中引入,其作用是按照一种虚拟的层次结构组织PHP代码,这种层次结构类似操作系统中文件系统的目录结构。命名空间是现代PHP组件生态的基础,现代的PHP组件框架代码都是放在各自全局唯一的厂商命名空间中,以免和其他厂商使用的常见类名冲突。
下面我来看看真实的PHP组件是如何使用命名空间的。Laravel框架中的Http组件用于管理HTTP请求和响应,这个组件用到了常见的类名,例如Request、Response,很多其他PHP组件也用到了这样的类名,既然其他PHP代码也用到了相同的类名,那怎么使用这个组件呢?其实我们可以放心使用,因为这个组件的代码放在了唯一的厂商命名空间Illuminate中。打开这个组件在GitHub中的仓库(https://github.com/laravel/framework/blob/master/src/Illuminate/Http/Response.php),找到Response.php文件:
第3行代码如下:
namespace Illuminate\Http;
这一行是PHP命名空间声明语句,声明命名空间的代码始终应该放在<?php标签后的第一行。通过这个命名空间的声明语句我们可以看到Response位于厂商命名空间Illuminate中(最顶层命名空间),我们还看到Response类在子命名空间Http中,你可以看下和Response.php文件在同一层级的其他文件,会发现它们都使用相同的命名空间声明语句。
命名空间的作用是封装和组织相关的PHP类,就像在文件系统中把相关的文件放在同一个目录中一样。PHP命名空间和操作系统的物理文件系统不同,这是一个虚拟概念,没必要和文件系统中的目录结构完全相同,虽然如此,但是大多数PHP组件为了兼容广泛使用的PSR-4自动加载标准,会把命名空间放到对应文件系统的子目录中。
2、为什么使用命名空间
前面已经提到过,我们的代码可能和其他开发者的代码使用相同的类名、接口名、函数或常量名,如果不使用命名空间,名称会起冲突,导致PHP执行出错。而使用命名空间将代码放到唯一的厂商命名空间,我们的代码就可以和其他开发者使用相同的类名、接口名、函数或常量名。
当然如果你开发的是小型个人项目,只有少量的依赖,类名冲突可能不是问题,但是如果在团队中工作,开发用到许多第三方依赖的大型项目,就要认真对待命名冲突问题,因为你无法控制项目依赖在全局命名空间中引入的类、接口、函数和常量,这也是为什么要使用命名空间的原因。
3、声明命名空间
每个PHP类、接口、函数和常量都在命名空间中,声明命名空间很简单,在<?php标签后的第一行声明,声明语句以namespace开头,随后是一个空格,然后是命名空间的名称,最后以;结尾。
命名空间经常用于设置顶层厂商名,比如我们设置厂商名为LaravelAcademy:
<?php
namespace LaravelAcademy;
在这个命名空间声明语句后声明的所有PHP类、接口、函数和常量都在LaravelAcademy命名空间中,而且和Laravel学院有某种关系。如果我们想组织学院用到的代码该怎么做呢?答案是使用子命名空间。
子命名空间的声明方式和前面的示例完全一样,唯一的区别是我们要使用\符号把命名空间和子命名空间分开,例如:
<?php
namespace LaravelAcademy\ModernPHP;
这个命名空间后的所有类、接口、函数和常量都位于LaravelAcademy\ModernPHP中。
在同一个命名空间中的类没必要在同一个PHP文件中声明,可以在PHP文件的顶部指定一个命名空间或子命名空间,此时,这个文件的代码就是该命名空间或子命名空间的一部分。因此我们可以在不同文件中编写属于同一个命名空间的多个类。
注:厂商命名空间是最顶层的命名空间,也是最重要的命名空间,用于识别品牌或组织,必须具有全局唯一性。子命名空间相对而言没那么重要,但是可以用于组织项目的代码。
4、导入和别名
在命名空间出现之前,PHP开发者使用Zend风格的类名解决命名冲突问题,这是一种类的命名方案,因Zend框架而流行,这种命名方案在PHP类名中使用下划线的方式表示文件系统的目录分隔符。这种约定有两个作用:其一,确保类名是唯一的;其二,原生的自动加载器会把类名中的下划线替换成文件系统的目录分隔符,从而确定文件的路径。例如,Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query类对应的文件是Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php。可以看出,这种命名有个缺点:类名特别长。
现代的PHP命名空间也有这个问题,例如上述Response类完整的全名是Illuminate\Http\Response,幸好,我们可以通过导入以及创建别名的方式来改变这一状况。
导入的意思是指,在每个PHP文件中告诉PHP想使用哪个命名空间、类、接口、函数和常量,导入后就不用使用全名了:
<?php
use Illuminate\Http\Response;
$response = new Response(‘Oops’, 400);
$response->send();
我们通过use关键字告诉PHP,我们想使用Illuminate\Http\Response类,我们只需要输入一次完全限定的类名,随后实例化Response的时候,无需使用完整的类名。
如果觉得这样的类名还是长,可以创建别名。创建别名指的是告诉PHP我要使用简单的名称引用导入的类、接口、函数或常量:
<?php
use Illuminate\Http\Response as Res;
$res = new Res(‘Oops’, 400);
$res->send();
从PHP 5.6开始还可以导入函数和常量,不过要调整use关键字的句法,如果要导入函数,需要使用use func:
<?php
use func Namespace\functionName
functionName();
如果想导入常量,可以使用use constant:
<?php
use constant Namespace\CONST_NAME;
echo CONST_NAME;
当然也支持别名,创建方式和类一样。
5、实用技巧
多重导入
如果想要在PHP文件中导入多个类、接口、函数或常量,需要在PHP文件的顶部使用多个use语句,PHP支持用简短的语法把多个use语句写成一行:
<?php
use Illuminate\Http\Request,
Illuminate\Http\Response;
但是为了可读性,建议不要这么写,还是一行写一个use语句比较好:
<?php
use Illuminate\Http\Request;
use Illuminate\Http\Response;
一个文件使用多个命名空间
PHP允许在一个文件中定义多个命名空间:
<?php
namespace Foo {
//声明类、接口、函数、常量
}
namespace Bar {
//声明类、接口、函数、常量
}
但这么做不好,违背了“一个文件一个类”的良好实践,因此不建议这么做。
全局命名空间
如果引用的类、接口、函数和常量没有指定命名空间,PHP假定引用的类、接口、函数和常量在当前的命名空间中。如果要使用其他命名空间的类、接口、函数或常量,需要使用完全限定的PHP类名(命名空间+类名)。
有些代码在全局命名空间中,没有命名空间,比如原生的Exception类就是这样。在命名空间中引用全局的代码时,需要在类、接口、函数或常量前加\符号:
<?php
namespace My\App;
class Foo {
public function doSomething() {
throw new \Exception();
}
}
自动加载
命名空间还为PHP-FIG制定的PSR-4自动加载标准奠定了坚实的基础,大多数现代的PHP组件都使用了这种自动加载模式,使用依赖管理器Composer可以自动加载项目的依赖,后续我们还会详细介绍Composer和PHP-FIG,现在你只需要知道没有命名空间,就没有现代的PHP生态系统和基于组件的新型架构,由此可见命名空间的重要性。
本文章为各位介绍一篇关于PHP设计模式之Repository资源库模式学习笔记了,希望这篇文章可以为各位带来帮助,具体如下。1、模式定义
Repository 是一个独立的层,介于领域层与数据映射层(数据访问层)之间。它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接口提供给领域层进行领域对象的访问。Repository 是仓库管理员,领域层需要什么东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不需要知道东西实际放在哪。
Repository 模式是架构模式,在设计架构时,才有参考价值。应用 Repository 模式所带来的好处,远高于实现这个模式所增加的代码。只要项目分层,都应当使用这个模式。
2、UML类图
3、示例代码
Post.php
<?php
namespace DesignPatterns\More\Repository;
/**
* Post 类
* @package DesignPatterns\Repository
*/
class Post
{
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $title;
/**
* @var string
*/
private $text;
/**
* @var string
*/
private $author;
/**
* @var \DateTime
*/
private $created;
/**
* @param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param string $author
*/
public function setAuthor($author)
{
$this->author = $author;
}
/**
* @return string
*/
public function getAuthor()
{
return $this->author;
}
/**
* @param \DateTime $created
*/
public function setCreated($created)
{
$this->created = $created;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @param string $text
*/
public function setText($text)
{
$this->text = $text;
}
/**
* @return string
*/
public function getText()
{
return $this->text;
}
/**
* @param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
}
PostRepository.php
<?php
namespace DesignPatterns\More\Repository;
use DesignPatterns\More\Repository\Storage;
/**
* Post 对应的 Repository
* 该类介于数据实体层(Post) 和访问对象层(Storage)之间
*
* Repository 封装了持久化对象到数据存储器以及在展示层显示面向对象的视图操作
*
* Repository 还实现了领域层和数据映射层的分离和单向依赖
*
* PostRepository 类
* @package DesignPatterns\Repository
*/
class PostRepository
{
private $persistence;
public function __construct(Storage $persistence)
{
$this->persistence = $persistence;
}
/**
* 通过指定id返回Post对象
*
* @param int $id
* @return Post|null
*/
public function getById($id)
{
$arrayData = $this->persistence->retrieve($id);
if (is_null($arrayData)) {
return null;
}
$post = new Post();
$post->setId($arrayData['id']);
$post->setAuthor($arrayData['author']);
$post->setCreated($arrayData['created']);
$post->setText($arrayData['text']);
$post->setTitle($arrayData['title']);
return $post;
}
/**
* 保存指定对象并返回
*
* @param Post $post
* @return Post
*/
public function save(Post $post)
{
$id = $this->persistence->persist(array(
'author' => $post->getAuthor(),
'created' => $post->getCreated(),
'text' => $post->getText(),
'title' => $post->getTitle()
));
$post->setId($id);
return $post;
}
/**
* 删除指定的 Post 对象
*
* @param Post $post
* @return bool
*/
public function delete(Post $post)
{
return $this->persistence->delete($post->getId());
}
}
Storage.php
<?php
namespace DesignPatterns\More\Repository;
/**
* Storage接口
*
* 该接口定义了访问数据存储器的方法
* 具体的实现可以是多样化的,比如内存、关系型数据库、NoSQL数据库等等
*
* @package DesignPatterns\Repository
*/
interface Storage
{
/**
* 持久化数据方法
* 返回新创建的对象ID
*
* @param array() $data
* @return int
*/
public function persist($data);
/**
* 通过指定id返回数据
* 如果为空返回null
*
* @param int $id
* @return array|null
*/
public function retrieve($id);
/**
* 通过指定id删除数据
* 如果数据不存在返回false,否则如果删除成功返回true
*
* @param int $id
* @return bool
*/
public function delete($id);
}
MemoryStorage.php
<?php
namespace DesignPatterns\More\Repository;
use DesignPatterns\More\Repository\Storage;
/**
* MemoryStorage类
* @package DesignPatterns\Repository
*/
class MemoryStorage implements Storage
{
private $data;
private $lastId;
public function __construct()
{
$this->data = array();
$this->lastId = 0;
}
/**
* {@inheritdoc}
*/
public function persist($data)
{
$this->data[++$this->lastId] = $data;
return $this->lastId;
}
/**
* {@inheritdoc}
*/
public function retrieve($id)
{
return isset($this->data[$id]) ? $this->data[$id] : null;
}
/**
* {@inheritdoc}
*/
public function delete($id)
{
if (!isset($this->data[$id])) {
return false;
}
$this->data[$id] = null;
unset($this->data[$id]);
return true;
}
}
例子一
function distance($lat1, $lng1, $lat2, $lng2, $miles = true)
{
$pi80 = M_PI / 180;
$lat1 *= $pi80;
$lng1 *= $pi80;
$lat2 *= $pi80;
$lng2 *= $pi80;
$r = 6372.797;
$dlat = $lat2 - $lat1;
$dlng = $lng2 - $lng1;
$a = sin($dlat/2)*sin($dlat/2)+cos($lat1)*cos($lat2)*sin($dlng/2)*sin($dlng/2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$km = $r * $c;
return ($miles ? ($km * 0.621371192) : $km);
}
例子二
/**
* 计算两个坐标之间的距离(米)
* @param float $fP1Lat 起点(纬度)
* @param float $fP1Lon 起点(经度)
* @param float $fP2Lat 终点(纬度)
* @param float $fP2Lon 终点(经度)
* @return int
*/
function distanceBetween($fP1Lat, $fP1Lon, $fP2Lat, $fP2Lon){
$fEARTH_RADIUS = 6378137;
//角度换算成弧度
$fRadLon1 = deg2rad($fP1Lon);
$fRadLon2 = deg2rad($fP2Lon);
$fRadLat1 = deg2rad($fP1Lat);
$fRadLat2 = deg2rad($fP2Lat);
//计算经纬度的差值
$fD1 = abs($fRadLat1 - $fRadLat2);
$fD2 = abs($fRadLon1 - $fRadLon2);
//距离计算
$fP = pow(sin($fD1/2), 2) +
cos($fRadLat1) * cos($fRadLat2) * pow(sin($fD2/2), 2);
return intval($fEARTH_RADIUS * 2 * asin(sqrt($fP)) + 0.5);
}
/**
* 百度坐标系转换成标准GPS坐系
* @param float $lnglat 坐标(如:106.426, 29.553404)
* @return string 转换后的标准GPS值:
*/
function BD09LLtoWGS84($fLng, $fLat){ // 经度,纬度
$lnglat = explode(',', $lnglat);
list($x,$y) = $lnglat;
$Baidu_Server = "http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x={$x}&y={$y}";
$result = @file_get_contents($Baidu_Server);
$json = json_decode($result);
if($json->error == 0){
$bx = base64_decode($json->x);
$by = base64_decode($json->y);
$GPS_x = 2 * $x - $bx;
$GPS_y = 2 * $y - $by;
return $GPS_x.','.$GPS_y;//经度,纬度
}else
return $lnglat;
}
Composer 中文网致力于推广 PHP 世界的包管理工具 Composer 在国内的普及以及独立开发并维护 Packagist 中国全量镜像系统
今天要装一个包endroid/qrcode,可是死活装不上。
composer require 安装的时候一直报
[InvalidArgumentException]
Could not find package endroid/qrcode at any version for your minimum-stability (stable). Check the pac
kage spelling or your minimum-stability
然后新composer init一个空项目就能装上了。然后开始分析我的现有项目的composer.json文件。
最后发现是之前使用了国内的composer镜像,加上了一个配置
{
"repositories": [
{
"packagist": false
},
{
"type": "composer",
"url": "http://packagist.phpcomposer.com"
}
]
}
之前只把后一段给删了,没有删前一段。把前一段的“packagist”: false 也删了就好了。
在php中set_include_path会导致require,include用法不一样了,对于这个问题我们来看一个简单的例子,具体如下。在PHP中经常使用include,require来引用其他文件,使用相对路径或者绝对路径,如果通过set_include_path函数或者ini_set(‘include_path’,xxx)设置include_path配置选项,那么require文件如果在其他目录也可以直接引用。
设置include_path配置选项
如:require_once “class.smarttemplate.php”,这个文件其实并不在当前文件所在目录,但是同样可以这样直接引用。因为前面通过ini_set设置了include_path:
PHP
// 包含配置文件和函数库路径
ini_set('include_path', INCLUDE_PATH . DS . "library" . PATH_SEPARATOR . ini_get('include_path'));
set_include_path函数
set_include_path函数和ini_set(‘include_pah’,xxx)作用是一样的,只不过ini_set方式适合所有PHP版本,对现在来说两者没有差别。
相关文章
- 这篇文章主要介绍了解决IDEA占用C盘空间过大的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-22
JavaScript中的Reflect对象详解(ES6新特性)
这篇文章主要介绍了JavaScript中的Reflect对象(ES6新特性)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下...2016-07-29- 这篇文章主要给大家介绍了关于Swift中命名空间的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
- 这篇文章主要介绍了c# 如何使用 My 命名空间,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2020-12-08
- 这篇文章主要介绍了ES6新特性之变量和字符串用法,结合具体实例形式分析了ES6中变量与字符串的特性、使用方法与相关注意事项,需要的朋友可以参考下...2017-04-03
- 这篇文章主要介绍了C# 命名空间(Namespace)的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以参考下...2020-11-03
- GTID(Global Transaction ID)是对于一个已提交事务的编号,并且是一个全局唯一的编号。下文给大家介绍MySQL 5.6 GTID新特性实践,感兴趣的朋友一起看看吧...2016-10-20
- 这篇文章主要介绍了C++17新特性个人总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-22
- 这篇文章主要介绍了Redis swap空间的使用示例,帮助大家更好的理解和学习使用Redis数据库,感兴趣的朋友可以了解下...2021-03-25
- namespace即“命名空间”,也称“名称空间” 、”名字空间”。接下来通过本文给大家介绍JavaScript中命名空间namespace模式的相关知识,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧...2016-06-24
- 这篇文章主要介绍了Redis开启键空间通知实现超时通知的步骤,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15
- 这篇文章主要介绍了python3.9之你应该知道的新特性详解,需要的朋友可以参考下...2021-04-29
- PHP的命名空间(namespace)是php5.3之后才有的之前学习php所以没有这个东西了,最近用到了php命名空间了,下面我们一起来看看命名空间namespace用法 现在说这个,感觉有...2016-11-25
ES6新特性之类(Class)和继承(Extends)相关概念与用法分析
这篇文章主要介绍了ES6新特性之类(Class)和继承(Extends)相关概念与用法,结合实例形式较为详细的分析了ES6中类(Class)和继承(Extends)的基本概念、语法、使用方法与注意事项,需要的朋友可以参考下...2017-05-27- 这篇文章主要介绍了Oracle如何设置表空间数据文件大小,文中讲解非常细致,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-22
- 从8.0开始,可通过SET PERSIST命令将全局变量的修改持久化到配置文件中,下面这篇文章主要给大家介绍了关于MySQL8新特性之全局参数持久化的相关资料,需要的朋友可以参考下...2021-10-11
- .net的命名空间类库的简单介绍,需要的朋友可以参考一下...2020-06-25
- PHP7新特性foreach与前版本稍一些改变了,那么在PHP7新特性foreach到底作了什么改变呢,我们下面一起来看看PHP7新特性foreach 修改使用例子,希望文章能够帮助到大家。...2016-11-25
- 今天小编在这里就来给美图秀秀的这一款软件的使用者们来说一下制作QQ空间MM签名的教程,各位想知道具体制作步骤的使用者们,那么下面就快来跟着小编一起看一下吧。 ...2016-09-14
- 下面小编就为大家分享一篇asp.net使用H5新特性实现异步上传的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-09-22