Am 28.05.2026 um 08:36 schrieb Alex Rock pierstoval@gmail.com:
Le 28/05/2026 à 06:32, Ben Ramsey a écrit :
I am trying to understand whether your objection is to extension-
based dispatch specifically, or to the broader idea of the engine
making this decision at all.I don't have a strong objection to the engine making this decision, but
I'd like to hear from others first.Cheers,
BenI think this can be summarised with this:
File extension cannot have a "meaning" for the engine, as the engine does not care about it at all in the first place
All SAPIs will consider a "normal PHP file" as default input, could it be CLI, mod_php for Apache, or anything else (even FrankenPHP)
IMO this means that the default entrypoint for "parsing and executing PHP files" for all SAPIs must not change, but might either be complemented with a boolean arg for "pure PHP"/"no <?php ?> tags" , or the engine must provide another public entrypoint for this that SAPIs could implement or not.An environment variable could be even used for that, and be included in the engine by default, so that all existing SAPIs could be updated at lower cost, but the variable name would have to be checked in all existing Composer packages (something like "PHP_INPUT_PURE=0|1" , or similar, TDB anyway), and would only be used for first included file. Any subsequent call to "include/require" would still behave as before, to avoid BC breaks.
And after that, to make use of this feature in userland projects, I would suggest an "include_pure" (and its "require" + "_once" variants) global keyword, to keep full compatibility everywhere.
WDYT?
Hi Alex,
Thanks for jumping in with concrete alternatives. Let me address each, because I think the most important thing for the RFC draft is to be precise about what the actual constraints are versus what is just the current default.
- File extension cannot have a "meaning" for the engine, as the engine
does not care about it at all in the first place
I would push back on this gently, because I think it is a description of current behavior, not a constraint. The engine does have the filename available in zend_compile_file and in the include/require resolution path. Teaching it to check the extension there is a small, additive change, not a violation of any architectural rule I am aware of. Other languages do this routinely (Python differentiates .py from .pyc at the loader level). So the question is not "can the engine know about extensions" but "should we use that as the signal here." Happy to be told there is an architectural reason I am missing, but if the objection is just "this is not how PHP does it today," then the whole RFC is by definition asking to change that.
- All SAPIs will consider a "normal PHP file" as default input, could
it be CLI, mod_php for Apache, or anything else (even FrankenPHP)
Agreed, and nothing in the proposal changes that. Existing .php files behave exactly as today through every SAPI. The proposal is additive: a new file type that the engine handles differently, with zero impact on the existing default path.
An environment variable could be even used for that, [...] and would
only be used for first included file. Any subsequent call to
"include/require" would still behave as before, to avoid BC breaks.
This is where I see a real problem with the env-var approach. If only the entry file is pure and everything it includes must be mixed-mode, then pure code cannot be shipped as a library. A library author writing pure code could never be loaded from a mixed-mode application, and vice versa. The adoption story dies on contact with Composer.
For pure-code files to be useful in practice, the parsing context has to attach to the file itself, not to the entry-point execution. Otherwise the feature is limited to a single bootstrap script, which is too narrow to justify the engine change.
And after that, to make use of this feature in userland projects, I
would suggest an "include_pure" (and its "require" + "_once" variants)
global keyword
This is essentially Tom Boutell's 2012 approach (rfc/source_files_without_opening_tag), which used a modified include syntax. Boutell ultimately abandoned that RFC, citing concerns that I would summarize as: the caller decides how the file is parsed, not the author. That inverts the usual relationship between a library and its consumer. A library author cannot ship pure-code files with the guarantee that they will be parsed as such, because consumers might forget include_pure and load them with regular include. The same physical file would parse differently depending on how it was loaded, which is a discoverability and tooling nightmare.
That said, I do not want to dismiss include_pure entirely. As a complement to extension-based dispatch, it has merit:
- For stdin and eval-like situations, where no filename exists, an explicit per-call signal is exactly what is needed (Ben suggested php -p for the CLI stdin case, similar idea).
- For interop scenarios where someone wants to force pure-code parsing on a file that does not have the extension, it provides an explicit escape hatch.
What I think does not work is replacing the extension-based mechanism with include_pure, because then we lose the "file declares its own parsing context" property.
A possible synthesis:
- Primary mechanism: file extension (the file itself declares its parsing context, author-controlled).
- CLI complement: -p flag for stdin (Ben's suggestion).
- Userland complement: include_pure or similar for explicit override (your suggestion, but as override not primary).
This gives every entry path a clear answer and keeps author intent intact. I will work this into the RFC draft.
Does the engine-level dispatch via extension still feel wrong to you once the alternative is framed this way, or is your objection more specific to the .phpc letter than to the mechanism?
Hendrik Mennen
Maintainer, PHPolygon