Menu

tlanyan

十里平湖霜满天,寸寸青丝愁华年

关于操作系统的三篇好文分享

转载请注明出处:https://tlanyan.me/share-three-posts-on-os/

我承认,对操作系统的底层运行原理一直是好奇的。但是市面上的大部分教材,千篇一律的从操作系统管理的各个部分大谈设计之道,风格沉闷乏味。对于我这种工作在应用层,缺乏操作系统基础(尤其是硬件操作)的人来说,一篇深入浅出讲解某个点,启发你独立思考的博文会是非常棒的学习资料。

昨天在朋友圈看到某位朋友分享的关于操作系统的文章,感觉十分入味。顺着原链找到原作者的博客,发现此篇文章写于2014年,近期才被翻译成中文。接着查看作者其他的文章,找到了另外两篇都关于操作系统的好文。本来有翻译的想法,保险起见先查查是否已有中文版,发现都已经翻译成中文(最近一篇是2月1日翻译完的)。

前人已经打好基础,我等只需好好学习。先说一下作者信息:三篇博文的作者都是Gustavo Duarte,其个人博客地址是https://manybutfinite.com(原博客链接http://duartes.org会自动跳转到新网站)。

本文是对这篇博文的分享,希望对需要的人有帮助。

第一篇: 操作系统何时运行?原文链接:https://manybutfinite.com/post/when-does-your-os-run/,写于2014年10月28日,中文版链接:https://linux.cn/article-9095-1.html

第二篇: 当CPU空闲时它都在做什么?原文链接:https://manybutfinite.com/post/what-does-an-idle-cpu-do/,写于2014年10月29日,中文版链接:https://linux.cn/article-9303-1.html

第三篇: 系统调用让这个世界运转。原文链接: https://manybutfinite.com/post/system-calls/,写于2014年11月6日,中文版:http://blog.csdn.net/monkeynote/article/details/45771121

php://output和php://stdout的区别

转载请注明文章出处:https://tlanyan.me/php-output-vs-stdout/

PHP包含了以php://开头的一系列输出输出流,如php://stdin, php://stdout等。今天查看代码时,忽然想到一个问题:php://output和php://stdout有什么区别?

从PHP的官方文献中找答案,对输入流php://stdin和php://input的解释分别如下(输出流的解释过于简略):

php://stdin

php://stdin, php://stdout and php://stderr allow direct access to the corresponding input or output stream of the PHP process. The stream references a duplicate file descriptor, so if you open php://stdin and later close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected. Note that PHP exhibited buggy behavior in this regard until PHP 5.2.1. It is recommended that you simply use the constants STDIN, STDOUT and STDERR instead of manually opening streams using these wrappers.

php://stdin is read-only, whereas php://stdout and php://stderr are write-only.

php://input

php://input is a read-only stream that allows you to read raw data from the request body. In the case of POST requests, it is preferable to use php://input instead of $HTTP_RAW_POST_DATA as it does not depend on special php.ini directives. Moreover, for those cases where $HTTP_RAW_POST_DATA is not populated by default, it is a potentially less memory intensive alternative to activating always_populate_raw_post_data. php://input is not available with enctype=”multipart/form-data”.

文档并未直接阐述两者的区别,仔细对比可得出以下信息:1. 均是只读流; 2. php://stdin是PHP进程的标准输入,php://input用来读取请求正文的原始数据。通过这些信息,该如何正确认识两者的本质区别?

顺着php://stdin进程输入的提示,联想PHP进程的执行过程,再结合SAPI的差异,可以得到两者主要区别:php://stdin是PHP进程的输入流,执行生命周期内均可能有数据流入(例如CLI下的交互式输入);php://input是PHP执行时的外部输入流,一般数据只能读一次(具体看SAPI的实现)。同理可得到php://stdout和php://output的区别:php://stdout是PHP进程的标准输出流,php://output是返回的结果数据流。

下面用代码验证结论:

