Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126011 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 4A5FC1A00BD for ; Wed, 20 Nov 2024 21:22:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1732137917; bh=CWJ85+MTpmNbdR9OVpW9xoNM4R64uiBnL03k+dbBuxo=; h=From:Date:Subject:To:From; b=iW9vIgS2AD7/F5KOcHBBd7crvPni86mSBh4nV2/lOjh2TonZQUO8VTAUesxSJvB7u oxd6vjqsKH6q36cv+ya0M0XyfJ1Ie2iHMqW6NSn1XnsHO0AE6xQZxpuU3b0XT4Zmn/ YTk/LimobOuceH1ThSh5kQOBW/4Mjxc+IcbZvoWjbVcIl1uC0uTWjAm6+660xkJFoJ UcsHqR6iU/PwFJaFheypa4j46SJhQKq22DWy3HZOGXLv6Ms5tnqiQrg6EvGSz7MZ4d eefxVV699Dos0Tk/9oP0L5mp5srMuXkj50aup2vn/0eZ/Zy+g17ujorp8g9NDHy8ay M5pGHhZQ99xwA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id D67DF18004D for ; Wed, 20 Nov 2024 21:25:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-qt1-f176.google.com (mail-qt1-f176.google.com [209.85.160.176]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 20 Nov 2024 21:25:16 +0000 (UTC) Received: by mail-qt1-f176.google.com with SMTP id d75a77b69052e-46098928354so1401011cf.1 for ; Wed, 20 Nov 2024 13:22:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1732137755; x=1732742555; darn=lists.php.net; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=dzBGkrRxJ6OnNX9169QDL5yf8tCm1jFeoi7zxxicb1w=; b=F3ZpRt50NWgwJegKHK+DB6KziqqzAOo91UYWa/lE6LN+HUJOaNxt4EdYhpfulYCvL/ 3gmfO8ERtTKiAAvKxFh25Ak1LDkuOURcKoDDAgCxqdgQO5Qa+hHTOp8KrtWVaZz2Ost4 G/bUpLfRsGFhyBLxePaePLEMPqCjq53gJt0M3htTZxTUhkTPlDcoMF5aRvR2qyUskpnE ElDucnb5w65y1h1hT4dQ2qvh1Vf2ocdW+FlB5TlFdpSfXIAdAKnI+EgtJsl8E7aFBmVC RT2e9jllqJfWX1i9o7HpWzAqK1R626kCHhIyJYA6621tId3Nj4Y2i4kvfFq/e6YObyrK FD1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1732137755; x=1732742555; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=dzBGkrRxJ6OnNX9169QDL5yf8tCm1jFeoi7zxxicb1w=; b=NOo9D836tAv213OLIWe/mRQiEFEngwnsvKSldyZ9hQksmd698vKThEI9sJCTUPAWSn wElVj43rW7qqiQ5/oGYoDMahZ7AIFijSChE081j/dvOPNNIFx7aQXqppg5gi7el19CR9 rMIWqISkt8DgCoYCXmf9/bSqh13ef1pzo17K6xT6rgv0aFbJ8+3JL2zsjMar6ptmPzoU sxVJY9nPuTHjaLTSeodKGBDQW0RJ8N4TPMcyWelQz+574ggt9H/Jw9jU7lSUmhhsF+Vj SJUHVvipHQkE2b/Whjm6xfmwS2Ycm1vMrhvk10PoPWfdD+dsgG+wX7U05lR52rPxOB/p d9pA== X-Gm-Message-State: AOJu0YzubKcNSbVvu4gP7Nw3FmbXZ39rLlLrj/nW3UWaAnAvsCyZ9noE OZpEGbJZryCejcPJmhUK30LFjWK+Hxwt0tvYnbsQ5uGRIetanOaWf6v8w4X+yKy30iC3a7NumsG 9TBGtZHJXQFTF3JeKVlRBXM68ncxQCehM1RY= X-Gm-Gg: ASbGncsEZXj8ebtPPrRDPqO6AoJXVCS+mFG5VCvbgIhe1Z4NTyAkW7kmoYMMp1oB8xH Jo4+CVRLb0WtQJfKrIJckRcu3BQamG+4V3lBHz1lj5Ume2BNoqaATXzXVggUkTYdG/g== X-Google-Smtp-Source: AGHT+IH1U3/TNyy9lJaQN5DY5PyR6ZM//VLmOegOFs7VlokaivEwF1s1tuVKq8+shjn2Q5ezVWLg65JSiOY4/W6qaLk= X-Received: by 2002:a05:6214:c86:b0:6d4:142d:8119 with SMTP id 6a1803df08f44-6d437869c9fmr56973346d6.42.1732137754893; Wed, 20 Nov 2024 13:22:34 -0800 (PST) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Wed, 20 Nov 2024 22:22:23 +0100 Message-ID: Subject: [PHP-DEV] opcache_compile_file() declares top-level functions To: PHP internals Content-Type: text/plain; charset="UTF-8" From: tovilo.ilija@gmail.com (Ilija Tovilo) Hi everyone We recently received a bug report regarding the behavior of opcache_compile_file() [1]. The documentation specifies: https://www.php.net/manual/en/function.opcache-compile-file.php > This function compiles a PHP script and adds it to the opcode cache without executing it. This can be used to prime the cache after a Web server restart by pre-caching files that will be included in later requests. Arguably, "without executing it" implies that, aside from putting the script into opcache, there are no other observable side-effects. This assumption is currently incorrect. To be more specific, opcache_compile_file() differs from require in two ways: * The "main" function of the script (containing all the code at the top-level) is not executed. * Classes will not be added to the class table. Confusingly, top-level functions _will_ be added to the function table. This has some weird consequences: * opcache_compile_file() can be called multiple times on files containing classes. However, the same is not true for files containing functions. The second call will lead to a function redeclaration error. ```php // index.php opcache_compile_file(__DIR__ . '/test.php'); opcache_compile_file(__DIR__ . '/test.php'); // test.php class Foo {} // No problem function foo() {} // Fatal error: Cannot redeclare function foo() ``` * Similarly, after calling opcache_compile_file() on files containing classes, the same file may later be required without issues. This does not work for files containing functions for the same reason. ```php // index.php opcache_compile_file(__DIR__ . '/test.php'); require __DIR__ . '/test.php'; // test.php class Foo {} // No problem function foo() {} // Fatal error: Cannot redeclare function foo() ``` * Mixing functions with classes is incompatible. An attempt to use one of the classes from one of the functions will either error because of an undeclared class, or trigger the autoloader and include the file again, leading to a function redeclaration error. ```php // index.php spl_autoload_register(function ($name) { if ($name === 'Foo') { require __DIR__ . '/b.php'; } }); opcache_compile_file(__DIR__ . '/test.php'); foo(); // test.php class Foo {} function foo() { // Triggers the autoloader, the autoloader fails due to: // Fatal error: Cannot redeclare function foo() var_dump(new Foo()); } ``` * Functions that are conditionally declared will not be added to the function table, since they are not top-level functions, even if the condition always evaluates to true. ```php // index.php opcache_compile_file(__DIR__ . '/test.php'); foo(); // Uncaught Error: Call to undefined function foo() // test.php if (true) { function foo() {} } ``` This behavior is inconsistent and confusing. Arguably, the correct behavior is to never register functions in opcache_compile_file() to begin with, since opcache_compile_file() exists to prime the cache, rather than execute code. I created a PR with this change [2]. This is breaking, since code using opcache_compile_file() might currently depend on functions being declared and then calling them directly. Such code would have to be adjusted to use require instead. Are there any concerns with making this change for 8.5? Are there any use-cases this would break? Of note is that it may be beneficial to provide a related function that allows compiling files and declaring symbols without executing the "main" function. This could be useful static analysis tools that want to reflect on files without executing them. That said, there are existing solutions in userland that solve this problem in a better way, like BetterReflection [3]. Ilija [1] https://github.com/php/php-src/issues/16668 [2] https://github.com/php/php-src/pull/16862 [3] https://github.com/Roave/BetterReflection