首页 后端开发 php教程 怪异的可怕php

怪异的可怕php

Feb 25, 2025 am 09:25 AM

Spooky Scary PHP

准备好南瓜糖和苹果酒了吗?一年一度的万圣节又到了!虽然全球各地庆祝万圣节的狂热程度不及美国,但我还是想分享一些“恐怖”的PHP技巧来庆祝这个节日。这篇文章轻松有趣,将向您展示PHP自身的一些令人惊讶(但合乎逻辑)的行为,以及一些人利用PHP完成任务时那些令人毛骨悚然(且可能非常不合逻辑)的方法。您可以把它看作我的节日礼物,一点程序员的“精神糖果”——毕竟,为什么只有不给糖就捣蛋的小朋友才能享用所有美味呢?

要点总结

  • PHP可能会表现出意想不到的行为,例如在第一个foreach循环之外保留引用,导致输出结果出乎意料。可以通过使用数组的键将字符串重新赋值来缓解这个问题。
  • 当使用PHP执行更复杂的任务(例如shell脚本)时,理解在fork时执行环境是如何克隆的,以及各种资源如何在所有进程中受到影响至关重要。例如,连接数据库时,最好在fork子进程后在父进程中连接,子进程如有需要则自行连接。
  • 单例模式(实际上只不过是花哨的面向对象全局变量)会使调试变得困难。建议尽可能避免使用单例模式。
  • 虽然像“Spooky Scary PHP”这样的非常规编码实践很有趣且具有教育意义,但它们通常不被认为是编写生产代码的良好实践,因为它们通常涉及使用效率低下、不清楚或不可预测的函数或技术。

闹鬼的数组

从前,在一个不那么遥远的开发工作室里,亚瑟深夜还在编写代码。他不知道自己即将使用的数组闹鬼了!每敲击一次键盘,他都感到一阵寒意从脊椎滑落,但他愚蠢地忽略了这种微妙的预感。

<?php
$spell = array("double", "toil", "trouble", "cauldron", "bubble");
foreach ($spell as &$word) {
    $word = ucfirst($word);
}
foreach ($spell as $word) {
    echo $word . "n";
}
登录后复制
登录后复制
登录后复制

好吧,这个数组并没有真正闹鬼,但输出结果确实出乎意料:

<code>Double
Toil
Trouble
Cauldron
Cauldron</code>
登录后复制
登录后复制
登录后复制

这种“恐怖”行为的原因在于PHP如何在第一个foreach循环之外保留引用。当第二个循环开始时,$word仍然是引用,指向数组的最后一个元素。第二个循环的第一次迭代将“double”赋值给$word,这覆盖了最后一个元素。第二次迭代将“toil”赋值给$word,再次覆盖最后一个元素。当循环读取最后一个元素的值时,它已经被多次覆盖了。要深入了解这种行为,我建议阅读Johannes Schlüter关于这个主题的博客文章,“References and foreach”。您还可以运行这个稍微修改过的版本并检查其输出,以便更好地了解PHP正在做什么:

<?php
$spell = array("double", "toil", "trouble", "cauldron", "bubble");
foreach ($spell as &$word) {
    $word = ucfirst($word);
}
foreach ($spell as $word) {
    echo $word . "n";
}
登录后复制
登录后复制
登录后复制

亚瑟那天晚上吸取了一个非常重要的教训,并使用数组的键将字符串重新赋值来修复了他的代码:

<code>Double
Toil
Trouble
Cauldron
Cauldron</code>
登录后复制
登录后复制
登录后复制

幽灵数据库连接

PHP越来越多的被要求不仅仅是每天生成网页。用PHP编写的shell脚本数量正在增加,这些脚本执行的任务也越来越复杂,因为开发人员看到了整合开发语言的优点。通常情况下,这些脚本的性能是可以接受的,为了方便而进行的权衡是可以证明的。因此,苏珊正在编写一个并行处理任务,其代码类似于以下代码:

<?php
$spell = array("double", "toil", "trouble", "cauldron", "bubble");
foreach ($spell as &$word) {
    $word = ucfirst($word);
}
var_dump($spell);
foreach ($spell as $word) {
    echo join(" ", $spell) . "n";
}
登录后复制

