貌似,被 isset 坑了又。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
raincious
V2EX    PHP

貌似,被 isset 坑了又。

  •  
  •   raincious May 12, 2014 3893 views
    This topic created in 4371 days ago, the information mentioned may be changed or developed.
    故事是这样的:



    Test1 你猜他会echo出什么?

    原先我猜是not found,可是竟然是found。



    猜测是不是调用isset的时候,PHP默默的跳过了非法的offset,然后使用了正确的数字索引……PHP啊亲,能别这么聪明么?

    版本5.5.12。
    55 replies    2014-05-16 17:47:38 +08:00
    yinxingren
        1
    yinxingren  
       May 12, 2014
    Test1 怎么也不会输出found把。
    测试了下
    PHP 5.3 not found / found / found 抛异常
    php 5.4 found / found/ not found 抛异常 且输出了string(1) "t"
    php 5.5 found / found / not found 未抛异常 且输出了 string(1) "t"

    翻PHP官网去了
    raincious
        2
    raincious  
    OP
       May 12, 2014
    @yinxingren 我又加了个测试(Test4),来测试一个不存在的数字索引。

    现在我这里是found / found / not found / not found / Illegal string offset 'check' 之后string 't' (length=1)。

    其实我想问的是,这是Bug么……
    soli
        3
    soli  
       May 12, 2014
    从来没有正确使用过 isset 。。。
    z4213489
        4
    z4213489  
       May 12, 2014
    为什么要这么用isset呢?
    raincious
        5
    raincious  
    OP
       May 12, 2014
    @z4213489 不是这样用。

    本来我有个isset是用来检测数组的,具体是这句:

    https://github.com/raincious/facula/blob/30bf00ac0f4408149801745f8e7b1040ca239ca5/src/Facula/Unit/SimpleORM/ORM.php#L520

    注意这里用了[0]来检测$jMVal['Field'](应该是字符串)是不是空的。而$jMVal原本应该是数组。

    但是我用这个模块的时候,意外的填错了参数,让原本应该是数组的$jMVal变成了字符串,然后报了Illegal string offset。我只是意外isset没拦截到这个问题罢了。

    总之,现在加入了is_array来检测$jMVal是不是数组类型了,我的问题算是解决了。剩下的就是isset为什么这么奇怪的问题了。
    lizheming
        6
    lizheming  
       May 12, 2014   1
    PHP官方手册是这么说的:

    Warning:用超出字符串长度的下标写入将会拉长该字符串并以空格填充。非整数类型下标会被转换成整数。非法下标类型会产生一个 E_NOTICE 级别错误。用负数下标写入字符串时会产生一个 E_NOTICE 级别错误,用负数下标读取字符串时返回空字符串。写入时只用到了赋值字符串的第一个字符。用空字符串赋值则赋给的值是 NULL 字符。

    http://www.php.net/manual/zh/language.types.string.php
    raincious
        7
    raincious  
    OP
       May 12, 2014
    @lizheming 不,这其实跟那个没关系。要关注的是Example #10里的那些isset,因为是在检测字符串是否被设置。

    当然那些都是单维度索引的isset,但是用了多维度的索引就出问题了,比如Test1和Test3的区别。
    vibbow
        8
    vibbow 
       May 13, 2014   1
    <?php
    $string = 'abcdefghijkl';

    var_dump(isset($string['check']));
    var_dump($string['check']);

    var_dump(isset($string['check'][0]));
    var_dump($string['check'][0]);

    echo "===================\r\n";

    $arr = array(
    array('a', 'b', 'c'),
    array('d', 'e', 'f')
    );

    var_dump(isset($arr['check']));
    var_dump($arr['check']);

    var_dump(isset($arr['check'][0]));
    var_dump($arr['check'][0]);
    ?>

    运行结果:
    C:\Users\vibbow\Desktop>php test.php
    bool(false)
    string(1) "a"
    bool(true)
    string(1) "a"
    ===================
    bool(false)
    NULL
    bool(false)
    NULL


    应该是在把String当Array访问时,PHP有特殊的处理。

    或许如 #6 所说的,虽然 $string['check'] 不存在,但是你访问的时候,下标的string会被强行转换成int (你这本身就是个string,所以PHP做这种强行转换是合理的,因为String类型的Array不可能有string类型的下标)

    当你的array是个原生的array,可以看到,就不存在这种“BUG”了。
    vibbow
        9
    vibbow  
       May 13, 2014
    以上结果在
    PHP 5.4.28 @ Windows 7
    PHP 5.5.9 @ Ubuntu 14.04
    里表现态一致。
    vibbow
        10
    vibbow  
       May 13, 2014
    再补充一个测试,虽然结果应该是显而易见的。
    <?php
    $string = 'abcdefghijkl';

    var_dump(isset($string['check']));
    var_dump($string['check']);

    var_dump(isset($string['check'][1]));
    var_dump($string['check'][1]);
    ?>

    运行结果:
    C:\Users\vibbow\Desktop>php test.php
    bool(false)
    string(1) "a"
    bool(false)
    string(0) ""
    vibbow
        11
    vibbow  
       May 13, 2014
    对了,检测String类型(仅String类型)是否为空时,empty函数也是不靠谱的。
    因为 "0" 会被认为是 空。
    raincious
        12
    raincious  
    OP
       May 13, 2014
    @vibbow 是的。应该是有特殊的处理。

    Test1里面的情况看起来是这样的:

    $string['check'][0] => $string[(int)'check'][0] => $string[0][0]

    看来PHP在取完$string[0]的值之后得到了字符t,然后比较了't'[0],然后isset就通过了。所以造成了上面的问题。

    看来以后做这类比较的时候得先判断下数据类型,以免造成问题(甚至可能是安全问题)。


    而且还得注意取值的时候并没有做这类转换。
    lizheming
        13
    lizheming  
       May 13, 2014
    @raincious keypoint就是我找的这段,String以Array形式访问的话不存在的key会被强制转换,这里是`interval('check')`就是0了。
    raincious
        14
    raincious  
    OP
       May 13, 2014
    @lizheming

    嗯。

    不明白为什么isset和取值会用两种方式。虽然在字符串上这样用多维下标不是正确的用法,但这样不规律的隐式转换感觉是欠考虑了啊,isset和取值应该都被隐式转换或都不被隐式转换才是正确的逻辑吧。

    不明白那群家伙怎么想的,或许……是给忘了。
    vibbow
        15
    vibbow  
       May 13, 2014
    @raincious 很规律啊
    对于字符串当array用,强制把下标转换成integer
    对于正常的array,就正常用啊。
    vibbow
        16
    vibbow  
       May 13, 2014
    @raincious 还有,如果你打开了错误报告,用string当下标访问字符串,PHP会raise一个warning级别的错误的。
    lizheming
        17
    lizheming  
       May 13, 2014
    @raincious 这个和isset完全没有关系啊骚年,在$string['check']这个的时候你已经就是不规范写法然后被PHP强制转换了啊,话说判断字符串是否为空难道不是应该直接 $string!="" 么...
    raincious
        18
    raincious  
    OP
       May 13, 2014
    @lizheming 你不觉得当遇到“不规范”的写法时,isset就应该因为找不到‘check’这个索引而失败么?没事“好心”转换integer才是有问题吧。
    lizheming
        19
    lizheming  
       May 13, 2014
    @raincious 卧槽,v2ex的消息提醒终于给力了一回....代码跟isset真的没关系啊...在isset()之前'check'就已经被转换成0了...至于为什么会有这么神奇的情况我也不太清楚,具体还得去看一下源码才能知道..不过这个真的有心无力,既然手册已经明摆写出来了就这么用着吧,而且这代码是会有报错的,报错才是“不规范”的写法的提示吧……
    最后的最后,我其实很纳闷为啥lz你会写出如此奇葩的代码出来...
    raincious
        20
    raincious  
    OP
       May 13, 2014
    @lizheming 什么叫奇葩的代码?上面已经说了怎么发现的这样的情况,你可以看到5楼。发这个帖子的时候我自己的Bug已经修复好了。只是奇怪这样的情况才来问。

    另外“在isset()之前'check'就已经被转换成0了”,如果真的是这样的话,那么var_dump也应该是[0][0]才对,那么Illegal string offset就不会出现了。

    所以是isset和var_dump对待$string['check']的方式不一致时才会出现这个问题。

    代码我已经翻了,对我来说太乱了,已经迷失。正考虑是不是要去PHP邮件组上把这个问题再吧啦一遍。
    lizheming
        21
    lizheming  
       May 13, 2014
    ...你确定认真看了我复制的那段话了么?PHP手册上已经说了,会把非整型key转换成整形,同时有一个E_NOTICE的报错。isset()没有报错是因为 isset()有特殊操作,在知道这是一个字符内的非整型key的时候就直接返回false了,具体你可以看手册 http://cn2.php.net/manual/zh/function.isset.php 更新说明里头明确写了:5.4.0 检查字符的非数字偏移量将会返回 FALSE。

    以及5楼的URL已经打不开了,就不吐槽这神奇的情况了。以及,真的还是好好看看手册吧……
    raincious
        22
    raincious  
    OP
       May 13, 2014
    @lizheming

    * Sigh *

    20楼


    > 所以是isset和var_dump对待$string['check']的方式不一致时才会出现这个问题。

    所以你不需要重新说明一遍,真的,12楼的时候我已经把出现这个现象的原因都解释了。

    我发这个帖子的目的不是为了让别人挑我用法上的刺。因为本身这样用就是错误的,我也只是意外的填错了参数才发现了这个问题罢了(这是我第二遍说了亲)。如果你真的有空,那么去看看我的代码,做做Review,我真的太感激了。但是这段代码,至少5楼链接里的具体那一行是没有问题的。(当然,至于为什么会有throw、return、break这样奇葩的组合,那真是我很奇葩的原因。而我会在未来某个版本删除掉那些。)



    > 以及5楼的URL已经打不开了,就不吐槽这神奇的情况了。以及,真的还是好好看看手册吧……

    Github是经常被墙的。难道你认为是我神奇的从Git历史记录里删掉了那个文件来遮丑么?这框架里面有2000多个文件啊,删掉任何一个都会导致问题,我不会那么蠢的。其次,我犯过很多很小的错误,而且他们都还在历史记录里,你甚至能看到修复记录,我不是喜欢删掉罪证然后把他们当作没发生过的家伙。



    我现在的想法,是如果有人也觉得这样比较奇葩的的话,我就鼓足勇气去PHP邮件组跟那群真正设计语言的人吧啦下,看看他们能不能思考下这个问题。因为这样也太怪异了。不是么?

    不过现在我担心的问题是,当我说“这样设计不太好吧”的时候,他们也会说,“哦,那不是问题,是你用错了,你看我们的手册要怎么用再来吧唧”。就跟……这个帖子的情况一样。

    当然,你觉得不怪异可能是你已经接受了这种世界设定,就像他们当初给丢出magic quotes时一样。



    我的观点也已经说过了,14楼。
    lizheming
        23
    lizheming  
       May 13, 2014   1
    @raincious …汗...您已经开启开喷模式了么...

    1.我刚才访问5楼的地址的时候是显示Github的404页面的,并不是被墙的问题,墙没被墙我还是能看的出来的:),不过现在正常了。以及我刚才说这句话并没有别的意思,只是单纯的陈述一个事实而已,你真的想太多了。
    2.我并没有说这种做法很好,当然也没有说这种做法不好。我认为,你要说一个东西好不好,首先你得会用这个东西,然后你得理解这个东西,人家为什么会这么做,这个不好的地方可能是基于什么样的考虑,然后你才能觉得人家这样做不好。你连人家为什么这样做都不知道就开始说人家的做法很差劲你觉得说的过去么?很不才,我楼上的评论正是在一直疏通这个代码是怎么运行的。如果你早已经明白了的话,您就当我在放屁吧,如果你不明白,就当我在帮您补充点知识以备你在官方邮件组里头和人讨论的时候有点资本。
    3.最后欢迎你去邮件组里头和官方讨论一下,也希望您能把讨论结果贴于此方便给大家答疑解惑。

    以上。
    raincious
        24
    raincious  
    OP
       May 13, 2014
    @lizheming

    1) 不,我只是借这个机会证明我是个诚实的人,哦呵呵。404我真不知道了,当然我刚才在Commit,不知道是不是Git在更新本地的数据。

    2) 其实我看过那些内容了。也说了check到最后会变成0,所以……我真的已经看了。你可以看到剩下的我就开始不解为什么会这样了。

    我的反应可能和你不一样,我猜测了原因(因为PHP的源代码真难看懂),因为数组下标和字符串下标在C里使用了不同的实现,所以导致处理方式上进行了不同的转换。isset是一种特殊的语法结构,他尝试在不操作目标的情况下对目标进检查(爬一下变量表嘛),然后再转换索引准备爬表的时候,使用了“绝顶聪明”的方法。

    好吧,我真的不知道是怎么具体实现的。但是这样的设定,分明就是在造坑啊不是么。我是说,为什么isset($string['check'])能正确的搞出false,isset($string['check'][0])就得先雅蠛蝶的转换下变成$string[0][0]然后搞成true呢?因该也还是的设定false才对吧。 // 请作为用户视角来看待这个问题

    3) 我已经不知道咋讨论了,就像22楼说的,说不定人家说“这是你不会用”。不过,等有空还是去吧唧下,不管有没有效果什么的。

    4) 我觉得强类型语言真省心啊有没有,比如Python啊,Go啊什么的。
    lizheming
        25
    lizheming  
       May 13, 2014   1
    @raincious 你说的这个isset()的行为并不是不能理解,不过我懒得说了,怕你又要喷我陷入人家的模式里头了。以及你的评论并没有让我觉得你明白了人家的代码是怎么运作的,只是一个劲的觉得很奇怪,希望你能在邮件组里头得到一个完美的解释吧。最后,我个人还是比较喜欢弱类型语言的。以上!
    lizheming
        27
    lizheming  
       May 14, 2014   1
    @raincious 我一直都是在针对你的7楼的回答,以及你在14楼的回答中说不知道为什么。我的回答就是在帮你找官方的原因,仅此而已:)。isset()这个我认为应该是只判断最后一个key是否存在的,比如 isset($str['f']['f']) 的中文理解可能是 "$str['f']"数组中是否存在'f'这个键值,所以先数组了前面一部分,然后对'f'这个键进行了检测,因为是字符串型的非整型key所以会直接返回false,具体的实例可以看这里: http://3v4l.org/A7Bqb
    raincious
        28
    raincious  
    OP
       May 14, 2014
    @lizheming

    哦,14楼可能我让你理解错了。我想说的其实是不知道为什么这帮家伙会做出这样的决定。

    总之,在看不懂源代码的情况下,咱们只能论断这两种可能了。要么就是'f'被估值成了0,要么就是PHP只估值了最后一个Key。

    当然或许是我没搞清楚你的意思,但是我测试$string['check'][1]的时候,得到的是not found的结果,所以其实貌似不是只估值了最后一个。而还是$string[0][1]这样的,然后't'[1]不存在。如果只估值最后一个结果也就是[1]的话,那么应该得到的是h。

    根据上面的来说,$str['f']['f'],或许只是变成$str[0]['f']了(如果这才是你的正确意思的话),所以这是有可能的。

    总之,我发了个帖在PHP的新闻组里,然后有个家伙让我直接用array_key_exists代替。除此之外还没有更详细的回复。

    http://news.php.net/php.general/323298

    然后我就借机抱怨了下
    http://news.php.net/php.general/323307

    不知道会不会有什么进展。

    总之我现在有两个方案来搞定这个问题了。

    要么就老老实实地用着复杂组合。if (isset($val) && array_key_exists('key', $val) &&
    is_string($val['key']) && !empty($val['key'])) {这些。

    要么就利用上面的诡计if (isset($val['key'][1]) && is_string($val['key'])) {,这样一样能正确检测,但是数据不但必须不能是空的,还得多于2个字符。

    当然还有一个做法是 isset($val['key'], $val['key'][0])。

    我决定就这么纠结着用了。一边等邮件列表里的结果,虽然貌似看来不会有啥结果。

    另外感谢你一直回复,我散点分给你哈。
    lizheming
        29
    lizheming  
       May 14, 2014
    @raincious
    ..已经完全不懂你在说什么了..我的理解是
    $string = 'this';
    var_dump( isset($string['check'][1] );
    这句话中 $string['check'] 因为isset只判断最后一个键 && $string是字符串 && 'check'是一个非整型键 被强制转换成了 $string[0] 也就是 't',然后实际上执行的就是 isset( 't'[1] )了,这里报false不是很正常的么,总共就一个字符串哪里来的1?

    你说的两种可能就是一个思路上的东西,并不存在两种的情况...不把$string['check']转换过来的话 isset()怎么知道是从哪个 数组/字符串 里面isset呢?
    lazyphp
        30
    lazyphp  
       May 14, 2014
    isset 是一个坑。有时候用来检测 东西是否 赋值了,很容易就出现意外 结果。
    于是乎,我现在的程序都是用empty, !empty 去判断了。
    raincious
        31
    raincious  
    OP
       May 14, 2014
    @lizheming 哦,很抱歉,我没给你看邮件原文,很明显这造成了你理解的问题,这是 Topic: http://news.php.net/php.general/323297

    事实上这一系列的帖子或许旨在讨论一个问题:“如何使用isset来快速检测当前设定的数组是否为合法的结构”。当然,我觉得目前这个说明是足够的。

    isset($string['check'][1]) && is_string($string['check']) 的目的在于,一旦$string是一个字符串,那么这个表达式将准确的返回false。

    因为无论isset的行为如何或者'check'到底有没有估值成0,只有当$string['check']是一个字符串(isset中包括$string是否存在,以及$string['check']是否存在的检查),且至少有两个字符的时候,才会是true。

    当输入是如下结构的时候:

    $string => array(
    'check => 'whatever',
    );

    表达式将返回true;

    而当结构如下的时候

    $string = 'whatever';

    表达式将返回false。

    而且根据我12楼的推导,这样的检查将会是准确的,因为如果$string['check']真的会被估值为$string[0]。那么$string[0][1]总是不存在的,(相反$string[0][0]总是存在的,哪怕$string[0][0][0]...[0][0][0][0][0])。

    所以其实我所有的问题都已经解决了。现在我希望能在PHP邮件列表上讨论下关于isset的细节。因为我想要的结果是isset($string['check'][0]) and isset($string['check'])都将返回false。也就是说isset不要对这个'check'进行估值,而应该先检查下数据类型,如果使用了不正确的下标则应该返回false而不是估值成0。

    以上。
    lizheming
        32
    lizheming  
       May 14, 2014
    @raincious 关于isset()的问题我我帮助不了你太多了,我只是来理清楚为什么出现这种结果的。更高一层的东西还得由其他人解决,希望你能得到个好结果:)
    raincious
        33
    raincious  
    OP
       May 14, 2014
    @lizheming

    关于isset为什么会出现这种情形。我12楼已经推导了(我已经说了好多次了),那是正确的结论,请不要继续纠结。

    关于你那个$str['f']['f']还是得继续研究下。不过我想我们都应该意识到猜测是没有意义的,必须去阅读PHP的源代码来找到答案。

    如果PHP新闻组那边有什么有价值的更新,或许我会在这里Append。
    lizheming
        34
    lizheming  
       May 14, 2014
    @raincious 12楼说的是PHP怎么"运行"isset()的,并没有说明isset()为什么要这么去做,关于怎么"运行"在5楼的时候我就已经名表了:),以及坐等append。
    lizheming
        35
    lizheming  
       May 14, 2014
    @raincious 12楼说的是PHP怎么"运行"isset()的,并没有说明isset()为什么要这么去做,关于怎么"运行"在5楼的时候我就已经明白了:),以及坐等append。
    raincious
        36
    raincious  
    OP
       May 14, 2014
    @lizheming 我……不是已经给你分了么……

    另外其实,文档里的解释是不全面的,因为根据var_dump(isset($str['1x']));在5.4下会返回bool(false)的事实,不应该得出Test1为Found的结果。

    我们讨论的是,为什么isset($str['check'])是false,而isset($str['check'][0])则会是true(Test 1和Test 3)。

    说了这么多……我想你应该明白了……

    以上。


    嗯……为什么我们在说这个?我觉得应该聊聊更有意义的话题,比如PHP为什么是最好的语言。
    lizheming
        37
    lizheming  
       May 14, 2014
    @raincious 原来你的感谢加分是这个意思?我一直以为是“另外感谢你一直回复,我散点分给你哈。”这个意思呢…… 5.4以下就不在我的讨论范围了,因为手册上明显的说着是>=5.4加入的。如果只是讨论为什么会有这个结果我想我已经说的很清楚了,如果是讨论PHP为什么要这么做的话我也不清楚,坐等你把官方的消息反馈给我:)。
    我也不太想说下去了,思路我已经捋清楚了还在这里饶口舌真是累。
    raincious
        38
    raincious  
    OP
       May 14, 2014
    @lizheming 所以我觉得我们一直在纠结一个乌虚有的问题。

    因为你的帖过档案之后,7楼我已经说过了,跟这个没有关系。简单一点来说,我在意的是单维和多维数组得到的结论不相符的问题。但可能我没解释清楚(抱歉,我还在一边做原型设计,然后实现,还要处理框架变更,所以抱歉,精力有限,做出了“你可能懂的”假设。)

    isset($string['check']); // Test 3
    isset($string['check'][0]); // Test 1

    * Sigh *,这两个例子我贴上去之后,竟然没有也感到很奇怪的。

    难道没有发现一个检查到了'check'之后false,一个估值成了0然后true这两种结论然后觉得很奇怪的。
    lizheming
        39
    lizheming  
       May 14, 2014
    @raincious 如果是基于官方手册给出的说明”检查字符的非数字偏移量将会返回 FALSE“,我觉得是可以理解的,我的理解在29楼...
    raincious
        40
    raincious  
    OP
       May 14, 2014
    @lizheming 那么Test 1为什么没有返回false,它也是非法下标啊(又没说具体是第几个)? 是因为有第二条规则,即非数字偏移量会被估值转然后换成0(PHP 5.3的做法)。

    其实我的意思就是,PHP应该在检测到第一个下标是非数字偏移量的时候,就return false;而他这么做很明显就是为了照顾isset($str['1'])。

    我觉得应该烧死那帮用isset($str['1'])的人。
    lizheming
        41
    lizheming  
       May 14, 2014
    @raincious 29楼不是说了很清楚么.. isset($str['check'])的中文意思是检查$str 数组/字符串 内是否存在 'check' 键,isset($str['check'][0])的意思是检查 $str['check'] 这个 数组/字符串 内是否存在 0 键.. 所以$str['check']会先转换,因为只有得到 $str['check']是什么才能确定是否有0键... isset($str['f']['f'])其实也是一样的,检测$str['f']中是否有 'f' 键,先转换$str['f']所以会有报错,但是'f'是字符串所以直接返回false了。
    raincious
        42
    raincious  
    OP
       May 14, 2014
    @lizheming

    你以为我不明白你29楼在说什么?

    > isset($str['check'][0])的意思是检查 $str['check'] 这个 数组/字符串 内是否存在 0 键.. 所以$str['check']会先转换

    (事实上你也说过好多次了。)

    就这个问题,更准确的说,我40楼是在问“为什么要转换?”,为什么不发现'check'这个数组是非法下标之后直接return false?

    好了,我觉得你已经清楚我12后之后一直在说什么了。
    lizheming
        43
    lizheming  
       May 14, 2014
    @raincious 我真没觉得你明白我29楼在说什么,为什么要转换我不是说了么“不把$string['check']转换过来的话 isset()怎么知道是从哪个 数组/字符串 里面isset呢?”,而且我41楼又重复说了一次“因为只有得到 $str['check']是什么才能确定是否有0键”...
    比如说
    $a = array( array=>(1,2) );
    var_dump( isset($a[0][3]) );

    isset()是先获取到了 $a[0] 为 array(1,2) 之后然后再在这里面检查 3 这个键是否存在。这样说应该好理解了吧()...

    p.s.其实我也不想要重复说啊..因为真的很累啊....但是...真的就只有这点东西嘛...
    raincious
        44
    raincious  
    div class="badge op">OP
       May 14, 2014
    @lizheming

    哈哈。其实,我搞清楚这个问题之后,一直是在抱怨,可能我用了疑问句,所以让你误解了。我觉得我再不说清楚咱们俩都得进入死循环了。

    因为我知道这个问题就算我在这里问也是搞不清楚的,因为要认真的阅读过源代码才会了解。所以我希望是在PHP新闻组上得到解答,或者被忽略。

    不过你说的这个“因为只有得到 $str['check']是什么才能确定是否有0键”,没错啊,但是不一定要先估值转换啊,它可以直接因为$str['check']这个根本不合法而直接返回false啊对不对?



    这个问题呢,就像是我写了个很垃圾的函数,叫做,嗯……比如说get_file_contents,嗯,get_file_contents。它是用来打开文件的,但是这个函数有个很讨厌的功能,就是如果这个文件不存在并且目标文件的文件名是以PHP结尾的话,他会给你建立一个文件,然后写入“PHP是最好的语言”这个字符串,然后返回。

    有一天,你用到了这个函数,用来它来获得一个文件,比如叫“任何文件”。但是这个文件可能存在也可能不存在。问题是这个文件的文件名不是由你控制的(而是由其他程序员以及配置控制的)。

    你搞清楚了get_file_contents的机制知道有这个问题。于是为了安全,你效验了文件名,写下了如下代码:

    if (文件名的结尾(文件名变量) != 'PHP') {
    return get_file_contents(文件名变量);
    } elseif (文件存在(文件名变量)) {
    return 另一个更加麻烦或者因为某些原因不建议使用的函数(文件名变量);
    }

    然后你开始了抱怨,是啊,如果get_file_contents没有判断文件是不是以PHP结尾这样糟糕的特性该多好,那时候你就可以直接return get_file_contents(文件名变量);了不是么?

    于是你抱怨上了论坛。询问get_file_contents为什么会这样?

    这个时候另一个人看到了你的问题,会答到:

    这是因为get_file_contents会判断文件名结尾,然后调用另一个函数put_file_contents来写入“PHP是最好的语言”。

    然后你说,为什么需要这样?难道返回一个false不是更好么?

    这时候那人又说,你得读文件啊,文件必须读过才知道是不是有内容啊,这都发生在put_file_contents之后。



    所以你看,我们的方向是不一样的。我期待的讨论是,是不是底层上有什么结构会导致我的设想无法成立,另外是不是我的设想就不对呢?接着我还可能会问“那么为什么不能实现我的设想,因为那样更加直觉正确”,最后可能真的能促成这件事(虽然,这,简直微乎其微,大PHP怎么会为这点小事而改动)。


    我期待的结果可能是比如这样的讨论“$string对于编译器来说是一个T_VARIABLE,除此之外编译器对其一无所知。而isset发生与编译阶段,于是它只知道$string的类型,而不知道$string其中内容的类型”。当然前面是扯淡,PHP会解析这些东西,然后得到所有TOKEN的必要信息,包括它是什么以及他里面是什么(何况或许根本就没有“里面”这东西,只是地址引用)。

    所以,嗯,咱们继续等那边啥情况吧,或许我真的被忽略了。

    不过就我自己这种情况来看,isset($string['check'][1]) + is_string($string['check'])工作良好。但或许我应该改成!empty($string['check']) + is_string($string['check'])。因为我发现他们都是通过zend_do_isset_or_isempty来实现的,只是作为第一个参数type,被设定成ZEND_ISEMPTY或者ZEND_ISSET而已。

    而这个type也就是在检查zend_is_function_or_method_call以及设定op编译扩展信息的时候用了下。(当然我还没完整看过代码,还是闭嘴的好。)


    好了不吧唧了,我继续做项目去了。也感谢你一直聊这个话题。
    lizheming
        45
    lizheming  
       May 14, 2014
    @raincious 写了这么长完全不知道你要吐槽什么。”直接因为$str['check']这个根本不合法而直接返回false啊“这个不在我的模式范围内所以我不多做讨论。举的例子也是bug,文件名分明要转成大写再做判断好么,而且函数名就说明了人家就是用来获取文件内容的,非得拿人家来做判断文件是否存在是不是稍微过分了点?...
    其实我早就明白你是在说要PHP为什么这么做,而我一直在强调的是我在说的是PHP怎么这么做,详情见23,25,32,35,37楼...我说到邮件组和其他人这部分的话的意思都是说我只是在解释PHP怎么这么做的,不明白为什么这么做。
    问题在于每次我这么说完之后你就把我扯到怎么做这个问题上,等我把怎么做这个问题解释清楚之后你又给我扯回到为什么这么做上头去……WTF!!!!
    raincious
        46
    raincious  
    OP
       May 14, 2014
    @lizheming


    > 写了这么长完全不知道你要吐槽什么。”直接因为$str['check']这个根本不合法而直接返回false啊“这个不在我的模式范围内所以我不多做讨论。

    这是我14楼以来一直在抱怨的啊,我就是想如果能那样该多好阿,简直直接乌托邦了啊。现在都46楼了中间这么多你干啥去了?我们在进行什么欢乐的娱乐活动么亲?PHP原理猜猜猜?看似是的呢。



    > “函数名就说明了人家就是用来获取文件内容的”

    isset函数名说明了这是用来检测某个变量是否存在的,那么解析这个变量的时候给我埋这个坑是不是也是Bug?



    另外你要知道,其实,你想要解释的那些Test1 ~ Test 4已经说明白了。

    比如你27楼str['f']['f'],只是做了Test1 + Test 3而已。


    当然,我原先的认知也不是对的,我一开始是认为,因为'check'这个索引找不到,所以才会是false,而不是isset检查过了索引,因为是非法的所以返回了false。


    我只想知道为什么不能想我说的那样实现isset,让Test 1和Test 3一起false。然而这个问题我是一直没有得到答案的。

    至于死循环是这样的:我一直想问为何不能这样 => 你没有答案 => 你解释PHP怎么这么做 => 我:艹我没问这个问题啊 => 回到1。这是很正常的轮询结构不是么o_o。


    其实我已经不想接着讨论这个问题了,所以才发上面那个帖子。老实说吧,我觉得在这里讨论不出任何有价值的信息了,因为……你也不知道源代码说了什么啊,根本不知道应该告诉我什么。再着么下去估计就得吵起来了不是么,就此打住我们还是好朋友,一包薯片都能分着吃的那种:我一包,。


    PS。如果VLD不只能打印出Opcode,也能跟踪整个工作流,我想就不会有这么多解释不清的麻烦了。现在只能看到FETCH_DIM_IS拿了check(后来应该是0才对吧)出来然后给到ISSET_ISEMPTY_DIM_OBJ,却不能知道IS出来的东西到底给编译器搞成什么了。

    其实我觉得,一直将输入数组当成DIM来用,次序也是从左往右的,ASSIGN的时候也知道目标数据类型了(是的,如果是数组的话会先INIT_ARRAY的亲),那,实现让isset遇到非数字下标直接return false能有多复杂呢。

    好了。我还是安心去等PHP那边有没有响应又或者永远没有响应。你也安了吧,消消气,睡个好觉。
    lizheming
        47
    lizheming  
       May 15, 2014
    好吧,我也来结下帖,看到你这个最后,我最后真的是想骂人了。
    最开始我看您的帖子内容是“猜测xxxxx”并没有给出PHP是怎么在这个代码中工作的,然后我就上手册查了查帮你找到了一个确实的工作机理,然后你就说我的内容各种不对不对什么的...

    现在可以确定的是,工作机理是我说的这样的这样,我的解释并没有什么问题。

    然后在22楼和23楼的时候我已经明白了您是想要探讨更高深的东西,想要知道为什么这么做,并在25楼的时候明确告知关于“为什么这么做”这个问题的答案我不知道并试图停止解释工作机理这个过程。

    结果您在26楼的时候又把我给扯进去了,之后我就一直在试图想说明“我说的是PHP的工作机理的一个合理解释,并且是一个可以自圆其说的解释,是没有问题的,以及我并不懂PHP的工作机理为什么是这样的,希望您在邮件组中能得到更完美的答案,并贴于此告知大家”,结果我也不知道您为什么就是各种吐槽我这不对那不对什么的..跟您扯这么多,我真的好忧桑好么!
    raincious
        48
    raincious  
    OP
       May 16, 2014
    @lizheming 怎么没有TL;DR,因为我真的不想R。

    我已经把这个问题丢给老外了。结果收获丰硕。

    具体其实已经被解释的很清楚了: http://stackoverflow.com/questions/6588016/php-5-4s-simplified-string-offset-reading 我很满意,但是还在等PHP里的家伙能不能给个回复,所以没有Append。其他的我也不想说什么了。
    lizheming
        49
    lizheming  
       May 16, 2014
    @raincious ...不懂您第一句的DR和R是什么意思(Д`),zend的源码也看不懂,好忧桑..
    raincious
        50
    raincious  
    OP
       May 16, 2014
    @lizheming 好吧,你一直在帮我,我还是挺感激的。所以我吧问题梳理下,然后就能意识到咱们两个(至少是我)个笨瓜在讨论什么。

    原始问题是原帖;答案是:'check'会估值成0。于是原始问题就解决了,答案在8楼。于是推导出了 $string['check'][0] => $string[0][0] => 't'[0] => 't' => isset == true

    于是出现了20楼的问题,对待var_dump和isset方式不一致。你的解答:isset()没有报错是因为 isset()有特殊操作。(简而言之,“对待var_dump和isset方式不一致”,但这就是我的结论啊)。这个问题可以Pass了。我说12楼其实是让你看“是的。应该是有特殊的处理。”,因为我一再确认(Conform)那个不一致。

    然后就是你27楼丢出的问题,我28回到“根据上面的来说,$str['f']['f'],或许只是变成$str[0]['f']了”。我没说我是对的你是错的,我是推断你是对的。这也是正确的答案。

    当然,过程是这样的:$str['f']['f']最后是变成了$str[0]['f']然后交给了isset,isset看到string,于是返回了false。

    正确的原因是这样的:

    isset($string['check'])能返回false因为它有代码段来专门进行这项检查,扫描到'check'是string直接返回了false。
    所以,这也就不难理解为什么isset($string['check'][0])会是true,因为那段检查没有检查$string['check']。他只检查了[0],然后对前面的进行了转换(根PHP 5.3之前一样),而不是你猜测的无法判断数据类型的原因,具体的原因是SO的那个链接(TL;DR:为了……优化)。

    所以可以理解为PHP还是在5.3的模式下运行,只是isset那边多了这项检查罢了。于是乎上面都说通了。


    于是乎,(你真的不用回答下面的问题的,因为你回答不了,对吧?),我艹,多爬个检查会死么。isset改改让他也爬下$string['check'][0]里的'check'不就没这问题了么。 // 其实我后面一直在说这个,但是我用了疑问句“为什么不能因为$string['check'][0]的而false,非得$string['check']呢”,这或许造成了你的理解错误嗯。

    另外,我46楼在说,其实PHP拿到那个isset的输入参数之后,(我猜)是直接拿它当数组用的,因为可以看到FETCH_DIM_IS,然后ISSET_ISEMPTY_DIM_OBJ,所以我觉得上面我的吐槽是略有道理的呢,除非我猜错了……
    lizheming
        51
    lizheming  
       May 16, 2014
    8楼的答案最终给出的也没有明确,也是用或许回答的,而且还转到了我的楼上来了。而且我回答的时候是针对你7楼说的“跟这个没关系”来回答的,就是想强调,其实是有关系的。

    20楼的时候我还没明白您是在问Why这种高层次问题,我是在22楼你“吐槽”我的时候我才明白的,再此我先为我的低智商道歉一下。

    从你以上的回答中我并没有感觉到你认为我说的是一个对的答案,而且回答中的示例跟我想的步骤也有不同,所以我在回复中一再进行“疏导”,并且强调了如果您明白了的话就当我是在放屁好了。结果您还在和我讨论...我以为你还没明白..我当然就只能陪着您讨论下去了..另外说一句,32楼我说的“isset()的问题”是说isset()为什么这样做的问题,我以为你能明白的……

    你说的原因不就是我讲的啊..我并没有说没法判断数据类型(我哪里有表达这个意思啊(Д`))..我只是想强调isset()只是转换了前面的一部分获取到最终一个数组后检查最后一个key是否存在,但是因为key是字符串的,就直接返回false了啊... 果然对我的回答的理解不一致才导致了我们这么累么....

    是的,你这句话让我出奇的愤怒了,什么叫做没办法回答?(□′)┻━┻ 我这就回答给你看:

    1.首先要强申一下isset()只是判断一个变量是否存在的,不管什么时候也不会报错的,比如 http://3v4l.org/Ao6GI 很抱歉之前没有认识到这一点,给我的解释上造成了一些误会。只有在取值 var_dump()或者echo的时候才会报错。

    2. 关于为啥会报错什么的,没必要去查源码了,laruence大大在博客上已经讲得很清楚了,http://www.laruence.com/2011/12/19/2409.html。总结来说就是为了兼容。

    3.关于怎么去isset()的过程就是我讲得那样,本回复的第四段也是在说这个。你的最后一次回复基本没有说错,除了这句”isset($string['check'])能返回false因为它有代码段来专门进行这项检查,扫描到'check'是string直接返回了false。“是不完全正确的。isset()就是判断最后一个而已..

    4.如果你真的想要搞清楚PHP为啥怎么运作的话可以试试vld这个扩展http://pecl.php.net/package/vld,可以逐步输出PHP的运行过程,能了解PHP的运行过程。

    好啦,我发现我现在连为什么也搞清楚了,你搞没搞清楚就随意吧。如果你要回答的话请别@我了,我是一看有@不回就不舒服斯基的人,这样可能就没完没了了T_T,亲我们就再也不见了吧....真蛋疼...
    raincious
        52
    raincious  
    OP
       May 16, 2014
    @lizheming 搞不懂你在说什么了。

    > 你说的原因不就是我讲的啊..我并没有说没法判断数据类型
    > 所以$str['check']会先转换,因为只有得到 $str['check']是什么才能确定是否有0键(41)

    那是我理解错了?

    回答在这里: http://news.php.net/php.general/323332

    意思是说 “对待var_dump和isset方式不一致”。而不是“因为只有得到 $str['check']是什么才能确定是否有0键”(所以才将'check'估值成0)。 // 这就是我想知道的。

    PHP一开始就知道$str的类型,因为变量的初始化方式不同。String类型是不可能有数组的,所以根本不需要去找$str['check']就能知道这是个字符串。

    46楼我已经说了,“FETCH_DIM_IS拿了check(后来应该是0才对吧)出来然后给到ISSET_ISEMPTY_DIM_OBJ”。'check' -> '0'这样一层一层取出的(所以没必要只检测最后一个)。

    之所以只拿最后一个,是因为SO那个帖子说的“I think t's not a problem at all. "b" makes sense because "abs"[1] -> "b" and "b"[0] -> "b".”。

    好了,我要知道的信息已经完全了,完结。
    lizheming
        53
    lizheming  
       May 16, 2014
    @raincious 你居然还@我了..真是不回不舒服斯基啊…... 没有什么一致不一致的,var_dump和isset的思路都是一样的,多为数组都得逐级获取才能得到值。但是isset只是判断值是否存在而已,如果硬要说特别就只有这一点特别而已,邮件上也是这么说的。而且这段”So, $string['check'] is effectively $string[0], which is "t"; so
    $string['check'][0] is $string[0][0], which is set and evaluates
    to "t".“就是我讲的思路(你引号中说的不是的那部分)。没错,是一层一层的取出的,但是取到最后面就只有最后一个key了啊..跟我说的判断最后一个key有什么区别么(Д`)..
    你倒数第二段讲的不也还是我说么一级一级的获取然后去顶最后那个key是否有(Д`)
    raincious
        54
    raincious  
    OP
       May 16, 2014
    @lizheming 我……跟你是一个星球的。

    > 但是isset只是判断值是否存在而已,如果硬要说特别就只有这一点特别而已
    好了,你成功被我摆直(歪)了。我很满意,哇哈哈。

    > 就是我讲的思路

    差不多,但其实我是在纠结你前面说的一句,就是:

    > 所以$str['check']会先转换,因为只有得到 $str['check']是什么才能确定是否有0键

    然后,老外告诉我,这是isset特有的检查之后

    > 就这个问题,更准确的说,我40楼是在问“为什么要转换?”,为什么不发现'check'这个数组是非法下标之后直接return false?

    这个问题就解决了。因为原先就要转换,isset只是“好心”的false了一个test。

    然后加上12楼推导出来的结果再加上“isset和var_dump不一致”,你6楼的帖子就变得可以理解了。(因为我一直在问$string['check'][0]为什么和$string['check']不一样,而6楼的帖子解释了$string['check']会被isset判定为false掉,但是没说$string['check'][0](就是说,其实isset在处理$string['check'][0]的时候还是按照$string[0][0]来处理的),你看36楼)


    > 跟我说的判断最后一个key有什么区别么

    不……我只是不理解为什么只判断最后一个。后来SO上的帖子给了我答案。就是这样。

    而且貌似,我没说你错了啊,我还确认了下说“根据上面的来说,$str['f']['f'],或许只是变成$str[0]['f']了(如果这才是你的正确意思的话),所以这是有可能的。”,可能你后面理解错了,觉得我说你说错了。


    好了,咱们俩说话好费劲,只是因为互相理解错了。

    另外你有没有觉得V2EX讨论这样的话题好费劲啊,不停的报楼号什么的。

    感谢你一直陪我吧唧。
    lizheming
        55
    lizheming  
       May 16, 2014
    @raincious 为啥是被你掰的..我51楼的第一点不就是这么说的么o(□)o……肯定是会转换的啊,一级一级的获取在获取 $str['check'] 这个的时候就会被转换成 $str[0] 获取啊..有什么问题?是的,我的意思就是变成了 $str[0]['f']
    嗯,是好费劲..还要自己去找楼号,就不能给楼号来个更简单的锚点然后自动解析锚点么OwQ。。
    好了大概误会解除了...我也谢谢你,查文档我也学习到了好多OwQ
    About     Help     Advertise     Blog     API     FAQ     Solana     971 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 70ms UTC 23:23 PVG 07:23 LAX 16:23 JFK 19:23
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86