写了这么久PHP,在心血来潮在StackOverflow上回答了别人PHP的问题,没想到被扣分还说没常识,求研判。

2013-04-29 12:40:51 +08:00
 raincious
这是我回答的地址:
http://stackoverflow.com/questions/16262944/emailing-an-array-from-html-form-via-php/16263381#16263381

之前总是通过Google得到StackOverflow的搜索结果,于是昨天就注册了,正好遇到别人提的问题我知道,于是就回答了,但是。。。。但是啊。。。就是在补充一点意见的时候,某个家伙。。就这么给我的回答扣分了。。。。感觉很悲剧。

但是还是想知道我这样处理是不是正确,所以发上来希望大家讨论下。

具体是这样的,我认为在PHP里,在楼主那情形下不需要isset,关掉E_NOTICE的警告就好了。但那家伙认为,我这样做违背了常识,(意思似乎说error_reporting(E_ALL)万岁?),但是我觉得不对。

所以想问问大家具体都是怎么处理变量未初始化的?

我先来说说我,一般都是习惯于先初始化好,不可能存在不初始化的变量,也就不需要isset了:

function($input) {
$sA = $sB = '';
$iA = $iB = 0;
$aA = $aB = array();

// do your code here
}

大家呢?
10775 次点击
所在节点    PHP
48 条回复
python
2013-04-29 12:57:29 +08:00
用E_ALL,写好了,各个环境都能通过...

对于用户的输入,要用isset处理, 程序不应该改变用户的原输入,即使是恶性的.
AlloVince
2013-04-29 13:04:38 +08:00
关掉E_NOTICE不是一个好习惯,正是因为写法中可能存在隐患,所以才会有NOTICE报出,php稍微注意一下完全可以写出通过E_ALL的代码。这样对项目对自己都有好处。

变量未初始化的问题

如果在功能函数中,提前声明变量设默认值即可。

如果在模板引擎以及一些处理大批量参数的场景,最好用OOP处理,活用__set(),__get(),__isset()即可
raincious
2013-04-29 13:15:44 +08:00
LS们真的用E_ALL?

我一开始学PHP的时候也是用E_ALL,一个页面打开,上百个isset调用,主要在数组处理上。

我实在没办法理解为什么程序员要将安全处理什么的交给解析器。

另外,isset跟安全处理没什么直接关系。他不能帮你判断用户输入是不是你想要的。一般人也主要用来防止Global Variable污染。

但是防止Global Variable污染你可以在函数或者什么地方声明啊,没事调用isset做什么呢?

另外,根据你们的说法,如果类似我这样保证变量使用之前都提前声明的话,就不需要开E_NOTICE了吧?

此外,我确实不相信有人的程序是error_reporting(E_ALL),你们的开发组真的是这样开发程序的么?那么你们其他意外怎么处理,比如file_get_content一个不存在的文件,这可是E_WARNING啊,如果不屏蔽,报错给用户不说,你们的HTTP Header不是会很糟。

如果为了file_get_content不报错,得file_existe吧?还得is_file吧?那么就3个IO操作了,值得么?

感觉E_ALL只是对新学PHP的人的一种手段而已。
chenz
2013-04-29 13:21:05 +08:00
1. 不应该关掉notice警告。如果你关掉notice,那么当一个需要set的变量没有set的时候,你可能无法察觉。但是所有的警告都应该写入log,而不是打印出来(至少生产环境)
2. 你应该用isset检查。不一定是在Yes那一行,可能是在更之前做这个isset检查。但是无论什么情况都应该做这个检查


你虽然习惯初始化,但是你不能保证你每次都记得初始化,你也不能保证团队里每个人都记得初始化



