Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:131051 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 lists.php.net (Postfix) with ESMTPS id DB1C81A00BC for ; Fri, 29 May 2026 10:48:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1780051694; bh=np+whDZBLp2UxG+nPntAulT/+eM1ecUmokT1ciPzZck=; h=Date:Subject:To:References:From:In-Reply-To:From; b=O+sWc3Kj28TxdbqlazvVNC7rm5QtGbe35dmDwjnOyzgwtjTz4BOoPfatGzz/kaib9 oE9ZdOacaGcqOrpHCiAjMMYrFFMpq3nvjvsJ0yCklFyUwsR9QSLEk7jW4cnf+nxDqR VSVXt7Kn7oxk7OPEiO/INcqyaFcL03Nzq29OPZrMM6B2uIluWBd/aTjLsMMZeTtrSH RncLnRzkkvu6mJrNh95AiTqtcI/7CeARYKcfarAOQq4sxFO6zDMaiNfjRSTvibvZok RVz0MKG7idqVbvjMQrtmja5pYjJ8+3Z4x+l3QpowVGxmIHpxPN7y8K4p8syM/0zPbm TIleq/cvpvzxA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id F06A0180040 for ; Fri, 29 May 2026 10:48:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, FREEMAIL_REPLY,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) (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 ; Fri, 29 May 2026 10:48:12 +0000 (UTC) Received: by mail-wr1-f41.google.com with SMTP id ffacd0b85a97d-45ee5cdbd28so1042021f8f.1 for ; Fri, 29 May 2026 03:48:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780051686; x=1780656486; darn=lists.php.net; h=in-reply-to:from:references:to:content-language:subject:user-agent :mime-version:date:message-id:from:to:cc:subject:date:message-id :reply-to; bh=uFnUV+tcjpIdZmP72kI6pr9eOCWtTkQtZTQ4suRZ2ko=; b=IFMAbfxEGYTgQLVLH6StjzSyALLUZ9EoqB8UNoboC/sB9Oj4yz1zhO2t7uCc9YVDSY xDObpV4aH3sMKXA97+yuSMC2DgmSR4T+8SBiPdKC5yL2gaMwmMwgNhqTOfR8XYvRIzBC eu4VyQU7EeCgNqd0Qt6Ja4EOAvoctNBZqzka8ieJ7QNN/0xSSSrXPC/Mze8mygE5xseG /cIafgPXPyg+cOcnVCD87AN984RDSfxfKNWH4flPEMOkzgM5Nerc8MOF2v6BV580fPBS v7fmmUDI53vg04QONUh77623Tqtr+L1CpYkaRGjCsvxl2F4cf9b0typv4Bmh3ro1RgU9 UTmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780051686; x=1780656486; h=in-reply-to:from:references:to:content-language:subject:user-agent :mime-version:date:message-id:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uFnUV+tcjpIdZmP72kI6pr9eOCWtTkQtZTQ4suRZ2ko=; b=WcXjjKVysvkOq6n8PslhK9cdfZvB/uHBnQ48JhKSTq/XD8aSsgIT4VmjGktmeJHJeQ 3WJlNKMjYWXaes4RKjHNbLDOC1qsnX/FR0H5ix0y6HKGfzN11MKklIqRPWl+jtcX1kbA orJYJ9rw31xU4RKbSGdrLwRfySvXGgJ2K+687wblmvDMKvgBIHTjb3llV6cJZPWCbWw0 8rFqXLoB9VZK/6kJ3385fnqAotSOT0Ito7rzEB9PNihA27udV38ZsVay7xHa/tjiTlzJ f8YOYa1HZSr41X2LdlrcjgnuQWrv/x0y9NhhxMhy4k4ijeHvQFMbF4vBvGnBiuumLrQh mgNg== X-Gm-Message-State: AOJu0Yx4qOUzedisj9b+uy8ivyysf6unsF4SOqlLS0/Ju+vPmZEiLE5T xCP0/dmpk7PT43tHXv5NKSvxj4+Q7We+vZoo6rXHImbGtPW/zySn1MonE6XE7w== X-Gm-Gg: Acq92OESq7ns/HHYI3LRbo0FTuASQxqwd4VwKomKcGIhfRRFZahJanTGexCfdQCBudu RZESLvgy9QI9ovQTCifs97RTbfmNPnfC3W8a8fgf34BTsNUcydunkqtMa7iuIX5M3/vNHfK6nV7 V8YWUcGyRAYQqtRMqN5wMM5ZcPalw12rTHXnazKPIPaC8kF4ufHElcbKGlt2JgI42I1ZA21ve8M H2B4k62HPFkhPuQ+nkKb7hm7QQpUj39p+w1AgrbhIOobs44ZPGkvogV3qZ4ed/L7/olFNL3T4RS MJi1DASRP9wlusff4w1n33AR/+SFZxOtotJMenPiOeccz9YIbkpGHv3KMrM0q4zkiuZvtOP3B+A gQUKN3fXRudgbSa/bTgXjsiKUwoz15V0fLYnz/IYbUafydMQygpJRF4HCv5C+rKET4ceeHssLPE 30z/+2KfIy1cLRBXWs5UuFqt68osqGXSqjKTV7UIZJMYFcSsQyS4vfHwxk0kSCt4NZVuvLjcRtf kiCO7xItTpNjfNDyJMWw293gqiT X-Received: by 2002:a05:600c:a30f:b0:490:4b89:5372 with SMTP id 5b1f17b1804b1-4909c62603cmr26758985e9.11.1780051686241; Fri, 29 May 2026 03:48:06 -0700 (PDT) Received: from [192.168.153.252] (laubervilliers-656-1-64-146.w82-127.abo.wanadoo.fr. [82.127.89.146]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4909d5f2347sm31754225e9.0.2026.05.29.03.48.05 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 29 May 2026 03:48:05 -0700 (PDT) Content-Type: multipart/alternative; boundary="------------xbMUxL9g8mzN655WXZksL6hz" Message-ID: Date: Fri, 29 May 2026 12:48:04 +0200 Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PHP-DEV] AW: [Pre-RFC] Pure-code source files via .phpc extension Content-Language: fr To: internals@lists.php.net References: <9E95EA03-B86A-D248-A980-B1E838F94C13@hxcore.ol> In-Reply-To: <9E95EA03-B86A-D248-A980-B1E838F94C13@hxcore.ol> From: pierstoval@gmail.com (Alex Rock) This is a multi-part message in MIME format. --------------xbMUxL9g8mzN655WXZksL6hz Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Le 27/05/2026 à 14:12, hmennen90@gmail.com a écrit : > Hi internals, > > I intend to submit an RFC introducing a new file extension for > pure-code PHP source files (no leading to gather feedback before drafting. > > Proposal in brief: > > Files ending in .phpc would be parsed starting in ST_IN_SCRIPTING > state. No tags permitted inside such files. Existing .php > files and their semantics are completely unchanged. This is purely > additive and BC-clean. > > Motivation: > > PHP's mixed-mode default reflects its 1995 templating origins. Since > PHP 7+, the language has evolved into a credible general-purpose tool: > strict types, enums, readonly classes, property hooks, JIT > compilation. I personally maintain PHPolygon, a CPU-bound 3D engine > written in PHP – a use case where the templating heritage is pure > ceremony. Other modern uses (CLI tooling, queue workers, code > generators) share this pattern. A dedicated pure-code file format > would be a small but meaningful acknowledgment that PHP-as-language is > now a first-class use case alongside PHP-as-template. > > Prior art and what's different: > > I have read both rfc/source_files_without_opening_tag (Boutell, 2012, > abandoned by author) and rfc/nophptags (Ohgaki, 2014, inactive). My > proposal deliberately avoids what I believe were the two design > choices that killed them: > > - No new include syntax (Boutell's AS keyword). Extension-based > detection only. > - No php.ini-based mode switch (Ohgaki's template_mode). No global > config side effects. > - No security framing. The mode-switch overhead is parse-time only and > OPcache/JIT eliminate it in practice; this proposal is about > conceptual clarity and tooling, not performance or LFI mitigation. > > Implementation: > > I will write and maintain the implementation patch. Initial scope: > extension registration in zend_compile_file, lexer state > initialization, OPcache awareness, CLI support, and rejection of > tokens inside .phpc files. I will also coordinate with > Composer maintainers ahead of RFC submission to confirm autoload support. > > Open questions for the list: > > 1. Is the .phpc extension acceptable as the disambiguator, or is there > appetite for something else (e.g. shebang line, declare directive – > both of which I think are worse, but I'd hear the case)? > 2. Should #! shebang lines and UTF-8 BOM be permitted before the > implicit scripting state begins? My intent is yes for both. > 3. Should __halt_compiler() retain its current behavior in .phpc > files? My intent is yes. > > I welcome substantive critique. If the concept itself is unwanted, I > would rather know now than discover it during a vote. > > Thanks. > > Hendrik Mennen > Maintainer, PHPolygon > Hey, I'm answering with a new thread here about the potential other ways to handle this. If your goal is to make sure a PHP file has no accidental "echo", here is my proposal: a `declare(pure);` statement that would prevent all potential calls to "echo" (as well as many notice/warning errors). Nothing more. All the constraints you are talking about would be fixed with something like that, on the price of having one single line containing "` in the file, obviously, even as last file character (this prevents potential accidental line feed after `?>` too) * The file must not contain call statements, only definitions. Of course, this implies `echo`, but it also implies `exit`/`die`. * This only allows declarations like `(include|require)(_once)?` `const`, `function`, `namespace`, `class`, `return`, `interface`, `trait`, `use` and `enum`. This also means that constant definitions cannot use global scope other than built-in constants (see later points), and `define()` would be forbidden. * `if`/`else`/`elseif`, `switch` or `match` statements can also be allowed, only if they respect the two previous points. This way, you can still define functions/constants/classes depending on PHP versions. I'm not sure about iterators (`for`, `while`, `do...while` or `foreach`), because I see no proper use-case, but they can still be allowed if they imply no global call statements, which seems unlikely anyway for iterators. * Previous point implies that `try/catch/finally`, `break` or `continue` or `new` also forbidden in the global scope (as a reminder). * `return` is allowed, because it might stop the file's execution process in case one doesn't want to define classes, functions and whatnot, and it can even return a value, as long as this value is either a literal or a built-in constant (see later). You couldn't return the result of a function call, for example, since they are disallowed in the file. * Since the file must not contain statements, the global scope of the file must not refer to any variable, and must not define variables either. Even superglobals. * The file's global scope can refer to constants defined internally by PHP or its extensions. (this means every constant that isn't in the `user` key when calling `get_defined_constants(true)` in PHP, as well as magic constants like `__DIR__`). Only constants that are *always* available at compile-time will be checked. This way, it can't accidentaly trigger an `Undefined constant` warning for userland, but it *can* trigger one if a native extension isn't enabled or doesn't have said constant, which can also be detected at compile-time. * The previous point implies that when a pure PHP file is compiled, constants can be checked. It would put more load on the compile-time engine, but it allows optimizing these values, therefore it /might/ improve runtime performances just a bit. Especially since constant checks also search in the namespace (and no existing PHP extension seem to define namespaced constants, and even if they did, the compiler would still be able to have access to them anyway since they are engine-based, not runtime-based, and users would have to either do `use const` or write the fully-qualified constant name). * Maybe other constraints I'm not yet thinking about, feel free to suggest, as I might have forgotten something. As said, this adds a new layer of complexity to an already big compiling process, but this brings a lot of advantages: * Your file is still pure PHP * Can still be interpreted by IDEs * Doesn't need a different file extension * Is explicitly visible at the beginning of the file when you open it * Still allows things that frameworks do for conditional function/class declaration * Potential compile-time built-in constant optimization (if not already done by the engine, I didn't search for this) * Everything that is not global/namespace-scope (functions, classes, etc.) can still contain whatever code they need. * All potential errors will be compile-time errors, therefore if the file is "correct", compiling it definitely means that it has its place in the opcache for a very long time as no runtime can alter its global context. * Having no actual call statements in the global/namespaced scope ensures no "echo", but overall has absolutely zero runtime impact other than compile-time errors, since there cannot be notice/warning errors that might also pollute the current buffer. (I might have forgotten what else can throw a notice/warning, but feel free to correct me if I do) There are only a tiny amount of drawbacks to this (from what I've thought about so far): * All "pure PHP" files will have to begin with `
Le 27/05/2026 à 14:12, hmennen90@gmail.com a écrit :
Hi internals,

I intend to submit an RFC introducing a new file extension for pure-code PHP source files (no leading <?php required) and would like to gather feedback before drafting.

Proposal in brief:

Files ending in .phpc would be parsed starting in ST_IN_SCRIPTING state. No <?php or ?> tags permitted inside such files. Existing .php files and their semantics are completely unchanged. This is purely additive and BC-clean.

Motivation:

PHP's mixed-mode default reflects its 1995 templating origins. Since PHP 7+, the language has evolved into a credible general-purpose tool: strict types, enums, readonly classes, property hooks, JIT compilation. I personally maintain PHPolygon, a CPU-bound 3D engine written in PHP – a use case where the templating heritage is pure ceremony. Other modern uses (CLI tooling, queue workers, code generators) share this pattern. A dedicated pure-code file format would be a small but meaningful acknowledgment that PHP-as-language is now a first-class use case alongside PHP-as-template.

Prior art and what's different:

I have read both rfc/source_files_without_opening_tag (Boutell, 2012, abandoned by author) and rfc/nophptags (Ohgaki, 2014, inactive). My proposal deliberately avoids what I believe were the two design choices that killed them:

- No new include syntax (Boutell's AS keyword). Extension-based detection only.
- No php.ini-based mode switch (Ohgaki's template_mode). No global config side effects.
- No security framing. The mode-switch overhead is parse-time only and OPcache/JIT eliminate it in practice; this proposal is about conceptual clarity and tooling, not performance or LFI mitigation.

Implementation:

I will write and maintain the implementation patch. Initial scope: extension registration in zend_compile_file, lexer state initialization, OPcache awareness, CLI support, and rejection of <?php/?> tokens inside .phpc files. I will also coordinate with Composer maintainers ahead of RFC submission to confirm autoload support.

Open questions for the list:

1. Is the .phpc extension acceptable as the disambiguator, or is there appetite for something else (e.g. shebang line, declare directive – both of which I think are worse, but I'd hear the case)?
2. Should #! shebang lines and UTF-8 BOM be permitted before the implicit scripting state begins? My intent is yes for both.
3. Should __halt_compiler() retain its current behavior in .phpc files? My intent is yes.

I welcome substantive critique. If the concept itself is unwanted, I would rather know now than discover it during a vote.

Thanks.

Hendrik Mennen
Maintainer, PHPolygon



Hey, I'm answering with a new thread here about the potential other ways to handle this.

If your goal is to make sure a PHP file has no accidental "echo", here is my proposal: a `declare(pure);` statement that would prevent all potential calls to "echo" (as well as many notice/warning errors).
Nothing more.
All the constraints you are talking about would be fixed with something like that, on the price of having one single line containing "<?php" , and all the rest being PHP code.

Just like the `declare(strict_types=1);` statement enforces types usage in said file calls, declaring a file to be pure would only add constraints to how the file is written.

The `declare(pure);` statement would enforce this at file-level:

  • Forbid the use of `?>` in the file, obviously, even as last file character (this prevents potential accidental line feed after `?>` too)
  • The file must not contain call statements, only definitions. Of course, this implies `echo`, but it also implies `exit`/`die`.
  • This only allows declarations like `(include|require)(_once)?` `const`, `function`, `namespace`, `class`, `return`, `interface`, `trait`, `use` and `enum`. This also means that constant definitions cannot use global scope other than built-in constants (see later points), and `define()` would be forbidden.
  • `if`/`else`/`elseif`, `switch` or `match` statements can also be allowed, only if they respect the two previous points. This way, you can still define functions/constants/classes depending on PHP versions. I'm not sure about iterators (`for`, `while`, `do...while` or `foreach`), because I see no proper use-case, but they can still be allowed if they imply no global call statements, which seems unlikely anyway for iterators.
  • Previous point implies that `try/catch/finally`, `break` or `continue` or `new` also forbidden in the global scope (as a reminder).
  • `return` is allowed, because it might stop the file's execution process in case one doesn't want to define classes, functions and whatnot, and it can even return a value, as long as this value is either a literal or a built-in constant (see later). You couldn't return the result of a function call, for example, since they are disallowed in the file.
  • Since the file must not contain statements, the global scope of the file must not refer to any variable, and must not define variables either. Even superglobals.
  • The file's global scope can refer to constants defined internally by PHP or its extensions. (this means every constant that isn't in the `user` key when calling `get_defined_constants(true)` in PHP, as well as magic constants like `__DIR__`). Only constants that are always available at compile-time will be checked. This way, it can't accidentaly trigger an `Undefined constant` warning for userland, but it can trigger one if a native extension isn't enabled or doesn't have said constant, which can also be detected at compile-time.
  • The previous point implies that when a pure PHP file is compiled, constants can be checked. It would put more load on the compile-time engine, but it allows optimizing these values, therefore it might improve runtime performances just a bit. Especially since constant checks also search in the namespace (and no existing PHP extension seem to define namespaced constants, and even if they did, the compiler would still be able to have access to them anyway since they are engine-based, not runtime-based, and users would have to either do `use const` or write the fully-qualified constant name).
  • Maybe other constraints I'm not yet thinking about, feel free to suggest, as I might have forgotten something.


As said, this adds a new layer of complexity to an already big compiling process, but this brings a lot of advantages:

  • Your file is still pure PHP
  • Can still be interpreted by IDEs
  • Doesn't need a different file extension
  • Is explicitly visible at the beginning of the file when you open it
  • Still allows things that frameworks do for conditional function/class declaration
  • Potential compile-time built-in constant optimization (if not already done by the engine, I didn't search for this)
  • Everything that is not global/namespace-scope (functions, classes, etc.) can still contain whatever code they need.
  • All potential errors will be compile-time errors, therefore if the file is "correct", compiling it definitely means that it has its place in the opcache for a very long time as no runtime can alter its global context.
  • Having no actual call statements in the global/namespaced scope ensures no "echo", but overall has absolutely zero runtime impact other than compile-time errors, since there cannot be notice/warning errors that might also pollute the current buffer. (I might have forgotten what else can throw a notice/warning, but feel free to correct me if I do)


There are only a tiny amount of drawbacks to this (from what I've thought about so far):

  • All "pure PHP" files will have to begin with `<?php declare(pure);`
  • Potential tiny compile-time performance drop, because all global statements would have to be checked and analysed. And a bit more if global scope constants are also analysed.

To me, this seems like the best compromise with your proposal.



--------------xbMUxL9g8mzN655WXZksL6hz--