命令行执行文件,输出如下:

浏览器端请求,输出如下:

在命令行下,PHP进程的标准输出流和结果输出流均指向终端,所有消息都打印出来。在浏览器端,PHP进程的输出流被忽略,只有结果数据流被发送到web服务器。同时,print和echo调用的信息都作为执行结果发往结果输出流,所以都正常显示。

最后再感慨一下PHP内置函数的简洁实用,一个file_put_contents函数就搞定流写入操作,换Java需要stream/writer一堆代码,也省去C风格的fopen/fwrite/fclose的繁琐。

参考

  1. http://php.net/manual/en/wrappers.php.php

使用PHPExcel读写excel

转载请注明文章出处:https://tlanyan.me/use-phpexcel-to-read-and-write-excel/

PHPOffice出品的PHPExcel是PHP读取和生成Excel的极佳工具。本文参考官方文档,对PHPExcel进行简要总结,希望对使用PHPExcel操作Excel的同行有帮助。

PHPExcel介绍

PHPExcel是用PHP实现的电子表格文档读写类库,其支持的文档类型包括:Excel(.xls)后缀,Excel 2007(.xlsx后缀),CSV(.csv后缀),LibreOffice Calc(.ods后缀),PDF和HTML等格式(某些格式只能读)。PHPExcel运行环境为PHP 5.2+,需要开启php_zip、php_xml和php_gd2拓展。

细心的读者可能看到PHPOffice有另外一款作品:PHPSpreadsheet。PHPSpreadsheet也是一个Excel读写类库,与PHPExcel主要区别是:

  1. PHPSpreadsheet是PHPExcel的重构版,基于PHP的新特性进行了重写。PHPSpreadsheet要求PHP 5.6+,使用了名字空间、PSR2编码规范、最新的PHP语言新特性;
  2. 对PHP版本的要求加强。官方的PHP版本支持结束后,PHPSpreadsheet对该版本至多额外支持6个月(意味肯定不支持PHP 5.5及以下版本,PHP5.6的支持也即将终止)。对比之下,PHPExcel依然支持PHP 5.2.0;
  3. 开发组已将所有资源转移到PHPSpreadsheet,PHPExcel的维护已经停止。

PHPSpreadsheet已经放出1.0.0稳定版,官方不再建议使用PHPExcel。本文内容主要讲解PHPExcel,掌握透彻后再转换到PHPSpreadsheet也是很容易的。

PHPExcel架构

理解PHPExcel的架构,可以先从理解Excel文件的结构开始。一个Excel文件包含多个表单,每个表单包含多个单元;文件、表单和单元都可以单独设置属性。这些概念对应到PHPExcel中的类,关系如下:

  • PHPExcel类 < -> Excel文件
  • PHPExcel_Worksheet类 < -> 表单
  • PHPExcel_Cell < -> 单元
  • PHPExcel_DocumentProperties < -> 文件属性
  • PHPExcel_Style_* < -> 格式设置类

下面开始介绍PHPExcel的常用操作。

使用PHPExcel

根据上面介绍的关系,分excel文件、表单、单元、格式设置四个部分分别介绍PHPExcel的使用方法。

excel文件

一个PHPExcel类的实例代表一个excel文件。新生成的PHPExcel对象,经常需要保存为文件;反之excel文件常需要导入为PHPExcel实例。保存和导入的行为分别由writer和reader负责。为了正确导入和保存数据,reader和writer需要知道具体的文件格式。PHPExcel提供了工厂类PHPExcel_IOFactory简化reader和writer的创建。读写文件的示例代码如下:

如果知道具体格式,可以使用具体的类操作:

可用的reader和writer类可以参考下图:

enter image description here

建议使用工厂方法读取文件,它能自动探测文件格式并加载。这在读取用户上传不同格式的文件时很有用,避免了格式与后缀名不符可能导致的错误。

注意不要混淆PHPExcel和writer/reader对象:PHPExcel持有数据,writer和reader是对其进行序列化和反序列化的辅助类。