话说,我怎么感觉我回到了10年前的那些PHP论坛和邮件组?当年我们就是在频繁讨论这些话题。虽然这10年来PHP技术有长足的发展(匿名函数、composer、psr等),但是isset该如何使用,notice是否应该关掉,我想不会有太多变化吧
chenz
2013-04-29 13:28:15 +08:00
>> 此外,我确实不相信有人的程序是error_reporting(E_ALL),你们的开发组真的是这样开发程序的么?那么你们其他意外怎么处理,比如file_get_content一个不存在的文件,这可是E_WARNING啊,如果不屏蔽,报错给用户不说,你们的HTTP Header不是会很糟。

我的所有代码都是在E_ALL下写的,有notice我会马上修正。

file_get_content的问题,当然是if (file_exists($f) && file_readable..)才file_get_contents。

另外,就算没有做上面的判断,也不会把错误报告给用户。因为生产环境下当然会把所有warning写入服务器的log file,而不是打印出来。用户永远看不到这些错误报告。如果你们团队没有这样做,我可以说你们的团队真的还需要很多磨练
skey
2013-04-29 13:42:26 +08:00
error_reporting(E_ALL) +1
Js
2013-04-29 13:45:39 +08:00
@raincious 针对性能方面质疑的前提就错了, php屏蔽错误(包括error_reporting和@)是需要额外开销的, isset($var) && $var==='xxx' 性能比 $var==='xxxx'高多了, 同理对于一个无法确定是否存在的文件, is_readable($filename) && include($filename) 也比 @include($filename)高多了. 不信的话可以做下benchmark。 关闭日志是为了在生产环境更安全一点(万一一个不可控的情况爆了些密码出来怎么办?), 平时开发应该把警告当错误看的
raincious
2013-04-29 14:04:43 +08:00
我觉得各位还是没有考虑好这个问题,或者最好能举个例子说明楼主那个==YES的if如果再没初始化的时候会有什么危害。

当变量未初始化
return $_REQUEST['visitor'] == 'Yes' ? true : false; // False

当变量初始化并等于其他值
return $_REQUEST['visitor'] == 'Yes' ? true : false; // False

当变量初始化并等于Yes
return $_REQUEST['visitor'] == 'Yes' ? true : false; // True

当变量未初始化并等于Yes // 未初始化如何等于?
/* */


可见情况是一致的。

另外,就算是为了防止全局变量污染:
http://www.php.net/manual/en/ini.core.php#ini.register-globals

PHP 5.4.0之后就没有自动全局变量注册了。于是乎,抱歉,使用这条利用来进行isset已经不靠谱了。

好吧,我整理下我的观点,基本不变:就变量是否初始化来打开E_NOTICE会增加不当开销,建议调试时打开,开发时写入次要日志,生产时关闭且不输出。

当然E_NOTICE还有其他错误,但在我看来,这跟程序员水平有关。如果为了安全,不应该让程序员来承担不必要isset的义务,而应该在核心上避免isset的需要(当然,不是完全禁止isset)。如果你们不明白,可以参看我3年前写的代码:

https://code.google.com/p/faculaframework/source/browse/trunk/include/inc.initializer.php#66

@Js 此外根据性能问题,我特别进行了测试,观点仍然是我不做变更,这是结果:

操作次数都是5000次

T1: 判断下文件
T2: file_get_content这个文件
T3: file_exist && is_readable && file_get_content 这个文件

Array
(
[Test1] => Array
(
[LastStartTime] => 1367215477.7918
[LastEndTime] => 1367215477.7918
[LastUsedTime] => 3.6001205444336E-5
[AvaTime] => 3.5916407863653E-5
[Total] => 0.33174586296082
)

[Test2] => Array
(
[LastStartTime] => 1367215478.5506
[LastEndTime] => 1367215478.5507
[LastUsedTime] => 0.00010204315185547
[AvaTime] => 0.00010305986018425
[Total] => 0.64278268814087
)

[Test3] => Array
(
[LastStartTime] => 1367215479.5682
[LastEndTime] => 1367215479.5684
[LastUsedTime] => 0.00016403198242188
[AvaTime] => 0.00016249667572205
[Total] => 0.90015578269958
)

)