她的代码fork了子进程来并行执行一些长时间运行的工作,而父进程继续监控子进程,并在所有子进程终止时报告结果。

<?php
foreach ($spell as $key => $word) {
    $spell[$key] = ucfirst($word);
}
登录后复制

然而,苏珊的领导要求她将状态信息记录到日志中,而不是输出到标准输出。苏珊使用已经包含在公司代码库中的单例模式PDO数据库连接机制扩展了她的代码。

#! /usr/bin/env php
<?php
$pids = array();
foreach (range(0, 4) as $i) {
    $pid = pcntl_fork();
    if ($pid > 0) {
        echo "Fork child $pid.n";
        // record PIDs in reverse lookup array
        $pids[$pid] = true;
    } else if ($pid == 0) {
        echo "Child " . posix_getpid() . " working...n";
        sleep(5);
        exit;
    }
}
// wait for children to finish
while (count($pids)) {
    $pid = pcntl_wait($status);
    echo "Child $pid finished.n";
    unset($pids[$pid]);
}
echo "Tasks complete.n";
登录后复制

苏珊期望看到timings表中的行被更新;“start time”行应该列出整个进程启动的时间戳,“stop time”行应该列出所有进程完成的时间戳。不幸的是,执行抛出了异常,数据库没有反映她的预期。

<code>Fork child 1634.
Fork child 1635.
Fork child 1636.
Child 1634 working...
Fork child 1637.
Child 1635 working...
Child 1636 working...
Fork child 1638.
Child 1637 working...
Child 1638 working...
Child 1637 finished.
Child 1636 finished.
Child 1638 finished.
Child 1635 finished.
Child 1634 finished.
Tasks complete.</code>
登录后复制
#! /usr/bin/env php
<?php
$db = Db::connection();
$db->query("UPDATE timings SET tstamp=NOW() WHERE name='start time'");

$pids = array();
foreach (range(0, 4) as $i) {
    ...
}
while (count($pids)) {
    ...
}

$db->query("UPDATE timings SET tstamp=NOW() WHERE name='stop time'");

class Db
{
    protected static $db;