表单操作

一个excel文件可以包含多个表单,常用操作包括读取、新建、复制和删除表单。表单从属于excel文件,一般需要挂载到具体的PHPExcel对象上。

获取表单的方式有多种,如获取当前表单、获取指定顺序表单、根据名字获取表单。以下是示例代码:

创建表单分为直接excel文件对象直接创建,也可以先创建表单实例,后续再关联。对应方法为:

PHPExcel也支持复制表单(包括复制其他PHPExcel对象中的表单):

删除表单的API比较简单,只提供了removeSheetByIndex一个方法:

单元操作

单元是承载内容的主体,其上操作比较复杂,大部分的类和API都与单元相关。单元隶属于具体的表单,使用上和表单类互动最多。

常用操作的包括定位、取值/赋值、格式化等。下面是一些代码示例:

文件属性

设置excel文件的属性,包括常见的作者、标题、创建时间、描述等。该功能由PHPExcel中类型为DocumentProperties的成员变量负责:

其他

上述介绍了常见的概念和操作,实际中可能会用到的概念还包括:

  • 缓存和性能
  • 图像、图表、超链接等富文本
  • 日期、货币等格式化和本地化
  • 公式设置
  • 打印属性设置
  • 内容对其、边距设置等
  • 文件密码安全设置

这些冷门或高级功能可以参照API文档。

PHPExcel官方文档可能稍有繁杂,网络上的二手资料在深入方面常有欠缺。要用好PHPExcel,一个基本功是搞清楚操作的对象,以及和其他类/对象的关系(这也是面向对象编程的基本功)。本文中提到的PHPExcel->PHPExcel_WorkSheet->PHPExcel_Cell继承体系,是使用过程中操作最为频繁的对象,希望以上说明和示例能加深读者对PHPExcel类库的理解。

参考

  1. https://github.com/PHPOffice/PHPExcel

Yii2中的事务

转载请注明来源:https://tlanyan.me/yii2-transactions/

今天运行程序时发现有条数据不完整。出现问题的数据属于某个事务,按道理要么逻辑走完数据提交,要么回滚。出现预料外问题,第一个反应是ActiveRecord中内嵌事务会单独提交到数据库中?为了验证这个问题,抽空写了一个测试用例验证。

准备工作

先建立两个表 foo1foo2

创建相应的ActiveRecord类,并定义好规则:

编写测试用例

为了彻底搞清楚Yii2中事务的执行情况,总共编写了六个例子。六个示例的作用分别是:

  1. 非事务保存、数据校验不通过
  2. 事务保存、数据校验不通过
  3. 校验通过、多模型数据保存
  4. 某条数据校验不通过
  5. 某条数据插入冲突
  6. 事务执行中exit/return

测试例子的代码如下:

执行结果

依次执行上述测试用例,结果如下:

  • case1: 输出”transaction committed”,数据未插入;
  • case2: 输出”transaction committed”,数据未插入;
  • case3: 输出”insert data error:SQLSTATE[23000]: Integrity constraint violation:1062 Duplicate entry ‘12345678’ for key ‘data1’
    The SQL being executed was: INSERT INTO foo1 (data1, value) VALUES (‘12345678’, ‘1245677553’)”,数据未插入;
  • case4: 输出”transaction committed”,foo1中的数据成功插入;
  • case5: 输出”insert data error:SQLSTATE[23000]: Integrity constraint violation:1062 Duplicate entry ‘12345678’ for key ‘data2’
    The SQL being executed was: INSERT INTO foo2 (data2, value) VALUES (‘12345678’, ‘1245677553’)”,数据未插入;
  • case6: 输出”exit now”,数据未插入。

分析

大部分示例的结果在预料之中,震惊的是case2和case4的结果。之前一直以为只要包裹在事务中,并且在transactions方法中声明了所在场景启用事务,数据保存出错就会抛异常,数据回滚。这个测试彻底颠覆了我的认知。