请不要参考AvaTime,这是线性平均时间,不准确

这是测试代码:
http://pastebin.com/i0ajTvMf


Bites Me!
AlloVince
2013-04-29 14:23:05 +08:00
@raincious 我还真的就是E_ALL

https://github.com/AlloVince/eva-engine/blob/master/init_autoloader.php

isset用的特别多,代表你项目的OOP程度不深或者代码组织不够好,还主要依赖函数式编程用参数传递,而没有把使用频率高或复杂参数抽象为一个对象,很好的OOP代码组织结构可以取代局部变量的传递。

至于文件读取的问题,使用file_get_content属于比较偷懒的做法,简单但是执行效率低,个人写脚本OK,项目中并不推荐。好的文件读取方式应该是

if ($fh = fopen($filename, "r")) {
# Processing
fclose($fh);
}


一次IO操作同时包含了文件的正确性检查。参看《用 PHP 读取文件的正确方法》 http://www.ibm.com/developerworks/cn/opensource/os-php-readfiles/index.html
Js
2013-04-29 14:27:42 +08:00
@raincious 这测试没什么意思, 同一个文件第一次就进OS缓存了, 等于单向的测file_exists+is_readable的效率, 尤其是is_readable本身就带了file_exists的功能...

http://pastebin.com/U9ye5kQz
GTim
2013-04-29 14:30:01 +08:00
人家是看到你还没达到大牛水平,哈哈...

错误和警告不能因为关闭了错误提示就忽略不是..
cute
2013-04-29 14:36:04 +08:00
我所有的代码都是error_reporting(E_ALL | E_STRICT).
chenz
2013-04-29 14:48:07 +08:00
1. 这里所有的讨论,从来就没有人说要用isset来防止global污染。实际上,就我个人而言,自动全局变量,在至少五年前已经不需要讨论了。现在还有人在讨论自动全局变量,在我看来很不可思议

2. 你提到"这可是E_WARNING啊,如果不屏蔽,报错给用户不说",证明你根本不知道error log应该如何设置。因为有经验的开发团队,在任何情况下,都不会让用户看到错误报告

3. 你不能根据一个例子没有问题,就假定所有情况下都没有问题。所以单单根据你那个request来讨论是否应该开启notice或者是否该使用isset是没有任何意义的。为什么要开启notice,我之前已经说得很清楚了,你不能保证所有人,以及所有场景下,都能严格初始化变量

4. 看了你写的inc.initializer.php,把最后的?>去掉吧。请看这里: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md The closing ?> tag MUST be omitted from files containing only PHP.

5. 不知道你为何多次提及"新手"或者"新学"。犯了上面第四条的人,很明显不能算是个非常有经验的PHP开发人员。而我要告诉你,这个帖子里的回复者里,有发布过多个PECL项目的程序员,有超过10年经验的开发者,有对zval了如指掌的同学。所以,低调吧,在你还在犯4这样的错误,还在连zval是什么都不知道的情况下,不要妄谈什么"新手"的习惯
chenz
2013-04-29 15:02:40 +08:00
看了你的http://pastebin.com/i0ajTvMf

1. 请了解clearstatcache这个函数,修改你的测试吧。你的测试代码没有任何意义
2. 你又犯了?>的错误:https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
breeswish
2013-04-29 15:07:02 +08:00
我一般开发环境使用E_ALL
生产环境关闭错误显示

检查变量是否为空什么的我觉得就跟C里面检查一个指针是否为NULL一样重要
raincious
2013-04-29 15:21:36 +08:00
事实证明,@AlloVince的结果是最快的,在各种情况下:

T1: file_existe
T2: file_get_content
T3: @chenz & @Js 的方式
T4: @AlloVince 的方式

