文档章节

不依赖PHP7和HHVM,如何在PHP中使用强类型

rockman509
 rockman509
发布于 2017/02/28 18:10
字数 2339
阅读 5
收藏 0
点赞 0
评论 0

现在PHP7已经发布了一段时间了,当中有很多有趣的特性,比如:错误处理、合并空运算符、标量类型声明等等。并且你肯定也听说过,PHP是弱类型的语言,所以在开发当中有些事情变得无法预测。

尽管上面说的对的,但是PHP还是提供了一些方法让你自己的应用在你的掌控之中。现在让我们来看一下下面这段代码:

function plusone($a)  
{  
    return $a + 1;  
}  
  
var_dump(plusone(1));  
var_dump(plusone("1"));  
var_dump(plusone("1 apple"));  
// output  
  
int(2)  
int(2)  
int(2)  

这个函数的功能是将传入的参数加1然后输出。然而第二次和第三次执行的时候我们传入了一个字符串,并且函数输出了整型。这里发生的事情叫做字符串转换。通过验证我们可以保证用户传入的是一个数字类型的值。

function plusone($a)  
{  
    if ( !is_numeric($a) )  
    {  
        throw new InvalidArgumentException("I can only increment numbers!", 1);  
    }  
  
    return $a + 1;  
}  

 

在第三次调用函数的时候会抛出一个InvalidArgumentException 错误信息。如果我们想指定传入参数的类型:

function plusone(int $a)  
{  
    return $a + 1;  
}  
  
var_dump(plusone(1));  
var_dump(plusone("1"));  
var_dump(plusone("1 apple"));  
// output  
  
PHP Catchable fatal error:  Argument 1 passed to plusone() must be an instance of int, integer given, called in /vagrant/test_at/test.php on line 7 and defined in /vagrant/test_at/test.php on line 2  

 

看到这个错误信息你可能会觉得有点奇怪,因为我们规定了传入的参数必须是个整型。

如果我们仔细看看这个错误信息,就会知道它说的是“必须是一个整型的实例”-这就说明了整型是一个类。

在PHP5中,函数的返回值会让人更加困惑。简单的说,我们无法自动锁定变量的类型,并且我们只有在函数执行后再检查返回的值是否和我们预期的一样。

 

增强类型

 

在PHP7之前的版本,Box开发组提出了一个想法来解决PHP5中由类型引发的安全问题。在使用了断言、类型提示等等方法之后,他们决定用cleaner来解决这一问题。

 

我们也看到了Facebook使用了HHVM和Hack来稍微推动了如何在PHP中解决这一问题。但是Box并不想改变PHP的源代码或者任何核心的东西。他们的解决方案是建立了一个独立的增强类型扩展,从而在PHP脚本执行的时候用phpDoc判断变量的类型。

安装

下面的扩展只适用于PHP5 - 如果你使用的是PHP7,你只需要坐在你的椅子上享受你的下午茶就好。

安装过程并不是很复杂,并且不需要任何的配置。下面介绍的方法基于Ubuntu,但是在其他Linux发行版和OS系统上也同样适用。

# update system  
sudo apt-get update  
  
# install required dependencies  
sudo apt-get install php5-dev bison flex  
  
# clone the repo  
git clone git@github.com:box/augmented_types.git  
  
# install extension  
  
phpize  
./configure --enable-augmented_types  
make  
make test  
sudo make install  

我们必须编辑php.ini文件来告诉PHP启用我们安装的扩展。

# Get php.ini location from PHP info. This will print the cli configuration file, you may need to edit /etc/php5/fpm/php.ini )  
php -i | grep 'Loaded Configuration File'  
  
# output: /etc/php5/cli/php.ini  
vim /etc/php5/cli/php.ini  
  
# Get `extension_dir` the PHP info  
php -i | grep extension_dir  
# output: extension_dir => /usr/lib/php5/20100525 => /usr/lib/php5/20100525  
  
# Add this line at the end of the file  
zend_extension=/usr/lib/php5/20100525/augmented_types.so  

 

在每个文件中使用ini_set方法同样可以启用扩展。

