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:
- 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)?- Should #! shebang lines and UTF-8 BOM be permitted before the
implicit scripting state begins? My intent is yes for both.- 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 impliesecho, but it also impliesexit/die. - This only allows declarations
like(include|require)(_once)?const,function,namespace,
class,return,interface,trait,useandenum. This also
means that constant definitions cannot use global scope other than
built-in constants (see later points), anddefine()would be
forbidden. -
if/else/elseif,switchormatchstatements 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
orforeach), 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
orcontinueornewalso forbidden in the global scope (as a
reminder). -
returnis 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
userkey when callingget_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 anUndefined constantwarning 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 douse constor 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.