    public static function connection() {
        if (!isset(self::$db)) {
            self::$db = new PDO("mysql:host=localhost;dbname=test",
                "dbuser", "dbpass");
            self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        return self::$db;
    }
}
登录后复制

像亚瑟的数组一样,苏珊的数据库也闹鬼了吗?好吧,如果我给你以下线索,看看你能不能把这个谜团拼凑起来:1. 当一个进程fork时,父进程会被复制为子进程。然后,这些复制的进程从那时起并行执行。2. 静态成员在类的所有实例之间共享。

PDO连接被包装为单例,因此应用程序中对它的任何引用都指向内存中的相同资源。DB::connection()首先返回对象引用,父进程fork,子进程继续处理,而父进程等待,子进程终止并PHP清理使用的资源,然后父进程尝试再次使用数据库对象。连接到MySQL的连接已在子进程中关闭,因此最终调用失败。在最终日志记录查询之前天真地尝试再次获取连接不会帮助苏珊,因为会返回相同的失效PDO实例,因为它是一个单例。我建议避免使用单例——它们实际上只不过是花哨的面向对象的全局变量,这会使调试变得困难。即使在我们的例子中,连接仍然会被子进程关闭,但如果在第二个查询之前调用DB::connection(),如果不用单例,它至少会返回一个新的连接。但更好的方法是理解在fork时执行环境是如何克隆的,以及各种资源如何在所有进程中受到影响。在这种情况下,最好在fork子进程后在父进程中连接到数据库,子进程如有需要则自行连接。连接不应该共享。

<code>PHP Fatal error:  Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away' in /home/susanbrown/test.php:21
Stack trace:
#0 /home/susanbrown/test.php(21): PDO->query('UPDATE timers S...')
#1 {main}</code>
登录后复制

弗兰肯斯坦博士的API

玛丽·雪莱的《弗兰肯斯坦》讲述的是一个科学家创造生命的故事,但他对其丑陋感到厌恶,于是抛弃了它。在一些不必要的死亡和破坏之后,弗兰肯斯坦博士追捕他的创造物,直到世界的尽头,试图摧毁它。我们许多人都赋予了如此丑陋的代码以生命,以至于我们后来希望自己能逃离它——代码如此丑陋、如此愚钝、如此混乱,以至于让我们想呕吐,但它只想要爱和理解。几年前,我一直在玩弄一个关于数据库接口的想法,以及如果它们更严格地遵守Unix的“一切都是文件”的哲学,它们会是什么样子:查询将被写入“文件”,结果集将从“文件”中读取。一件事导致另一件事,经过我自己的一些死亡和破坏性编码之后,我编写了以下这个与我最初的想法几乎没有关系的类:

<?php
$spell = array("double", "toil", "trouble", "cauldron", "bubble");
foreach ($spell as &$word) {
    $word = ucfirst($word);
}
foreach ($spell as $word) {
    echo $word . "n";
}
登录后复制
登录后复制
登录后复制

结果是天才的,但令人厌恶:一个看起来像对象(没有真正的API方法)、数组或字符串的实例……

<code>Double
Toil
Trouble
Cauldron
Cauldron</code>
登录后复制
登录后复制
登录后复制

我此后不久写了一篇博客,并将其标记为邪恶的。看到它的朋友和同事几乎都做出了同样的反应:“太棒了!现在把它杀了……用火烧死它。” 但多年以来,我承认我对它有所软化。它真正违反的唯一规则是程序员对query()result()等平淡无奇的命名方法的期望。相反,它使用查询字符串本身作为查询方法,对象是接口,结果集是结果。当然,它并不比过度泛化的ORM接口更糟糕,ORM接口将select()where()方法链接在一起,看起来像SQL查询,但有更多的->。也许我的类并没有那么邪恶?也许它只是想被爱?我当然不想死在北极!

结束语

我希望您喜欢这篇文章,并且这些例子不会给您带来(太多)噩梦!我相信您也有自己关于闹鬼或可怕代码的故事,无论您身在何处,都不需要让节日的乐趣消失,因此请随时在下面的评论中分享您可怕的PHP故事!图片来自Fotolia

(以下为FAQ,已根据原文内容调整和精简)

关于“Spooky Scary PHP”的常见问题

什么是“Spooky Scary PHP”?

“Spooky Scary PHP”是一种独特的PHP编码方法,它涉及使用非常规或意想不到的方法来实现某些结果。这可能包括使用鲜为人知的函数、利用语言中的特性,甚至使用看起来不应该工作但确实有效的代码。这是一种有趣且令人兴奋的方式,可以探索PHP的深度,并且经常会带来令人惊讶和启迪的发现。

如何开始学习“Spooky Scary PHP”?

学习“Spooky Scary PHP”的最佳方法是对PHP基础知识有扎实的理解。一旦您对基础知识感到满意,就可以开始探索该语言更晦涩的角落。阅读关于“Spooky Scary PHP”的文章、教程和论坛讨论也可能非常有帮助。记住,目标不是编写高效或实用的代码,而是以更深入的方式探索和理解语言。

“Spooky Scary PHP”是一种好的实践吗?

“Spooky Scary PHP”通常不被认为是编写生产代码的良好实践。它通常涉及使用效率低下、不清楚或不可预测的函数或技术。但是,它可能是学习更多关于该语言以及挑战您对PHP的理解的一种好方法。它更像是一种学习工具和有趣的实验,而不是一种实用的编码风格。

“Spooky Scary PHP”有害吗?

虽然“Spooky Scary PHP”既有趣又有教育意义,但务必负责任地使用它。在“Spooky Scary PHP”中使用的一些技术如果在实时环境中使用,可能会造成危害,例如那些利用语言中的特性或错误的技术。务必彻底测试您编写的任何代码,并且永远不要在项目的重要部分使用“Spooky Scary PHP”技术。

以上是怪异的可怕php的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1653
14
CakePHP 教程
1413
52
Laravel 教程
1304
25
PHP教程
1251
29
C# 教程
1224
24
在PHP API中说明JSON Web令牌(JWT)及其用例。 在PHP API中说明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息,主要用于身份验证和信息交换。1.JWT由Header、Payload和Signature三部分组成。2.JWT的工作原理包括生成JWT、验证JWT和解析Payload三个步骤。3.在PHP中使用JWT进行身份验证时,可以生成和验证JWT,并在高级用法中包含用户角色和权限信息。4.常见错误包括签名验证失败、令牌过期和Payload过大,调试技巧包括使用调试工具和日志记录。5.性能优化和最佳实践包括使用合适的签名算法、合理设置有效期、

PHP 8.1中的枚举(枚举)是什么? PHP 8.1中的枚举(枚举)是什么? Apr 03, 2025 am 12:05 AM

PHP8.1中的枚举功能通过定义命名常量增强了代码的清晰度和类型安全性。1)枚举可以是整数、字符串或对象,提高了代码可读性和类型安全性。2)枚举基于类,支持面向对象特性,如遍历和反射。3)枚举可用于比较和赋值,确保类型安全。4)枚举支持添加方法,实现复杂逻辑。5)严格类型检查和错误处理可避免常见错误。6)枚举减少魔法值,提升可维护性,但需注意性能优化。