为了搞清楚执行机制,开始跟踪Yii2执行数据保存的源码。首先查看ActiveRecord基类BaseActiveRecord中的save方法:

save方法根据是否新数据,走插入或更新流程。继续跟踪insert方法(定义在yii\db\ActiveRecord中):

insert方法的实现代码解决了我的疑问:数据的规则验证不通过,直接返回false,不会抛异常。

再看保存过程:如果当前场景未声明事务,常规保存;事务保存第一步还是尝试常规保存,如果失败,回滚并抛出异常;如果事务保存成功,提交事务。

到这一步,Yii中事务处理已经比较清晰了。剩下的问题是:嵌套事务如何处理?继续跟踪yii\db\Transaction中的commit方法:

代码中出现事务的层级(level),结合begin方法,每嵌套一层事务,level加一并创建savepoint。事务提交时,如果是最外层事务,直接提交到数据库;如果是内嵌事务,释放savepoint或什么都不做。所以嵌套事务的疑问也解决了:内嵌事务不会单独提交。

总结

通过这次测试和源码跟踪阅读,对Yii的事务了解又深入一步。最大的收获是:事务开始前调用validate方法先校验数据,无错误时再通过事务中调用save(false)方法插入数据,此时出错才会抛出异常。

升级Windows 10遇到的坑和解决办法

转载请注明来源: https://tlanyan.me/windows-10-problems/

前几天测试巨硬的“辅助技术升级通道”是否关闭,把常用的笔记本电脑的升级到了Win10 Pro。除了界面炫酷一点,其他操作感觉不如Win7用得顺手,尤其是花里胡哨的开始菜单。好的功能自己体会,这里说说这几天使用过程中遇到问题和解决办法。

第一个是外接高分辨率显示器上字体太小和模糊。两个显示器是码农高效工作必备工具,Win10这种搞法首先会凉了码农的心。虽然网上一堆抱怨的帖子,但看情况巨硬暂时不着急解决这个问题。试过在显示设置中把文本缩放比例放大,效果不理想:比例小字体也小,看的吃力;比例大外接显示器浏览网页时会出现滚动条,非常别扭。从知乎上找到解决方案:使用Windows 10 DPI blurry fix这个工具修复设置。

第二个是vagrant和VirtualBox不好使了!这两个是本地开发必备工具,还是那句话:巨硬这样搞会凉了码农的心!无论用vagrant还是VirtualBox打开虚拟机,都出现“E_FAIL (0x80004005) Component: ConsoleWrap Interface: IConsole{872da645-4a9b-1727-bee2-5585105b9eed}”类似的错误提示。用vagrant version查看了一下版本,发现有新版的vagrant。出事当天有开发任务在身,没时间去处理。昨天事情忙完后,将VirtualBox和Vagrant升级到最新版(VirtualBox迟迟不升级是Vagrant既没有新版,也不支持高版本的VirtualBox)。问题解决!

第三个是不知道按了什么键,系统所有画面颜色突然全部变灰。刚开始以为是触发了色温调节软件f.lux的快捷键,把f.lux关掉后问题依旧,原因定位到系统上。自己尝试触发操作的可能组合,无济于事。上网找到win10系统忽然变灰黑色怎么办?关闭win10颜色筛选器的方法这篇文章,问题解决。处于好奇,在Win7系统上测试,ctrl+win+c的快捷键无特殊作用。暂时不知道巨硬搞出这个快捷键的意义何在。

半年前用过两天Win10 Home,直接放弃到Win7。就目前使用而言,Win10 Pro尚可。虽然有一些问题,庆幸的是都能顺利解决。

参考

  1. https://www.zhihu.com/question/34304515
  2. http://windows10_dpi_blurry_fix.xpexplorer.com/
  3. http://www.winwin7.com/JC/5625.html

关于“清屏网”抓取本站文章的声明