ini_set("augmented_types.enforce_by_default",1);  

 

如果你需要更多安装的细节,可以参考官方文档。https://github.com/box/augmented_types/wiki/Installation

开始使用

之前我们提到了这个扩展是使用了phpDoc的原型。它的作用可以在下面的实例代码中加以解释。

/** 
 * Add one 
 * 
 * @param   int $a 
 * @return    int 
 */  
function plusone($a)  
{  
    return $a + 1;  
}  
  
var_dump(plusone(1));  
var_dump(plusone("1"));  
var_dump(plusone("1 apple"));  

 

你可以猜猜上面的代码会输出什么,但是你很可能会猜错。

int(2)  
PHP Fatal error:  Wrong type encountered for argument 1 of function plusone, was expecting a integer but got a (string) '1' in /vagrant/test_at/test.php on line 15  

 

第二次调用方法甚至都不会被执行!产生这个错误是因为PHP并没有像之前一样帮我们做类型转换。函数中严格要求传入一个整型类型。现在,如果我们传入一个浮点类型的值呢?

var_dump(plusone(1.5));  
PHP Fatal error:  Wrong type encountered for argument 1 of function plusone, was expecting a integer but got a (float) 1.500000 in /vagrant/test_at/test.php on line 14  

 

现在我们使用定义一个符合类型让我们的函数接受两种类型的值(整型和浮点型)。

/** 
 * Add one 
 * 
 * @param   int|float $a 
 * @return    int 
 */  
function plusone($a)  
{  
    return $a + 1;  
}  
  
var_dump(plusone(1));  
var_dump(plusone(1.5));  

 

现在函数应该可以正常执行了。

int(2)  
PHP Fatal error:  Wrong type returned by function plusone, was expecting a integer but got a (float) 2.500000 in /vagrant/test_at/test.php on line 0  

 

出错了!其实我们同样应该设置函数的返回值类型。

/** 
 * Add one 
 * 
 * @param   int|float $a 
 * @return    int|float 
 */  
function plusone($a)  
{  
    return $a + 1;  
}  

我们同样可以使用符合类型:下面的例子使用的是数组的累加。

 

/** 
 * Calculate sum 
 * 
 * @param   array $nums 
 * @return    int 
 */  
function sum($nums)  
{  
    $sum = 0;  
    foreach ($nums as $value) {  
        $sum += $value;  
    }  
  
    return $sum;  
}  
  
var_dump(sum([10, 12, 76]));  
// output  
int(98)  

函数返回了期望的值。但如果数组中不只是包含整型类型的值呢?

var_dump(sum([10, 12, "something"]));  
  
// output  
int(22)  

 

扩展使得我们可以使用数组类型。这个例子中,如果在数组中传入了其他的类型值,将会发生一个致命错误。

/** 
 * Calculate sum 
 * 
 * @param   int[] $nums 
 * @return    int 
 */  
function sum($nums)  
{  
    $sum = 0;  
    foreach ($nums as $value) {  
        $sum += $value;  
    }  
  
    return $sum;  
}  
  
var_dump(sum([10, 12, 76]));  
var_dump(sum([10, 12, "something"]));  
int(98)  
PHP Fatal error:  Wrong type encountered for argument 1 of function sum, was expecting a (integer)[] but got a array in /vagrant/test_at/test.php on line 20  

 

如果我们想让这个函数接受任意类型的数字或者符合类型呢?这点我们也可以做到。

/** 
 * Calculate sum 
 * 
 * @param   *int $nums 
 * @return    int 
 */  
function sum($nums)  
{  
    $args = func_get_args();  
    $sum = 0;  
    foreach ($args as $value) {  
        $sum += $value;  
    }  
  
    return $sum;  
}  
  
var_dump(sum(10, 12, 76));  

 

*int 的定义使得我们的函数接受任何数字类型的变量。我们可以使用*int 和 int[]让我们的函数做到前面说到的问题。

/** 
 * Calculate sum 
 * 
 * @param   *int|int[] $nums 
 * @return    int 
 */  