描述扎实的原则及其如何应用于PHP的开发。 描述扎实的原则及其如何应用于PHP的开发。 Apr 03, 2025 am 12:04 AM

SOLID原则在PHP开发中的应用包括:1.单一职责原则(SRP):每个类只负责一个功能。2.开闭原则(OCP):通过扩展而非修改实现变化。3.里氏替换原则(LSP):子类可替换基类而不影响程序正确性。4.接口隔离原则(ISP):使用细粒度接口避免依赖不使用的方法。5.依赖倒置原则(DIP):高低层次模块都依赖于抽象,通过依赖注入实现。

会话如何劫持工作,如何在PHP中减轻它? 会话如何劫持工作,如何在PHP中减轻它? Apr 06, 2025 am 12:02 AM

会话劫持可以通过以下步骤实现:1.获取会话ID,2.使用会话ID,3.保持会话活跃。在PHP中防范会话劫持的方法包括:1.使用session_regenerate_id()函数重新生成会话ID,2.通过数据库存储会话数据,3.确保所有会话数据通过HTTPS传输。

解释PHP中的晚期静态绑定(静态::)。 解释PHP中的晚期静态绑定(静态::)。 Apr 03, 2025 am 12:04 AM

静态绑定(static::)在PHP中实现晚期静态绑定(LSB),允许在静态上下文中引用调用类而非定义类。1)解析过程在运行时进行,2)在继承关系中向上查找调用类,3)可能带来性能开销。

什么是REST API设计原理? 什么是REST API设计原理? Apr 04, 2025 am 12:01 AM

RESTAPI设计原则包括资源定义、URI设计、HTTP方法使用、状态码使用、版本控制和HATEOAS。1.资源应使用名词表示并保持层次结构。2.HTTP方法应符合其语义,如GET用于获取资源。3.状态码应正确使用,如404表示资源不存在。4.版本控制可通过URI或头部实现。5.HATEOAS通过响应中的链接引导客户端操作。

您如何在PHP中有效处理异常(尝试,捕捉,最后,投掷)? 您如何在PHP中有效处理异常(尝试,捕捉,最后,投掷)? Apr 05, 2025 am 12:03 AM

在PHP中,异常处理通过try,catch,finally,和throw关键字实现。1)try块包围可能抛出异常的代码;2)catch块处理异常;3)finally块确保代码始终执行;4)throw用于手动抛出异常。这些机制帮助提升代码的健壮性和可维护性。

PHP中的匿名类是什么?您何时可以使用它们? PHP中的匿名类是什么?您何时可以使用它们? Apr 04, 2025 am 12:02 AM

匿名类在PHP中的主要作用是创建一次性使用的对象。1.匿名类允许在代码中直接定义没有名字的类,适用于临时需求。2.它们可以继承类或实现接口,增加灵活性。3.使用时需注意性能和代码可读性,避免重复定义相同的匿名类。

See all articles