几天前搜索一个问题,发现清屏网上有本站的文章。让人气愤的是,清屏网将文章以“清屏网”的名字堂而皇之地刊登出来,完全抹去作者信息和原文链接。本站部分文章不时被一些内容站收录,但至少会给出原作者或原文链接。像清屏网这样赤裸裸的盗取他人成果,还真是刷新了三观。

如此恶劣的行为,应该很多人声讨。网上查了一下,只有“编码无悔”旗帜鲜明地站出来表态。作为原创博主,为“编码无悔”的侵权声明点赞:不尊重他人劳动成果的垃圾网站就应该被共同唾弃并消失。

气愤之余,顺手查查哪些文章被窃取。目前查到的多为技术类文章,以PHP为主(本站PHP相关文章应该最多)。例如去年写的“PHP回顾之XXXX”,新发表的“Yii2的场景(scenario)和验证规则(rule)”等;一些小众的如“使用frp转发内网端口”、“CentOS 7开启BBR”等也以“清屏网”的名义出现在他们的网站上。

本着分享的目的,本人鼓励在“注明作者和出处的”的前提下转发和转载。清屏网抹去作者信息和来源的行为让我非常鄙视,因为这种做法让原创作者和读者都受到潜在的伤害:

  • 作者层面:窃取他人成果,打击原作者的创作积极性;作者不能与文章的阅读者直接沟通和交流。
  • 读者层面:原作者对文章的一些更新和修正,无法从这些站点上看到;对系列性的文章,如果不抓取就无法完整阅读,也失去了发现优秀原创作者的机会。

作为防御措施,今后本站将在所有新发表的博文上加上转载声明。本站内容欢迎注明出错的站点和搜索引擎转载和收录,拒绝并抵制类似于清屏网这种盗取成果站点的抓取。

参考

  1. [声明] 关于“清屏网”(www.qingpingshan.com) 侵权本站原创文章的声明

Yii2的场景(scenario)和验证规则(rule)

转载请注明来源:https://tlanyan.me/scenario-and-rule-in-yii2/

和用户有交互的系统必不可少的功能包括收集用户数据、校验和处理。实际业务中,往往还需要将数据进行持久化存储。出于安全考虑,开发人员应当牢牢把握“客户端的输入都是不可信”的准则,客户端传过来的数据先进行过滤和清洗后再存储或传递到内部系统。

Yii2推荐使用Model类来收集和校验用户数据,持久化的ActiveRecord类是其子类。Model类的load和validate两个方法,分别用来收集和校验客户端数据。哪些数据应该被收集,哪些数据需要在什么场景下验证,便是本文的主题:场景(scenario)和验证规则(rule)。

系统结构

先引入一个简单的业务系统:系统中存在学生和教师两种角色,数据库中使用了三张表保存角色信息:

user: [id, username, password, status, 其他通用属性]

student: [id, user_id, student_no, grade, class, 其他学生属性]

teacher: [id, user_id, work_no, title, telphone, 其他教师属性]

实际业务不限于对这三张表的增删查改操作。为了简化问题,后续仅讨论user和student两张表的数据变更(给出teacher表是为了让读者不认为设计数据库的人是脑残:明明可以放到一张表的,为什么要拆开!)。

学生报名

学生报名是典型的增删查改操作,送分题。学生报名的简要代码示例如下:

相信有Yii2使用经验的人都能根据数据库的字段约束快速的把User和Student类的rules方法写出来。例如User类文件内容可能如下:

定义数据的验证规则,这是大多数人对rules的第一印象,并且是一个很好的印象:它打回非法的数据,让正常的数据进入系统中。安全的实践应该尽量定义完整的规则,充分验证数据。也建议每一个Yii2开发人员对内置的核心校验器熟悉。

修改信息