Array
(
[Looped] => 7500
[Test1] => Array
(
[LastStartTime] => 1367219062.6647
[LastEndTime] => 1367219062.6648
[LastUsedTime] => 0.0001220703125
[AvaTime] => 0.00010805782386953
[Total] => 0.7269139289856
)

[Test2] => Array
(
[LastStartTime] => 1367219062.6648
[LastEndTime] => 1367219062.665
[LastUsedTime] => 0.00015997886657715
[AvaTime] => 0.00016230891026936
[Total] => 3.4872250556946
)

[Test3] => Array
(
[LastStartTime] => 1367219062.665
[LastEndTime] => 1367219062.6654
[LastUsedTime] => 0.0004270076751709
[AvaTime] => 0.00042835394322712
[Total] => 2.5338525772095
)

[Test4] => Array
(
[LastStartTime] => 1367219062.6654
[LastEndTime] => 1367219062.666
[LastUsedTime] => 0.00052094459533691
[AvaTime] => 0.00033806347033363
[Total] => 1.6245625019073
)

)

这是代码:http://pastebin.com/gu7MSFRz

但是除了Test3,和用来参考的Test1其他方式都至少可能会有E_WARNING抛出的风险。

另外@chenz,我没有质疑各位的历史。当然也不在乎各位是否写过zval之类,一个工具而已。就事论事,你看清我的观点,我认为你应当error_reporting(0),以上所有各位我的观点都是如此,但是同时必须使用set_error_handler()。这是我的核心观点。也是我在开发时一直的观点。

https://code.google.com/p/faculaframework/source/browse/trunk/include/class.oops.php#86

我同时也没有说“就应该让错误看不见”。你也要注意这一点,不要没事挑刺。另外,我搬出新学,是因为我一开始也用的E_ALL,我代码的r2版本里你能看到一堆file_exist和isset,但是后来,这些错误扰乱了我正常的HTTP HEADER,于是我必须做出调整,保持程序在任何情况下都是稳定的。于是我固执的认为error_reporting这种事情就应该 = 0,然后你自己负责的好好处理,不要直接抛出。

我想我的处理方式不在各位之下吧?

当然,为了证明上述,于是我搬了一些论据,比如isset的开销。当然,@AlloVince的方法确实是最快的,条件允许我会将一些地方的file_exist改成fopen来追求速度。

但是到此为止,我只看到了我的观点,没看到各位什么观点,我想知道你们开了E_ALL,然后怎么处理?就让错误飘在页面上?
allenhsu
2013-04-29 15:51:22 +08:00
@raincious 我是新手,但是 E_ALL 和『飘在页面上』好像是两回事。
Js
2013-04-29 15:51:23 +08:00
@raincious 你那个例子不确定因素太多了, 比如./test/1.txt的大小, 毕竟你file_get_contents测的全部文件, 而后者是读取的是filesize('./test/1.txt')大小.(PS: 我不认为读取大段或者全部数据fopen再fread比file_get_contents高, 两者逻辑基本一样, 后者比前者少了几次函数调用+内存再分配+字符窜拼接,只会快不会慢的).

言归正题, 文件不存在fopen一样会报warnning, 所以,按照你之前的思路, 你必须继续@fopen或者关掉警告, 至于性能,你可以把我那个例子的file_get_contents改成fopen, 差异还是那么大, 不会变的, 所以, 对待无法确定是否存在的文件, 还是在操作前判断是否存在
qq286735628
2013-04-29 15:51:41 +08:00
@raincious
开发环境全部打开,至于这个提示出现在哪里,看个人爱好。
生产环境写入日志,页面上不会出现任何额外的东西。
shiny
2013-04-29 15:52:42 +08:00
个人的习惯是开 E_ALL,像 file_get_contents 这类错误就用 @ 来屏蔽,error_handler 里可以识别是否是用 @ 屏蔽的。如果是 @ 屏蔽的则跳过,非 @ 屏蔽的则记录日志。错误级别足够高则显示自定义的 500 页面。

所以平时都不可能出现「错误飘在页面上」。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/67309

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX