[PHP-DEV] [RFC] Pure-code source files via the .phpc extension

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

On Mon, Jun 15, 2026 at 10:06 AM Hendrik Mennen <hmennen90@gmail.com> wrote:

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

Hi,

It seems something broke with the formatting in the “What is unchanged” section.

Hi Hendrik,

Il 15/06/2026 10:03, Hendrik Mennen ha scritto:

Following the pre-RFC discussion from late May [1], I'm moving the
"pure-code source files" proposal into formal RFC stage as

PHP: rfc:optional_php_tags <PHP: rfc optional_php_tags>

FYI, it seems there's some formatting issue caused by __halt_compiler().

I appreciate the effort, but I'm afraid I think it's unnecessary complication for practically no real-world benefit.

Cheers
--
Matteo Beccati

Am 15.06.2026 um 10:11 schrieb Lynn kjarli@gmail.com:

On Mon, Jun 15, 2026 at 10:06 AM Hendrik Mennen <hmennen90@gmail.com> wrote:

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

Hi,

It seems something broke with the formatting in the “What is unchanged” section.

Hi,

Yeah saw and already fixed it :slight_smile: Thanks

On Mon 15 Jun 2026, 10:03 Hendrik Mennen, <hmennen90@gmail.com> wrote:

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

I tried to catch up on this discussion last night. I see a lot of things that could go wrong with this, but I don’t see a clear motivation for why would we want this change.

Reduced ceremony in pure-code files.
This is a moot point because after the change you still have the ceremony of adding .phpc to every file. Just unnecessary churn.

First-class CLI scripts.
Is this really a big concern? Is it going to work better? Are there things you cannot do today because of it?

Clearer file-mode intent.
We already have that. By convention many people add .php extension to pure PHP files, and .phtml to templates. It’s not mandated by a language and people are free to choose their naming conventions.

Strictly additive.
That’s not entirely true, but that’s beside the point. Why is this one of the main motivations? We don’t add things just because we can. They need to have a purpose.

We already have a good way of writing pure PHP code files. Just add 5 characters at the beginning of the file <?php and the rest of the file will be treated as PHP source code.

I would also like to echo what others said that PHP source code doesn’t have to be within <?php. A text file will be compiled and interpreted by PHP as a print statement. Even in pure PHP code files you may want to escape the PHP context with ?>. Mandating that a file with a certain extension will have these restrictions is worse than having to start every file with <?PHP.

Regards,
Kamil

Am 15.06.2026 um 10:43 schrieb Kamil Tekiela tekiela246@gmail.com:

On Mon 15 Jun 2026, 10:03 Hendrik Mennen, <hmennen90@gmail.com> wrote:

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

I tried to catch up on this discussion last night. I see a lot of things that could go wrong with this, but I don’t see a clear motivation for why would we want this change.

Reduced ceremony in pure-code files.
This is a moot point because after the change you still have the ceremony of adding .phpc to every file. Just unnecessary churn.

First-class CLI scripts.
Is this really a big concern? Is it going to work better? Are there things you cannot do today because of it?

Clearer file-mode intent.
We already have that. By convention many people add .php extension to pure PHP files, and .phtml to templates. It’s not mandated by a language and people are free to choose their naming conventions.

Strictly additive.
That’s not entirely true, but that’s beside the point. Why is this one of the main motivations? We don’t add things just because we can. They need to have a purpose.

We already have a good way of writing pure PHP code files. Just add 5 characters at the beginning of the file <?php and the rest of the file will be treated as PHP source code.

I would also like to echo what others said that PHP source code doesn’t have to be within <?php. A text file will be compiled and interpreted by PHP as a print statement. Even in pure PHP code files you may want to escape the PHP context with ?>. Mandating that a file with a certain extension will have these restrictions is worse than having to start every file with <?PHP.

Regards,
Kamil

Hi Kamil,

Thanks for the substantive pushback - these are exactly the right
questions, and you’re correct on more of them than the RFC as currently
worded admits.

Conceding upfront: “strictly additive” is a necessary property, not a
motivation. I leaned on it too much. Same for the three weaker arguments
(CLI ergonomics, convention clarity, ceremony shift) - none of them
alone justifies the mechanism.

The honest single-sentence motivation:

Most new PHP files written today are pure code (framework classes,
autoloaders, console commands, configuration). For those files the

<?php opener carries zero semantic information. PHP is the only modern mainstream language where it is mandatory. If that friction is not worth solving, that is a legitimate position and I would rather hear that than dress the proposal up to look weightier than it is. Three honest paths from here, and I would find it useful if respondents could say which they are closest to: 1. The friction is real and a small targeted addition is worth shipping. (what the RFC proposes) 2. The friction is real but the mechanism is wrong. (in which case: what signal would you prefer? per-file declare, magic first line, SAPI-only, ...) 3. The friction is not real, or not worth a language change at all. That is a sharper question than re-litigating each motivation in turn, and the answer determines whether to refine, pivot, or withdraw before voting. On your last point - that .phpc "restricts" PHP's text-as-print feature - I think we agree more than disagree. .phpc would not change .php behaviour at all. It is opt-in by filename. Authors who want the templating shape keep using .php, ?> drop-out continues to work inside

.phpc, and the engine on existing files is byte-for-byte identical.
The “restriction” you describe (no top-of-file inline content;

<?php-as-tag rejected inside .phpc) is intentional - it is what makes the mode meaningful for the cases that opt in. I will update the RFC's Motivation section to reflect this clarification in the next day or two so the rest of the Discussion runs on the stronger framing rather than the weaker one I shipped with. Thanks again, Hendrik

On Tue, Jun 16, 2026 at 12:21 AM Hendrik Mennen <hmennen90@gmail.com> wrote:

Am 15.06.2026 um 10:43 schrieb Kamil Tekiela <tekiela246@gmail.com>:

On Mon 15 Jun 2026, 10:03 Hendrik Mennen, <hmennen90@gmail.com> wrote:

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

I tried to catch up on this discussion last night. I see a lot of things that could go wrong with this, but I don’t see a clear motivation for why would we want this change.

Reduced ceremony in pure-code files.
This is a moot point because after the change you still have the ceremony of adding .phpc to every file. Just unnecessary churn.

First-class CLI scripts.
Is this really a big concern? Is it going to work better? Are there things you cannot do today because of it?

Clearer file-mode intent.
We already have that. By convention many people add .php extension to pure PHP files, and .phtml to templates. It’s not mandated by a language and people are free to choose their naming conventions.

Strictly additive.
That’s not entirely true, but that’s beside the point. Why is this one of the main motivations? We don’t add things just because we can. They need to have a purpose.

We already have a good way of writing pure PHP code files. Just add 5 characters at the beginning of the file <?php and the rest of the file will be treated as PHP source code.

I would also like to echo what others said that PHP source code doesn’t have to be within <?php. A text file will be compiled and interpreted by PHP as a print statement. Even in pure PHP code files you may want to escape the PHP context with ?>. Mandating that a file with a certain extension will have these restrictions is worse than having to start every file with <?PHP.

Regards,
Kamil

Hi Kamil,

Thanks for the substantive pushback - these are exactly the right
questions, and you’re correct on more of them than the RFC as currently
worded admits.

Conceding upfront: “strictly additive” is a necessary property, not a
motivation. I leaned on it too much. Same for the three weaker arguments
(CLI ergonomics, convention clarity, ceremony shift) - none of them
alone justifies the mechanism.

The honest single-sentence motivation:

Most new PHP files written today are pure code (framework classes,
autoloaders, console commands, configuration). For those files the

<?php opener carries zero semantic information. PHP is the only modern mainstream language where it is mandatory. If that friction is not worth solving, that is a legitimate position and I would rather hear that than dress the proposal up to look weightier than it is. Three honest paths from here, and I would find it useful if respondents could say which they are closest to: 1. The friction is real and a small targeted addition is worth shipping. (what the RFC proposes) 2. The friction is real but the mechanism is wrong. (in which case: what signal would you prefer? per-file declare, magic first line, SAPI-only, ...) 3. The friction is not real, or not worth a language change at all. That is a sharper question than re-litigating each motivation in turn, and the answer determines whether to refine, pivot, or withdraw before voting. On your last point - that .phpc "restricts" PHP's text-as-print feature - I think we agree more than disagree. .phpc would not change .php behaviour at all. It is opt-in by filename. Authors who want the templating shape keep using .php, ?> drop-out continues to work inside

.phpc, and the engine on existing files is byte-for-byte identical.
The “restriction” you describe (no top-of-file inline content;

<?php-as-tag rejected inside .phpc) is intentional - it is what makes the mode meaningful for the cases that opt in. I will update the RFC's Motivation section to reflect this clarification in the next day or two so the rest of the Discussion runs on the stronger framing rather than the weaker one I shipped with. Thanks again, Hendrik

I don’t get a vote, but mark me as opposed. If we ever do this, it needs to accompany something akin to JavaScript’s strict mode which shards off several long standing warts that can’t otherwise be gotten rid of because of BC. If we ever do a strict mode we’d better get it right and do it only once.

As it stands, there just isn’t anything here other than lifting the need to type 5 characters at the start of every file. Worse, it could be closing a door we’ll never see again.

Hi Alex,

Thanks for the detailed reply, and for the explicit “I still
disagree” - useful signal, and in the (1/2/3) framing from the
Kamil sub-thread you are at (3), which is exactly the kind of
direct answer I asked for. Even without voting power your
arguments will be read by voters, so engaging substantively.

The genuinely new concern in your reply that the RFC does not
yet address head-on is forward-compatibility. Taking that first.

Strictly additive
Sure thing, but it’s not allowing any kind of BC, since
including “.phpc” files from older PHP versions would be
possible, and would be rendered as text instead of being
compiled…

You are correct. On PHP < target-version, a .phpc file pulled
in via require/include would be parsed by the existing lexer
in INITIAL state and emitted as inline content. This is the
standard 8.x adoption story in shape (a readonly class on 8.1
is a syntax error; a .phpc file on 8.5 is text emission), but
“text emission” is a worse failure mode than “syntax error”
because it can silently expose source code in a web context.

The realistic mitigation, which the RFC should make explicit
(and will, in the next revision):

  • A library shipping .phpc files declares the target version
    in composer.json:
    “require”: { “php”: “>=8.6” }
  • composer install fails at install time with a clear
    PHP-version error on lower versions. The .phpc file never
    reaches a running interpreter that doesn’t understand it.
  • Library authors who want to support older PHP versions
    don’t ship .phpc files. Same constraint as readonly
    classes, property hooks, enums, etc.
  • For non-Composer distribution (manual clone, raw .zip),
    the user is on their own - same as for any 8.x feature.

For application code (where the deployed PHP version is known)
the FC constraint is moot. For shared libraries it is real and
worth flagging in the RFC. I will add a Forward Compatibility
subsection covering this.

Reduced ceremony / First-class CLI scripts / Clearer file-mode
intent

These largely overlap with Kamil’s earlier points; I’d rather
not re-litigate the same arguments inline. The Motivation
section is being rewritten this week to drop the four-bullet
framing entirely in favour of one honest sentence (“modern PHP
is overwhelmingly pure-code; <?php is a vestige for those files;
.phpc is the smallest mechanism to remove it opt-in”) plus a
“what this RFC does NOT claim” subsection. If after that you
still see no value, that is a fair (3).

Closing-tag-echo issue, having “?>” somewhere…

To clarify: this RFC does NOT try to solve that. ?> behaviour
inside a .phpc file is byte-for-byte identical to .php - drop-
out to inline mode is preserved, __halt_compiler() works, <?=
has no special meaning. The “What is unchanged” subsection of
the RFC says this; I will make it more prominent.

All existing ecosystem would have to adapt: Composer, IDEs,
web servers…

Concede partially. Adoption work is non-zero. The RFC’s
position:

  • Composer: PSR-4 autoloader already accepts a per-rule
    extension list; adding .phpc is documentation + a small
    Composer-side acknowledgement. RFC commits to opening
    that PR pre-landing.
  • IDEs / static analysers: configure file modes by extension
    via their per-project config; one-line change per tool.
    RFC commits to opening tracking issues on day of vote
    acceptance.
  • Web servers: explicit handler mapping or deny. Appendix A
    of the RFC ships canonical snippets for Apache, Nginx,
    Caddy, FrankenPHP.

You are right that adoption would be slow. I do not think
that disqualifies an additive opt-in feature, but I respect
that you do.

Templating system based on PHP files […] many such projects

Agreed, and .phpc proposes to harm none of them. It is opt-in
by filename. Template engines (compiled .php cache files
prefixed with <?php) are untouched; hand-written .phtml
templates are untouched; a legacy codebase that never adopts
.phpc loses nothing.

Definition files (that I suggested in another internals
thread) […] but even having “?>” from inside a function is
no issue

I have not read your Definition files thread yet; pointer
welcome. If that concept subsumes .phpc as a special case of
something broader, I would rather refine the bigger idea than
ship a smaller one that conflicts with it. Genuinely
interested.

Thanks,
Hendrik

···

Le 15/06/2026 à 10:03, Hendrik Mennen a écrit :

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

I still disagree with this proposal, though I have no power anywhere, because IMO it doesn’t bring lots of benefits compared to the issues it creates.

Side-note: the proposals I made about potential alternatives to include such files were only based on the “What if we still do this, but in another way?” motto. The bottom thought is that I disagree with the feature, but if it had to be done somehow, these suggestions are about how I would have done this, mostly to ensure better (IMO) BC and DX, nothing fancy or professional, just my random internet person’s opinion :slight_smile:

About the motivations, here are my comments:

Reduced ceremony in pure-code files

As you say: modern PHP code is already pure-PHP, and having to prepend “<?php” everywhere doesn’t seem to be an issue for maintainers. I don’t see why removing “<?php” would add any value.

First-class CLI scripts

You can already use shebang lines on PHP scripts and PHAR files, because what’s after the shebang line is sent to the environment-based script that is requested from said shebang line. It’s basically doing “env php {script file}” , therefore is already compatible with any shebang-line-compatible shell. Having a “<?php” just means it’s PHP code. Having pure-PHP code without the opening tag wouldn’t change a thing.

Clearer file-mode intent

It’s not clearer, it’s just moving the intent to the file extension rather than the file content itself. It’s a really big DX change.
If I wanted a pure-PHP file with another extension, I could do it today by adding “<?php” in the file and it would be interpreted as PHP code without issues.
Here, the proposal is about allowing to shift a 30-plus-years-old working habit with PHP with something that is currently not intuitive neither expected. Adoption would definitely be complicated for lots of people, and if it’s opt-in and doesn’t bring lots of value, I’m not sure that many people would do it anyway.

Strictly additive

Sure thing, but it’s not allowing any kind of BC, since including “.phpc” files from older PHP versions would be possible, and would be rendered as text instead of being compiled… This has a big impact on the ecosystem.
And by the way, about the ecosystem, here’s my last comment on the motivations that definitely makes this proposal really hard to accept, at least for me: all existing ecosystem would have to adapt: Composer, IDEs, web servers, and so on. So it would be additive, indeed, but adoption would be extremely slow.

To me, one of the issues you are partially trying to fix is the potential closing-tag-echo issue, having “?>” somewhere indicating that PHP will echo something during the process. This issue can be solved with “Definition files” (that I suggested in another internals thread as a first-step towards Modules), but even having “?>” from inside a function is no issue: it just exposes a clear intent of “echo-ing something”, and isn’t really different than "echo <<<HTML " followed by a bunch of HTML for instance. IDEs may behave a bit differently with syntax highlighting, but modern IDEs are capable of injecting languages properly depending on lots of contextual metadata (and the “<<<HTML” heredoc syntax isn’t random: IDEs can know that code inside this context is HTML). Your proposal would enforce using “echo” manually, while the “?>” syntax defines the same thing.
This system would also be useless for template systems based on PHP files, which are used in many legacy PHP projects for, well, legacy reasons ; I could tell you about many such projects I’ve been working on in the past 5 years only, and I’m one single person, so extrapolate this to worldwide devs, and you get more people that wouldn’t care at all about pure-php files because it’s useless to their stack.

Am 16.06.2026 um 06:52 schrieb Michael Morris tendoaki@gmail.com:

On Tue, Jun 16, 2026 at 12:21 AM Hendrik Mennen <hmennen90@gmail.com> wrote:

Am 15.06.2026 um 10:43 schrieb Kamil Tekiela <tekiela246@gmail.com>:

On Mon 15 Jun 2026, 10:03 Hendrik Mennen, <hmennen90@gmail.com> wrote:

Hi internals,

Following the pre-RFC discussion from late May [1], I’m moving the
“pure-code source files” proposal into formal RFC stage as

https://wiki.php.net/rfc/optional_php_tags

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

  • Ben Ramsey on engine-vs-SAPI dispatch:
    own section explaining why include/require/Phar all live below
    SAPI and need engine-level handling. Ben’s -p/stdin idea is
    adopted as Future Scope (a sister RFC after this one).

  • Alex Rock on alternatives (env var, include_pure keyword,
    declare(pure=1)): each rebutted individually under “Rejected
    Alternatives”.

  • Bruce Weirdan on BC of the .phpc extension itself: own
    paragraph in the BC section, honest about the fact that I have
    NOT scanned public corpora; asking voters with private-codebase
    visibility for data points during Discussion.

  • .pyc visual collision and scattered prior .phpc usage:
    addressed via a sub-vote on the extension name (.phpc / .phpp /
    .pcode / .phpx).

  • Security (source-code exposure via misconfigured webservers,
    the historic .inc footgun): own section with mandatory
    documentation commitments and canonical Apache/Nginx/Caddy/
    FrankenPHP snippets in Appendix A.

  • Boutell 2012 and Ohgaki 2014: own section distinguishing this
    proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

  • The Open Issues block in the RFC (extension name, real-world
    .phpc-in-use data, case-sensitivity on Windows/macOS).
  • Anything in the Phar / OPcache surface I should test more
    thoroughly before voting opens.
  • Whether the Security section’s commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] https://news-web.php.net/php.internals/131024

I tried to catch up on this discussion last night. I see a lot of things that could go wrong with this, but I don’t see a clear motivation for why would we want this change.

Reduced ceremony in pure-code files.
This is a moot point because after the change you still have the ceremony of adding .phpc to every file. Just unnecessary churn.

First-class CLI scripts.
Is this really a big concern? Is it going to work better? Are there things you cannot do today because of it?

Clearer file-mode intent.
We already have that. By convention many people add .php extension to pure PHP files, and .phtml to templates. It’s not mandated by a language and people are free to choose their naming conventions.

Strictly additive.
That’s not entirely true, but that’s beside the point. Why is this one of the main motivations? We don’t add things just because we can. They need to have a purpose.

We already have a good way of writing pure PHP code files. Just add 5 characters at the beginning of the file <?php and the rest of the file will be treated as PHP source code.

I would also like to echo what others said that PHP source code doesn’t have to be within <?php. A text file will be compiled and interpreted by PHP as a print statement. Even in pure PHP code files you may want to escape the PHP context with ?>. Mandating that a file with a certain extension will have these restrictions is worse than having to start every file with <?PHP.

Regards,
Kamil

Hi Kamil,

Thanks for the substantive pushback - these are exactly the right
questions, and you’re correct on more of them than the RFC as currently
worded admits.

Conceding upfront: “strictly additive” is a necessary property, not a
motivation. I leaned on it too much. Same for the three weaker arguments
(CLI ergonomics, convention clarity, ceremony shift) - none of them
alone justifies the mechanism.

The honest single-sentence motivation:

Most new PHP files written today are pure code (framework classes,
autoloaders, console commands, configuration). For those files the

<?php opener carries zero semantic information. PHP is the only modern mainstream language where it is mandatory. If that friction is not worth solving, that is a legitimate position and I would rather hear that than dress the proposal up to look weightier than it is. Three honest paths from here, and I would find it useful if respondents could say which they are closest to: 1. The friction is real and a small targeted addition is worth shipping. (what the RFC proposes) 2. The friction is real but the mechanism is wrong. (in which case: what signal would you prefer? per-file declare, magic first line, SAPI-only, ...) 3. The friction is not real, or not worth a language change at all. That is a sharper question than re-litigating each motivation in turn, and the answer determines whether to refine, pivot, or withdraw before voting. On your last point - that .phpc "restricts" PHP's text-as-print feature - I think we agree more than disagree. .phpc would not change .php behaviour at all. It is opt-in by filename. Authors who want the templating shape keep using .php, ?> drop-out continues to work inside

.phpc, and the engine on existing files is byte-for-byte identical.
The “restriction” you describe (no top-of-file inline content;

<?php-as-tag rejected inside .phpc) is intentional - it is what makes the mode meaningful for the cases that opt in. I will update the RFC's Motivation section to reflect this clarification in the next day or two so the rest of the Discussion runs on the stronger framing rather than the weaker one I shipped with. Thanks again, Hendrik

I don’t get a vote, but mark me as opposed. If we ever do this, it needs to accompany something akin to JavaScript’s strict mode which shards off several long standing warts that can’t otherwise be gotten rid of because of BC. If we ever do a strict mode we’d better get it right and do it only once.

As it stands, there just isn’t anything here other than lifting the need to type 5 characters at the start of every file. Worse, it could be closing a door we’ll never see again.

Hi Michael,

Thanks - registered as opposed, conditional on “not bundled with
something larger”. That conditional is a useful distinction.

The “you’re closing the door on a bigger one-shot opportunity”
framing is worth answering directly. Two reasons I don’t think it
holds:

  • .phpc and a hypothetical JS-strict-mode-style language cleanup
    are best designed as orthogonal axes, not bundled. Strict mode
    naturally lives in declare() or an attribute - both work inside
    .php AND .phpc. Coupling “pure code” with “strict mode” would
    force every .phpc adopter into strict mode whether they want it
    or not, which is a much bigger ask than “opt into either
    independently”.

  • PHP already has per-file opt-ins that landed individually -
    declare(strict_types=1), declare(ticks=1), readonly classes,
    enums, property hooks. None of them consumed “the one-shot
    file-mode slot” because there isn’t one. .phpc adds one more
    opt-in along a different axis; it does not consume design space
    a future strict mode would need.

Bundling would make BOTH proposals harder, not easier. Strict mode
is contentious by design (every behaviour change has defenders) and
would extend Discussion and Voting by months or years. Removing the
opener does not have to wait on that.

If you have concrete ideas about what a PHP strict mode should
contain - which warts, which behaviours, which design - I would be
genuinely interested, separate from this RFC. That is a conversation
worth having on its own merits, in its own thread.

Regards,
Hendrik

Am 16.06.2026 um 09:26 schrieb Davey Shafik <me@daveyshafik.com>:

On Jun 15, 2026, at 21:25, Hendrik Mennen <hmennen90@gmail.com> wrote:

<snip>

If that friction is not worth solving, that is a legitimate position
and I would rather hear that than dress the proposal up to look
weightier than it is. Three honest paths from here, and I would find
it useful if respondents could say which they are closest to:

1. The friction is real and a small targeted addition is worth
   shipping. (what the RFC proposes)
2. The friction is real but the mechanism is wrong. (in which case:
   what signal would you prefer? per-file declare, magic first line,
   SAPI-only, ...)
3. The friction is not real, or not worth a language change at all.

I am strongly in camp 3; I see zero value in this.

Adding meaning to a specific extension is a BC break when no extensions have meaning prior. In addition, it makes it difficult to identify the file type from the code alone if you're not familiar with it.

There really isn't any friction here, any decent editor or IDE will give you a default file header with the open tag (at least). At worst they'll auto-complete after at most 2 characters.

It's also important to note that other languages have explicit template files like Jinja or ERB, PHP does not have this and is itself essentially both the template language and the full programming language.

Yes it's a legacy way of doing things like ASP classic or ColdFusion, but that doesn't make it wrong or bad.

- Davey

Hi Davey,

Thanks for the direct camp-3 position - the (1/2/3) framing was
intended exactly to surface that kind of clarity, and a voter
landing explicitly in 3 with substantive reasons is more useful
to me than vague reservations would have been.

I won't try to argue you out of camp 3 - your stance is
well-formed and arguing past it would just escalate. A few
responses point-by-point, primarily for other readers who are
still forming a view.

Adding meaning to a specific extension is a BC break when no
extensions have meaning prior.

Mostly conceded, with one nuance: .phar already carries
filename-aware semantics in the engine (the phar:// stream
wrapper resolves entries against the .phar filename, and the
archive opens differently from a regular file). It is not a
clean precedent for "extension drives lexer mode at file-open
time", but PHP does not have //zero// precedent for filenames
mattering to the engine.

You are right that this RFC would be the first to make filename
drive lexer state. That is a categorical shift even if no
existing code's behaviour changes. The RFC was leaning too hard
on "strictly additive" framing; I have added an explicit "this
is a categorical precedent shift" entry to the "What this RFC
does NOT claim" section in v0.4 (just published, see Changelog).

It makes it difficult to identify the file type from the code
alone if you're not familiar with it.

Conceded as a real cost. The <?php opener is a portable "this
is PHP" marker that travels with code into Stack Overflow
answers, blog posts, diff views, IDEs that hide extensions. A
naked .phpc snippet loses that signal until the reader has
learned what .phpc means. Also added as an explicit downside in
the v0.4 "does NOT claim" section.

The flip side (most snippets already drop <?php in practice
because the surrounding context is clear) softens the cost but
does not erase it. Reasonable to weigh it at "small but real".

There really isn't any friction here, any decent editor or IDE
will give you a default file header with the open tag

Agreed; the typing cost is microscopic. The RFC has tried to
say this honestly ("not a big ergonomic win" was already in
the Motivation section). What remains is the //read// cost: the
visual noise of <?php on every file you open. You can reasonably
value that at zero, especially if your IDE renders the opener
in a muted colour or folds it.

Other languages have explicit template files like Jinja or
ERB, PHP does not have this and is itself essentially both the
template language and the full programming language. [...] Yes
it's a legacy way of doing things like ASP classic or
ColdFusion, but that doesn't make it wrong or bad.

Agreed - PHP's template-and-programming-language duality is a
real and ongoing identity, defended even by people who never
write inline templates themselves. .phpc does not change that:
.php files continue to support full templating, mixed mode,
short tags, ?>, the lot. .phpc is an opt-in for files where the
author has decided "this is code, no template behaviour wanted".

On the legacy framing: those languages died for many reasons,
but "you could use them as a template engine" was not one. PHP's
longevity is partly because of that duality, not in spite of it.
I would not pitch .phpc as "removing the legacy" - it is purely
adding a way for individual files to opt out of inheriting it.

---

If you have time, one clarifying question that would help shape
the rest of the discussion:

Is your camp-3 stance disqualifying the specific mechanism
(.phpc-extension dispatch), or disqualifying the broader concept
of "let me write a PHP file without <?php" regardless of
mechanism? Camp 3 on the mechanism is actionable for future
iteration; camp 3 on the concept is information that tells me
to stop spending the Discussion's air time on this.

Either answer is useful to know.

Regards,
Hendrik

Le 16/06/2026 à 10:47, Hendrik Mennen a écrit :

Adding meaning to a specific extension is a BC break when no
extensions have meaning prior.

Mostly conceded, with one nuance: .phar already carries
filename-aware semantics in the engine (the phar:// stream
wrapper resolves entries against the .phar filename, and the
archive opens differently from a regular file). It is not a
clean precedent for "extension drives lexer mode at file-open
time", but PHP does not have //zero// precedent for filenames
mattering to the engine.

Wong: the ".phar" extension is just a reminder. A PHAR file is "just" a PHP file that contains the "__HALT_COMPILER(); ?>" statement that tells the engine and extension "where" to look to decompress the archive into code, with an internal filesystem covered under the "phar://" stream.

Any PHAR file could use any extension, and ".phar" is just indicative. It runs natively with PHP without needing an extension to be considered as an archive.

I'm also camp-3, I think this feature doesn't add anything to PHP, and breaks BC by introducing file extension as a constraint for conditional parsing methods.

Le 16/06/2026 à 07:00, Hendrik Mennen a écrit :

Strictly additive
Sure thing, but it's not allowing any kind of BC, since
including ".phpc" files from older PHP versions would be
possible, and would be rendered as text instead of being
compiled...

- A library shipping .phpc files declares the target version
in composer.json:
"require": { "php": ">=8.6" }
- composer install fails at install time with a clear
PHP-version error on lower versions. The .phpc file never
reaches a running interpreter that doesn't understand it.
- Library authors who want to support older PHP versions
don't ship .phpc files. Same constraint as readonly
classes, property hooks, enums, etc.
- For non-Composer distribution (manual clone, raw .zip),
the user is on their own - same as for any 8.x feature.

For application code (where the deployed PHP version is known)
the FC constraint is moot. For shared libraries it is real and
worth flagging in the RFC. I will add a Forward Compatibility
subsection covering this.