修改信息,也是典型的增删查改操作。实现代码和报名差别不大,这里仅讨论两点:

  1. 用户密码的验证

    注册时会校验用户密码是否8-16位,密码的规则可能是: ["password", "string", "length" => [8, 16]]。明文保存密码是不可取的,插入数据库时至少会做MD5加密,password变成32位。假设用户修改信息时未修改密码,再次保存时密码规则校验出错(长度不符合),无法保存!

    怎么解决这个问题呢?翻阅Yii文档,发现了规则中的when属性可以救场。一种可能的验证规则是:

只有在注册(新增数据)时才校验密码字段。问题解决,完美!

  1. 防止用户私自改密码

    假设有个小聪明的家伙(例如汤姆),发现系统是用Yii框架做的,想搞点小破坏炫耀一下水平。在发送修改信息的表单时,汤姆增加&password=12345678这一段数据。系统使用$user->load($data)收集用户输入,更新password字段,带来如下后果:rules设置更新时不校验密码字段,12345678直接作为password的值保存到数据库中。这个操作带来连锁反应:用户再次登录时,加密过后的密码与数据库中的明文密码不匹配,导致汤姆无法登录系统。烦人的是汤姆是个刺头,登录不上后整天骚扰客服,不省心!

    怎么样防止这种情况出现呢?一种解决的方法是阻止修改密码:

把用户输入的密码过滤掉,私自修改密码的问题就解决了。

但是问题还没有结束:汤姆可以转向修改其他字段,比如说性别,身份证等。更严重情况是修改student中user_id,就可以更改任意学生的信息。事情十分严重,需要马上修复漏洞。

可以按照密码的方法,逐个屏蔽受保护属性,但显得啰嗦难看(虽然好使)。如果受保护属性多,可以仅允许白名单进入,具体操作为:新增一个UpdateInfoForm类继承Model,属性是白名单属性合计。用UpdateInfoForm类过滤用户数据,校验通过后再更新到user和student中:

这种方式更优雅,但仔细一想代价不小:属性和验证规则要重复写一遍;user和student保存时又重复校验属性。这种方式看起来优雅,实际上却冗余又低效。

scenario的登场,完美的解决解决上述问题。

场景(scenario)

分析上面问题,会发现关键点是批量赋值(massive assignment)和数据校验(validate)两个方法。如果对不同的场景指定赋值字段和检验规则,问题就迎刃而解。

Yii中的scenario有安全属性活跃属性两个概念。安全属性用在批量赋值的load方法,只有安全属性才能被赋值;活跃属性用在规则校验的validate方法,在活跃属性集中并且定义了校验规则的属性才会被校验。活跃属性和安全属性的关系是,安全属性是活跃属性的子集。

\yii\base\Model类定义了默认场景:SCENARIO_DEFAULT(值为default)。默认场景下,出现在rules方法中的属性既是活跃属性,又是安全属性(这句话基本正确,看后续解释)。为不同场景指定活跃属性、安全属性以及校验器,可以通过覆盖senarios或rules两个方法实现(几乎每个Model类都会重写rules方法,senarios用得少)。

rules

先看rules方法。默认的属性加校验器定义方式,让每个属性既是安全属性,也是活跃属性。如果想让某个属性不是安全属性(不能通过load批量赋值),在属性名前加感叹号!即可。例如student中的user_id字段:

user_id是活跃属性,在写入数据库时会被校验。但它不是安全属性,不能通过load方法进行赋值,解决了安全隐患。

再看rules方法按场景区分校验器规则的做法:定义校验器时on属性指定规则在哪些场景下生效,except属性则排除一些场景(如果不指定on和except,规则对所有场景生效)。例如:

在原来基础上新增感叹号和on/except属性,非常简便的就定义了非安全属性以及分场景指定校验规则。

scenarios

另外一种更清晰定义安全属性和活跃属性的做法是重写scenarios方法。scenarios方法返回一个数组,数组的键是场景名称,值是活跃属性集合(包饭安全属性)。例如student表的可能实现如下:

默认情形下(学生报名),年级、班级这些信息是安全属性,但user_id不是,只能在程序内部赋值,并在插入数据时被校验;在修改信息时,user_id不是活跃属性,既不能被批量赋值,也不需要校验(事实上它不应该改变)。

scenarios方法只能定义活跃属性和安全属性,无法定义校验规则,需要和rules配合使用。

总结

  1. 尽可能定义完善的数据校验规则
  2. 业务复杂时定义多个场景,仔细为每个场景定义安全属性和校验规则
  3. 优先使用rules;属性较多、rules复杂时,可以配合scenarios方法迅速理清安全属性和活跃属性

参考

  1. http://www.yiiframework.com/doc-2.0/guide-input-validation.html
  2. http://www.yiiframework.com/doc-2.0/guide-structure-models.html
  3. http://www.yiiframework.com/doc-2.0/guide-db-active-record.html

PHP函数类型声明总结

转载请注明出处:https://tlanyan.me/argument-type-declare-in-php/

PHP7开始支持标量类型声明,强类型语言的味道比较浓。使用这个特性的过程中踩过两次坑:一次是声明boolean类型参数,最近一次是声明double类型参数,都导致运行时出错。为避免以后继续犯类似错误,这几天翻阅了官方文档。本文是看完后对PHP函数的类型声明使用做的总结。

从语法上,PHP的函数定义经过了几个时期:

远古时代(PHP 4)

定义一个函数非常的简单,使用 function name(args) {body}的语法声明。不能指定参数和返回值类型,参数和返回值类型有无限种可能。这是到目前为止最常见的函数声明方式。

数组和引用类型参数值声明(PHP 5)

数组(array)、类(class)、接口(interface)、函数(callable)可以用在函数声明中。从5.6开始,支持常量(包括类常量)为默认参数,以及参数数组(以省略号…为前缀)。例如:

注意:如果参数的值可能为null,null必须为参数的默认值,否则调用时会出错。例如:

标量类型和返回值声明(PHP 7)

函数正式支持标量类型(int, bool, float,string)和返回值类型(可声明类型同参数)声明。从这个版本开始,除了语法差异,函数声明形式上可以做到像强类型语言。

遗憾是如果函数返回值有可能是null,就不能指定返回值类型。例如:

参数和返回值可为null以及void返回类型声明(PHP 7.1)

当参数和返回值类型有可能是null时,类型前以问号(?)修饰,可以解决null值问题(与默认参数不冲突);类型声明新增iterable,同时还支持void类型返回值。例如:

当函数返回值为void时,函数体的不能return任何东西(return void;的写法也是错误的!),或者可以省略return语句。

回顾以上历史变更,可以看到在PHP 7.1中函数类型声明已经十分完善(虽然实践中用的不多)。注意,文章说的是参数和返回值类型声明,PHP不保证运行过程中参数类型不变,即下面的代码是合法的:

从这点上看,PHP还是弱类型语言,不能做静态编译。

再说说实践中踩到的坑。根据官方文档,函数参数和返回值类型声明可用的类型有:

  1. 类/接口
  2. self,只能用在自身的方法上
  3. array
  4. bool
  5. callable
  6. int
  7. float
  8. string
  9. iterable
  10. void(仅用在返回值)

注意列表中并没有boolean和double类型!除非你定义了这两个类型,否则用在参数和返回值中是错误的!

这也是PHP有点蛋疼的地方。平常使用时的double和float两个关键字几乎等同,例如doubleval是floatval的别名,is_double是is_float的别名,转换时用(double)和(float)效果相同。但在用在类型声明就不一样,同样的情况出现在bool和boolean身上。

总结

目前PHP 7.2稳定版已经发布,建议在新项目中尽量使用PHP 7.1及后续版本。为了写出清晰和可维护的代码,推荐声明类型。建议引用类型或者string才使用null值,int/float等标量类型的参数尽量不要用null。func_get_argc等函数,如非必要,尽量不使用。