function sum($nums)  
{  
    if ( !is_array($nums) )  
    {  
        $nums = func_get_args();  
    }  
  
    $sum = 0;  
    foreach ($nums as $value) {  
        $sum += $value;  
    }  
  
    return $sum;  
}  
  
var_dump(sum(10, 12, 76));  
var_dump(sum([10, 12, 76]));  

 

现在两次函数调用都会返回相同的值 (int(98)).

默认值

通常情况下,函数在接受参数之前都会做一些接受参数的初始化工作。我们可以使用void类型的定义来告诉扩展程序在这个变量不传入参数的时候也是可以的。

/** 
 * SSH to server. 
 * 
 * @param   string      $url 
 * @param   int|void    $port 
 * @return  bool 
 */  
function ssh($url, $port = 2222)  
{  
    return true;  
}  

提示: 当我们定义了默认参数并且传入参数可以为空的时候,扩展就不会强制判断类型。这就意味着当我们在port参数即使传入一个字符串类型的值也不会报错。

返回类型

之前我们也说到了,返回值的类型也应当被定义。每一个方法都应该有一个@return <type>定义,没有返回值的时候,应该定义@return void

class User  
{  
    protected $id;  
  
    protected $name;  
  
    /** 
     * Constructor 
     * @param int       $id 
     * @param string    $name 
     * @return void 
     */  
    public function __construct($id, $name)  
    {  
        $this->id = $id;  
        $this->name = $name;  
    }  
  
    /** 
     * Return the user info as a string. 
     * 
     * @return    string 
     */  
    public function __toString()  
    {  
        return $this->name;  
    }  
}  

忽略文件

通常情况下,我们的应用都会引入许多的类库,但是我们并不希望在这些类库引用的时候因为没有声明变量类型和返回类型而报错。但是在arguments_type扩展中,我们可以为文件和文件夹来设置相应的白名单和黑名单来解决这个问题。

augmented_types_blacklist([__DIR__."/vendor"]);

现在扩展就会忽略我们的扩展目录了。如果在扩展目录下有我们自己需要监听类型的文件,我们还可以在这个方法之后使用白名单来再次启用扩展。参考官方文档来了解更多关于白名单和黑名单的方法:https://github.com/box/augmented_types/wiki/Whitelisting-and-Blacklisting

augmented_types_whitelist([__DIR__."/vendor/mylib"]);

编辑php.ini文件同样可以达到上述效果,并且这是推荐做法。

# php.iniaugmented_types.whitelist=./vendoraugmented_types.blacklist=./vendor/mylib

PHP7的变化

PHP7在方法和函数中使用了标量类型声明和返回类型声明,从而消除了对参数和返回类型的验证。你可以在PHP官网中看到这些特性。

如果PHP7已经支持了这些特性,那我们安装一个新的扩展有什么好处呢?

扩展提供了一些PHP7没有提供的选项 :

  • 复合类型 (@param string|int $var)
  • 数组类型 (@param int[] $var)
  • 制函数返回值(at least void)

总结

升级PHP7并且享受它的新特性是一个不错的主意。然而,我们并不是总能在我们的项目中使用这些最新的技术。

Argumented types扩展肯定会延长我们的开发时间(你肯定也料到了)这就是为什么这个扩展经常被使用在部署和测试环境中来保证我们的产品服务干净、高效。

© 著作权归作者所有

共有 人打赏支持
rockman509

rockman509

粉丝 6
博文 20
码字总数 25462
作品 0
静安
程序员
PHP落伍了?Facebook的HHVM引擎改用Hack

  【IT168 评论】Facebook的Hip Hop虚拟机(HHVM)是一种PHP快速执行引擎,但是这次却没有继续沿用最新的PHP 7版本,而是将重点放在了PHP衍生工具――Hack上。Hack集合了PHP和Java两种语言的...

it168网站 ⋅ 2017/09/22 ⋅ 0

PHP7和HHVM的性能之争?

语言本身无分好坏,只是在各自使用的场景中解决不同的问题。互联网的时代车轮是很快的,随着移动互联网的到来,在短短四年多的时间里,移动端技术发展横扫全球。与此同时,各种语言群雄并起,...

旋转木马-千里马 ⋅ 2015/12/04 ⋅ 0