This list is a big reason to not implement your proposal: since it breaks BC for everyone upgrading to the newer PHP version, it's putting a huge burden to the entire ecosystem just to avoid disclosing PHP code as text instead of parsing/compiling it.
(and side-note: it also forces potential users of ".phpc" extension to change their code, though I doubt anyone is using this extension, and checking Github isn't enough for that, considering the millions of worldwide projects using PHP).
All that makes the RFC absolutely non-additive: you do not /add/ a feature to the language, you are /changing/ how it behaves internally, from not caring about file ext to making a big opinionated choice on one particular extension name.

Reduced ceremony / First-class CLI scripts / Clearer file-mode
intent

These largely overlap with Kamil's earlier points; I'd rather
not re-litigate the same arguments inline. The Motivation
section is being rewritten this week to drop the four-bullet
framing entirely in favour of one honest sentence ("modern PHP
is overwhelmingly pure-code; <?php is a vestige for those files;
.phpc is the smallest mechanism to remove it opt-in") plus a
"what this RFC does NOT claim" subsection. If after that you
still see no value, that is a fair (3).

I don't think adding a file extension constraint adds any value to PHP. Any file using "<?php" is just five characters at the beginning and all works. Implementing this RFC would imply too much change on the language and the ecosystem, just to avoid writing these five characters. That's way too overkill.

Closing-tag-echo issue, having "?>" somewhere...

To clarify: this RFC does NOT try to solve that. ?> behaviour
inside a .phpc file is byte-for-byte identical to .php - drop-
out to inline mode is preserved, __halt_compiler() works, <?=
has no special meaning. The "What is unchanged" subsection of
the RFC says this; I will make it more prominent.

Noted

All existing ecosystem would have to adapt: Composer, IDEs,
web servers...

Concede partially. Adoption work is non-zero. The RFC's
position:

- Composer: PSR-4 autoloader already accepts a per-rule
extension list; adding .phpc is documentation + a small
Composer-side acknowledgement. RFC commits to opening
that PR pre-landing.
- IDEs / static analysers: configure file modes by extension
via their per-project config; one-line change per tool.
RFC commits to opening tracking issues on day of vote
acceptance.
- Web servers: explicit handler mapping or deny. Appendix A
of the RFC ships canonical snippets for Apache, Nginx,
Caddy, FrankenPHP.

You are right that adoption would be slow. I do not think
that disqualifies an additive opt-in feature, but I respect
that you do.

It's not about respecting what I do, or making the adoption quick, it's about the relevance and impact of it.

Your RFC enforces a change in all the three elements on this list: package managers, IDEs and web servers.

I think you already know that 80% of worldwide websites and apps are using PHP.

You can *not* expect some hosting providers to advertise support for a new PHP version if that version implies that they must update their entire HTTP configuration on all their servers, VMs and so on, even if it's a "one-liner" in your mind. I've been there on a small scale, updating one machine that served dozens of domain names, and trust me, it comes with a cost, and I'm a really smaller-cases user. Now scale this to the worldwide web. It's just not rational at all.

This RFC not only enforces a "config change", it enforces lots of companies to actually PAY for forward-compatibility before even upgrading PHP.

In almost all its time, PHP's strength have been its ease to deploy and configure, and here you're making deployment and configuration more complex for the sake of removing five characters at the beginning of a file. That's just too much.

Templating system based on PHP files [...] many such projects

Agreed, and .phpc proposes to harm none of them. It is opt-in
by filename. Template engines (compiled .php cache files
prefixed with <?php) are untouched; hand-written .phtml
templates are untouched; a legacy codebase that never adopts
.phpc loses nothing.

On the template part, /maybe/ a legacy codebase loses nothing. Maybe.

But all other arguments still stand.

Definition files (that I suggested in another internals
thread) [...] but even having "?>" from inside a function is
no issue

I have not read your Definition files thread yet; pointer
welcome. If that concept subsumes .phpc as a special case of
something broader, I would rather refine the bigger idea than
ship a smaller one that conflicts with it. Genuinely
interested.

You can read the thread there: Two-step creation for PHP modules - Externals

It's on the internals mailing-list, feel free to check it out.

On 2026-06-15 20:03, Hendrik Mennen wrote:

Following the pre-RFC discussion from late May [1], I'm moving the
"pure-code source files" proposal into formal RFC stage as

PHP: rfc:optional_php_tags <PHP: rfc optional_php_tags>

The single core motivation is:

> Most new PHP files written today are pure code – framework classes, autoloaded library code, console commands, configuration files, route definitions. For these files the '<?php' opener carries no semantic information; it is a vestige of PHP's templating heritage that every file must repeat. PHP is the only modern mainstream language in which this prefix is mandatory.

To which my response is "and that is a bad thing ... how?"

On the subject of ceremonial boilerplate ... Maybe it's changed in the years since I had to work with it, but Perl modules had to end with "1;" because they were evaluated as they were imported, and the whole module had to return a truthy value to indicate that it had successfully loaded. (Of course, any truthy value would work and '1' was merely convention - TMTOWTDI.)

The mechanism is a single new opt-in file extension, .phpc, whose
semantics are: the file is parsed as pure PHP. The lexer enters
ST_IN_SCRIPTING on the first byte; no opening <?php is required.
A leading UTF-8 BOM and a CLI shebang are silently skipped. The
classic .php extension and every other code-loading path are
completely untouched - the change is strictly additive.

Reference implementation: PR against php/php-src (link in the RFC).
Patch is ~50 lines of straight-line C in
Zend/zend_language_scanner.l::open_file_for_scanning, plus 15 new
.phpt tests in Zend/tests/phpc/. Full regression run against Zend/,
ext/tokenizer/, ext/standard/, ext/spl/, ext/reflection/, ext/phar/
(9836 tests): 0 failures, 0 modifications to any pre-existing test.

Pre-RFC feedback, addressed in the RFC document:

- Ben Ramsey on engine-vs-SAPI dispatch:
own section explaining why include/require/Phar all live below
SAPI and need engine-level handling. Ben's -p/stdin idea is
adopted as Future Scope (a sister RFC after this one).

- Alex Rock on alternatives (env var, include_pure keyword,
declare(pure=1)): each rebutted individually under "Rejected
Alternatives".

- Bruce Weirdan on BC of the .phpc extension itself: own
paragraph in the BC section, honest about the fact that I have
NOT scanned public corpora; asking voters with private-codebase
visibility for data points during Discussion.

- .pyc visual collision and scattered prior .phpc usage:
addressed via a sub-vote on the extension name (.phpc / .phpp /
.pcode / .phpx).

- Security (source-code exposure via misconfigured webservers,
the historic .inc footgun): own section with mandatory
documentation commitments and canonical Apache/Nginx/Caddy/
FrankenPHP snippets in Appendix A.

- Boutell 2012 and Ohgaki 2014: own section distinguishing this
proposal mechanically from both.

Discussion period: at least two weeks from today. Looking forward
to feedback, especially on:

- The Open Issues block in the RFC (extension name, real-world
.phpc-in-use data, case-sensitivity on Windows/macOS).
- Anything in the Phar / OPcache surface I should test more
thoroughly before voting opens.
- Whether the Security section's commitments are strong enough.

Thanks for reading,
Hendrik Mennen

[1] php.internals: AW: [Pre-RFC] Pure-code source files via .phpc extension <https://news- web.php.net/php.internals/131024>

On Mon, Jun 15, 2026, at 11:20 PM, Hendrik Mennen wrote:

1. The friction is real and a small targeted addition is worth
    shipping. (what the RFC proposes)
2. The friction is real but the mechanism is wrong. (in which case:
    what signal would you prefer? per-file declare, magic first line,
    SAPI-only, ...)
3. The friction is not real, or not worth a language change at all.

I also am in camp 3. I basically never type <?php myself. My IDE does it for me when I create a file. This is a non-issue, and the proposed solution, as others have explained, has many issues and gotchas.

One useful part of Alex's module proposal was a file type that had stricter rules about what it could do; basically it had to follow PSR-1. I think there could be potential in that concept, but it wouldn't be triggered by a different file name; it would be triggered by, say, <?phpo (PHP Only), or some other prefix that indicates the whole file must be code, no ?> allowed, cannot have top-level output or code, etc. The idea needs some development, but I could see that having use in a way that doesn't break BC.

But pivoting off the file name is a non-starter.

--Larry Garfield

On 16 June 2026 09:47:07 BST, Hendrik Mennen <hmennen90@gmail.com> wrote:

Am 16.06.2026 um 09:26 schrieb Davey Shafik <me@daveyshafik.com>:

On Jun 15, 2026, at 21:25, Hendrik Mennen <hmennen90@gmail.com> wrote:

<snip>

If that friction is not worth solving, that is a legitimate position
and I would rather hear that than dress the proposal up to look
weightier than it is. Three honest paths from here, and I would find
it useful if respondents could say which they are closest to:

1. The friction is real and a small targeted addition is worth
   shipping. (what the RFC proposes)
2. The friction is real but the mechanism is wrong. (in which case:
   what signal would you prefer? per-file declare, magic first line,
   SAPI-only, ...)
3. The friction is not real, or not worth a language change at all.

I am strongly in camp 3; I see zero value in this.

Adding meaning to a specific extension is a BC break when no extensions have meaning prior. In addition, it makes it difficult to identify the file type from the code alone if you're not familiar with it.

There really isn't any friction here, any decent editor or IDE will give you a default file header with the open tag (at least). At worst they'll auto-complete after at most 2 characters.

It's also important to note that other languages have explicit template files like Jinja or ERB, PHP does not have this and is itself essentially both the template language and the full programming language.

Yes it's a legacy way of doing things like ASP classic or ColdFusion, but that doesn't make it wrong or bad.

- Davey

Hi Davey,

Thanks for the direct camp-3 position - the (1/2/3) framing was
intended exactly to surface that kind of clarity, and a voter
landing explicitly in 3 with substantive reasons is more useful
to me than vague reservations would have been.

Did you generate this reply with an LLM? It certainly reads like that.

I'm asking because I find it an extremely rude thing to do, unless it is to assist with language.

Because if people don't want to spend their own time and effort coming with an argument, then I also don't find it necessary for me to reply to them, or hear what they're saying.

cheers
Derick

Am 16.06.2026 um 20:43 schrieb Derick Rethans derick@php.net:

On 16 June 2026 09:47:07 BST, Hendrik Mennen <hmennen90@gmail.com> wrote:

Am 16.06.2026 um 09:26 schrieb Davey Shafik me@daveyshafik.com:

On Jun 15, 2026, at 21:25, Hendrik Mennen hmennen90@gmail.com wrote:

If that friction is not worth solving, that is a legitimate position
and I would rather hear that than dress the proposal up to look
weightier than it is. Three honest paths from here, and I would find
it useful if respondents could say which they are closest to:

  1. The friction is real and a small targeted addition is worth
    shipping. (what the RFC proposes)
  2. The friction is real but the mechanism is wrong. (in which case:
    what signal would you prefer? per-file declare, magic first line,
    SAPI-only, …)
  3. The friction is not real, or not worth a language change at all.

I am strongly in camp 3; I see zero value in this.

Adding meaning to a specific extension is a BC break when no extensions have meaning prior. In addition, it makes it difficult to identify the file type from the code alone if you’re not familiar with it.

There really isn’t any friction here, any decent editor or IDE will give you a default file header with the open tag (at least). At worst they’ll auto-complete after at most 2 characters.

It’s also important to note that other languages have explicit template files like Jinja or ERB, PHP does not have this and is itself essentially both the template language and the full programming language.

Yes it’s a legacy way of doing things like ASP classic or ColdFusion, but that doesn’t make it wrong or bad.

  • Davey

Hi Davey,

Thanks for the direct camp-3 position - the (1/2/3) framing was
intended exactly to surface that kind of clarity, and a voter
landing explicitly in 3 with substantive reasons is more useful
to me than vague reservations would have been.

Did you generate this reply with an LLM? It certainly reads like that.

I’m asking because I find it an extremely rude thing to do, unless it is to assist with language.

Because if people don’t want to spend their own time and effort coming with an argument, then I also don’t find it necessary for me to reply to them, or hear what they’re saying.

cheers
Derick

Hi Derick,

Yes, I use AI but for assist - not to „copy-paste“ reply.

Reason for this: I’m native German and it’s easier for me to write an answer with LLM instead of using google translator for words, which don’t come to my mind.

To clarify my position with LLM: It’s a tool and not a placement.

Hendrik

On Tue, Jun 16, 2026 at 3:34 PM Hendrik Mennen <hmennen90@gmail.com> wrote:

Am 16.06.2026 um 20:43 schrieb Derick Rethans <derick@php.net>:

On 16 June 2026 09:47:07 BST, Hendrik Mennen <hmennen90@gmail.com> wrote:

Am 16.06.2026 um 09:26 schrieb Davey Shafik <me@daveyshafik.com>:

On Jun 15, 2026, at 21:25, Hendrik Mennen <hmennen90@gmail.com> wrote:

If that friction is not worth solving, that is a legitimate position
and I would rather hear that than dress the proposal up to look
weightier than it is. Three honest paths from here, and I would find
it useful if respondents could say which they are closest to:

  1. The friction is real and a small targeted addition is worth
    shipping. (what the RFC proposes)
  2. The friction is real but the mechanism is wrong. (in which case:
    what signal would you prefer? per-file declare, magic first line,
    SAPI-only, …)
  3. The friction is not real, or not worth a language change at all.

I am strongly in camp 3; I see zero value in this.

Adding meaning to a specific extension is a BC break when no extensions have meaning prior. In addition, it makes it difficult to identify the file type from the code alone if you’re not familiar with it.

There really isn’t any friction here, any decent editor or IDE will give you a default file header with the open tag (at least). At worst they’ll auto-complete after at most 2 characters.

It’s also important to note that other languages have explicit template files like Jinja or ERB, PHP does not have this and is itself essentially both the template language and the full programming language.

Yes it’s a legacy way of doing things like ASP classic or ColdFusion, but that doesn’t make it wrong or bad.

  • Davey

Hi Davey,

Thanks for the direct camp-3 position - the (1/2/3) framing was
intended exactly to surface that kind of clarity, and a voter
landing explicitly in 3 with substantive reasons is more useful
to me than vague reservations would have been.

Did you generate this reply with an LLM? It certainly reads like that.

I’m asking because I find it an extremely rude thing to do, unless it is to assist with language.

Because if people don’t want to spend their own time and effort coming with an argument, then I also don’t find it necessary for me to reply to them, or hear what they’re saying.

cheers
Derick

Hi Derick,

Yes, I use AI but for assist - not to „copy-paste“ reply.

Reason for this: I’m native German and it’s easier for me to write an answer with LLM instead of using google translator for words, which don’t come to my mind.

To clarify my position with LLM: It’s a tool and not a placement.

Hendrik

Here’s a thought - Instead of .phpc as an extension, why not <?phpc or even <?psf (PHP source file). Explicitly tell the engine "nothing should be before this other than a shebang, no further tags will be in this file.

That also solves the inadverted echo problem and would be fully BC.

Not convinced it would be useful, but it has fewer problems.

On Tue, Jun 16, 2026, 18:22 Michael Morris <tendoaki@gmail.com> wrote:

Here’s a thought - Instead of .phpc as an extension, why not <?phpc or even <?psf (PHP source file). Explicitly tell the engine "nothing should be before this other than a shebang, no further tags will be in this file.

That also solves the inadverted echo problem and would be fully BC.

Not convinced it would be useful, but it has fewer problems.

I could actually get behind an alternative tag that reads everything as PHP. It would at least solve the problem of accidental output prior to the opening tag, though I’m not sure how big of a problem that really is. Could we expect any performance benefits from not having to parse for any closing tags? I imagine it would be minuscule if anything

On Tue, Jun 16, 2026 at 7:02 PM Sarina Corrigan <sarina.corrigan@gmail.com> wrote:

On Tue, Jun 16, 2026, 18:22 Michael Morris <tendoaki@gmail.com> wrote:

Here’s a thought - Instead of .phpc as an extension, why not <?phpc or even <?psf (PHP source file). Explicitly tell the engine "nothing should be before this other than a shebang, no further tags will be in this file.

That also solves the inadverted echo problem and would be fully BC.

Not convinced it would be useful, but it has fewer problems.

I could actually get behind an alternative tag that reads everything as PHP. It would at least solve the problem of accidental output prior to the opening tag, though I’m not sure how big of a problem that really is. Could we expect any performance benefits from not having to parse for any closing tags? I imagine it would be minuscule if anything

Thinking on this a bit more, if it is an independent tag it could hold directives on how the file should be parsed.

<?psf strict ?> Parse the file with strict mode. Here the closing tag means we're done passing directives, no further tags will be allowed in the file and the parser will remain in PHP mode for this file. <?psf new_scope ?> Create a new scope - that is no variables, symbols or autoloaders will be shared with the file that included this one. This is an alternate way to do containers aside from creating a new function.

Thoughts?

Am 17.06.2026 um 02:58 schrieb Michael Morris tendoaki@gmail.com:

On Tue, Jun 16, 2026 at 7:02 PM Sarina Corrigan <sarina.corrigan@gmail.com> wrote:

On Tue, Jun 16, 2026, 18:22 Michael Morris <tendoaki@gmail.com> wrote:

Here’s a thought - Instead of .phpc as an extension, why not <?phpc or even <?psf (PHP source file). Explicitly tell the engine "nothing should be before this other than a shebang, no further tags will be in this file.

That also solves the inadverted echo problem and would be fully BC.

Not convinced it would be useful, but it has fewer problems.

I could actually get behind an alternative tag that reads everything as PHP. It would at least solve the problem of accidental output prior to the opening tag, though I’m not sure how big of a problem that really is. Could we expect any performance benefits from not having to parse for any closing tags? I imagine it would be minuscule if anything

Thinking on this a bit more, if it is an independent tag it could hold directives on how the file should be parsed.

<?psf strict ?> Parse the file with strict mode. Here the closing tag means we're done passing directives, no further tags will be allowed in the file and the parser will remain in PHP mode for this file. <?psf new_scope ?> Create a new scope - that is no variables, symbols or autoloaders will be shared with the file that included this one. This is an alternate way to do containers aside from creating a new function.

Thoughts?

Hm my core idea was, to get PHP a bit more like C#, Python and so on - they also don’t have a start -/ end tags

But as a first goal, your idea seems logical

Hendrik