参考

  1. http://php.net/manual/en/functions.arguments.php
  2. http://php.net/manual/en/migration70.new-features.php
  3. http://php.net/manual/en/migration71.new-features.php

处理Gitlab升级时的错误

CentOS软件降级这篇文章中提到过升级Gitlab后,出现node_exporter和postgres_exporter两个服务无法启动的问题,会导致web版的Gitlab不能正常使用。几天后有外国友人(如何能看懂中文,这是个谜~_~)在博客下评论说使用gitlab-ctl reconfigure命令,重新配置Gitlab,能解决问题。

今天更新Let’s Encrypt证书,顺带把机器的软件也升级一下(近期爆出的Meltdown和Spectre两个CPU漏洞挺严重)。果不其然,升级到Gitlab 10.3.3版本后,同样出现node_exporter和postgres_exporter不能启动的问题,日志中记录的问题也相同。

既然有外国友人说gitlab-ctl reconfigure好使,现在就是验证的绝佳机会。运行gitlab-ctl reconfigure,发现毫无反应!WTF!在Google上找,没靠谱的方案;用gitlab-ctl tail查看日志,没有任何输出;用dmesg查看,毫无结果;最终摸索到/var/log/gitlab/reconfigure下,找到了问题:8889端口被占用!

解除端口占用后,再次运行gitlab-ctl reconfigure,信息像模像样的输出。好事多磨,很快出现了错误:PG::ConnectionBad: could not connect to server: No such file or directory。上网查了一圈,发现这篇文章的解决方案有点像。遗憾的是,按照文章中的说法,不能解决问题。无奈之下,发现有gitlab-psql命令,运行gitlab-psql help,输出和gitlab-ctl reconfigure的类似:Is the server running locally and accepting connections on Unix domain socket “/var/opt/gitlab/postgresql/.s.PGSQL.5432?这个错误输出就比较清晰:postgresql没有运行。

运行gitlab-ctl start postgresql,再执行gitlab-ctl reconfigure。postgresql的错误顺利解决,接着出现redis的错误:No such file or directory – connect(2) for /var/opt/gitlab/redis/redis.socket。参考postgresql的问题,解决起来也非常轻松:先执行gitlab-ctl start redis,然后再执行gitlab-ctl reconfigure。配置成功!执行gitlab-ctl restart,各项服务均正常启动!浏览器打开Gitlab站点,正常!

外国友人诚不我欺。

2017年总结

转眼2017年已过,先做一个简要总结。

工作方面,主要是负责公司和其他项目的技术。总体表现及格,缺点在于事情杂乱导致经常奔波救火,没有从更高一层看问题并把事情分类好。行动上过于谨慎,比如从公司省成本考虑,对招人这一块不是很重视(绝大部分的活我自己都能应付)。

技术方面,对java熟悉更多的目的达到。缺点是练手太少,尤其是多线程和并发这一块,没有具体的实践。一直在做的PHP,理解上更为深入,目前在琢磨分层和设计模式,以及多编写测试用例。对JS的理解有了质的改变,原因是前端选用React,并完全采用ES2016语法。

输出方面,博文数量比去年多不少。遗憾的是事情多加偷懒,PHP回顾系列只写了一半。原创微博较少,风格尚未形成。

阅读方面,今年对阅读更重视,看书的数量比去年多很多,类型更丰富。

2017,是焦虑的一年,也是成长的一年。国内环境的变化,带来更沉闷的气息;AI和各种加密货币的火热,让场内玩家更狂热,场外玩家更眼红。人到中年,网上说的油腻和压力山大暂未感觉,但已心生警惕。回过头看,看似前景无限的东西,并没有那么美好;犹豫不决的事情,现在看来更有宿命的意味。

2018年计划在这些方面多努力:

  1. 阅读更多书籍;
  2. 提升博客数量和质量;
  3. 加强健身锻炼;
  4. 更合理的安排时间。

提了提升逼格外,主要目的是让自己成为更有价值的人。努力,奋斗!

Scroll Up