【专访 PHP 之父】PHP7 性能翻倍关键大揭露

20岁老牌网页程序语言PHP,最快将在10月底释出PHP 7新版,这是十年来的首次大改版,最大特色是在性能上的大突破,能比前一版PHP 5快上一倍,PHP之父Rasmus Lerdorf表示,甚至能比HHVM虚拟机下...

oschina ⋅ 2015/10/26 ⋅ 101

日请求亿级的QQ会员AMS平台PHP7升级实践

版权声明:本文由PHP7升级项目组原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/74 来源:腾云阁 https://www.qcloud.com/community QQ会员活动运营平台...

偶素浅小浅 ⋅ 2016/11/01 ⋅ 0

深入理解PHP7内核之Reference

08 Apr 18 深入理解PHP7内核之Reference 作者: Laruence( ) 本文地址: http://www.laruence.com/2018/04/08/3179.html 转载请注明出处 问题 上一章说过引用(REFERENCE)在PHP5的时候是一个标志...

Laruence ⋅ 04/08 ⋅ 0

在Qcon 2015 北京上的演讲PPT – PHP7

06 May 15 在Qcon 2015 北京上的演讲PPT – PHP7 作者: Laruence( ) 本文地址: http://www.laruence.com/2015/05/06/3007.html 转载请注明出处 不少同学对于PHP7, 还是了解的不多, 这次在Qco...

Laruence ⋅ 2017/10/21 ⋅ 0

PHP历史之3:秣兵历马的PHP7-王者归来

提起PHP7,就不得不提到源自FB的HHVM,据说安装了这种优化器,可以高效的PHP运行环境提升PHP性能9倍以上 当然PHP7已经超过了这种性能。 伴随着PHP 7的发布,这几天关于PHP 7性能和兼容性成了大...

ccpit2b2c ⋅ 2017/07/20 ⋅ 0

Modern PHP 笔记(三):部署测试和调优

系列笔记: Modern PHP 笔记(一):语言特性 Modern PHP 笔记(二):良好实践 第六章:主机 可选: 共享服务器 虚拟私有服务器VPS 专用服务器 PaaS 第七章:配置 目标:安装Web服务器,以便...

郝开心信札 ⋅ 2017/12/09 ⋅ 0

忘记 PHP!Facebook 的 HHVM 引擎将转用 Hack 语言

因为 PHP 7 与 PHP 5 存在很大的兼容性问题,Facebook 的 HHVM 团队决定改用 Hack 开发 HHVM。 Facebook 的 HHVM 是一个高速的 PHP 执行引擎,日前宣布将会逐渐摆脱对最新的主要 PHP 版本 —...

局长 ⋅ 2017/09/21 ⋅ 47

PHP 虚拟机 HHVM 发布 3.24.4,3.21.8 和 3.25.0

PHP 虚拟机 HHVM 已发布 3.24.4,3.21.8 和 3.25.0。 HHVM 3.25 版本包含新功能,错误修复,性能改进以及支持未来改进的工作。3.24.4 和 3.21.8 主要更新内容为 CVE-2018-6332 ,此外,不包括...

达尔文 ⋅ 03/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

BS与CS的联系与区别【简】

C/S是Client/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、InFORMix或 SQL Server。客户端需要安装专用的客户端软件。 B/S是Brower/...

anlve ⋅ 45分钟前 ⋅ 0

发生了什么?Linus 又发怒了?

在一个 Linux 内核 4.18-rc1 的 Pull Request 中,开发者 Andy Shevchenko 表示其在对设备属性框架进行更新时,移除了 union 别名,这引发了 Linus 的暴怒。 这一次 Linus Torvalds 发怒的原...

问题终结者 ⋅ 今天 ⋅ 0

在树莓派上搭建一个maven仓库

在树莓派上搭建一个maven仓库 20180618 lambo init 项目说明 家里有台树莓派性能太慢。想搭建一个maven私服, 使用nexus或者 jfrog-artifactory 运行的够呛。怎么办呢,手写一个吧.所在这个...

林小宝 ⋅ 今天 ⋅ 0

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 今天 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 今天 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部