Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,7 @@
<url>
<loc>https://scherzer.dev/Blog/20260302-confoo</loc>
</url>
<url>
<loc>https://scherzer.dev/Blog/20260309-php-friends</loc>
</url>
</urlset>
141 changes: 141 additions & 0 deletions src/Blog/posts/20260309-php-friends.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
extensions:
footnotes: true
pygments: true
title: Friends in PHP
---

# Friends in PHP

As I mentioned in [my last blog post][blog-confoo], while at ConFoo I and a few
other developers worked on some ideas for adding friendship support to PHP. To
be clear, these are just ideas at the moment, but I figured they were worth
sharing.

## Inspiration

In C++, the `friend` declaration allows other classes or functions to access
the internal methods and properties of class. For example, consider the
following `User` and `UserFactory` classes:

```cpp
using namespace std;

class User {
friend class UserFactory;

public:
string getName() const {
return this->name;
}

private:
string name;
User (string name) : name(name) {};
};


class UserFactory {
public:
static User newUser(string name) {
return User(name);
}
};
```

Because the `User` constructor is marked as private, the class can only be
instantiated from methods within `User` (i.e. if a static method was added).
To allow a dedicated `UserFactory` to be able to construct `User`s, the
`UserFactory` is marked as a `friend` of the `User` class; otherwise, the
constructor would need to be made public in order to be called from the factory.

## PHP workarounds

PHP does not have such a system of friendship. Visibility restrictions can be
overcome by using the Reflection extension, but using Reflection to circumvent
visibility restrictions in production code is frowned upon. Instead, visibility
is simply relaxed, with methods and/or properties being marked as `public`,
and documentation is used to indicate that, though public, the relevant methods
and properties are only intended to be publicly used by specific classes (like
a factory) and are not part of the stable interface.

[Dave Liddament][Dave] and I met for the first time back at Longhorn PHP in
October, and had discussed his [`php-language-extensions`][gh-lang-exts]
library, which adds a few attributes (like `#[Friend]`[^1]) that are
processed by static analysis. Instead of being enforced at a language level, his
version of the friendship attribute is applied to *public* class members, and
the use of those class members is validated by
[a PHPStan extension][gh-phpstan-lang-exts], which verifies that public members
tagged with `#[Friend]` are only accessed from outside of the class by code that
is marked as a friend. At Longhorn, we had a brief discussion about potentially
adding support for that attribute on a language level - we fleshed out a
potential design while at ConFoo.

## Attribute approach

I am a big fan of PHP's attribute system, and wrote a few RFCs to expand
attributes in PHP 8.5. I have also already written the code for a few future
RFC ideas related to attributes. Thus, my first thought was to add friendship
as a built-in attribute, similar to how Dave's library does it.

Of course, there would by necessity be some big differences between how PHP
implements the attribute and how it was done in userland. In userland, the
underlying methods and properties were made public so that they could be
accessed, and the `#[Friend]` attribute added some restrictions via static
analysis. If the methods or properties were not public, they would be
inaccessible!

On the other hand, if the attribute is built in, it can change the visibility
semantics and allow using protected or private class members. It does not make
much sense to use friendship for public members, since those can already be
accessed without restriction. Thus, a built-in attribute would likely be
restricted to use on protected or private class members.

## Attributes as optional

After we had mostly fleshed out a design, however, [Derick Rethans][Derick] walked
by, and we discussed our ideas. Derick made a great point - currently (as of
PHP 8.5), any code with attributes applied will run the same with those
attributes removed. Sure, some new warnings might be shown,[^2] and some old
warnings might no longer be triggered,[^3] but the actual functionality would be
essentially unchanged. With the proposed `#[\Friend]` attribute, however,
removing the attribute would lead to runtime errors when attempting to access
protected or private class members.

Derick's observation led me and Dave to also write up a basic design for
applying friendship at a class level without using attributes, and that would
work. However, trying to do the same for class methods, properties, and
constants did not result in a syntax that either of us liked. I would be fine
with only supporting class-level friendship (like C++), but Dave was pushing for
more fine-grained control.

## Next steps

I'm planning to discuss both options (attribute and class member) in an email to
the internals mailing list and gauge community response before working on an
RFC and implementation. Ideally, this would all be accepted, implemented, and
merged in time for PHP 8.6, but no promises.

[^1]: Dave's attribute is in the `\DaveLiddament\PhpLanguageExtensions`
namespace, and is written here as `#[Friend]` on the assumption that the
relevant `use` statement is present. If added directly to the PHP language, the
attribute would presumably be in the global namespace, indicated by the leading
backslash in `#[\Friend]`.

[^2]: e.g. the warnings about dynamic properties previously suppressed with
[`#[\AllowDynamicProperties]`][attrib-adp], or warnings about return type
compatibility that were suppressed by [`#[\ReturnTypeWillChange]`][attrib-rtwc].

[^3]: e.g. those previously caused by using functions, constants, or other
items marked as [`#[\Deprecated]`][attrib-dep], or the warnings from not using
the result of a function marked with [`#[\NoDiscard]`][attrib-nd].

[Dave]: https://www.daveliddament.co.uk/
[Derick]: https://derickrethans.nl/
[attrib-adp]: https://www.php.net/manual/en/class.allowdynamicproperties.php
[attrib-dep]: https://www.php.net/manual/en/class.deprecated.php
[attrib-nd]: https://www.php.net/manual/en/class.nodiscard.php
[attrib-rtwc]: https://www.php.net/manual/en/class.returntypewillchange.php
[blog-confoo]: ./20260302-confoo
[gh-lang-exts]: https://github.com/DaveLiddament/php-language-extensions
[gh-phpstan-lang-exts]: https://github.com/DaveLiddament/phpstan-php-language-extensions
7 changes: 4 additions & 3 deletions tests/data/Home.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Computer Science and Political Science and graduating <em>magna cum laude</em> in 2024 with a Bachelor of Science degree. As part of my work in Computer
Science, I chose to write an honors thesis in my senior year, see <a href="./Thesis">here</a> for details. I continued at Tufts for graduate school, graduating in 2025 with
a Master of Science in Computer Science degree.</p><p>See the links in the navigation bar above for more information about my
experience.</p><h2 class="subsection-header">Contact</h2><ul><li><a href="https://www.linkedin.com/in/daniel-scherzer-520539263/" target="_blank" class="external-link">LinkedIn</a></li><li>Email: <code>daniel.e.scherzer@gmail.com</code></li></ul><h2 class="subsection-header">Blog</h2><p>I also have a blog. You can see a full index of my posts <a href="/Blog">here</a>. My latest blog post is: </p><div class="blog-preview"><h3>ConFoo 2026</h3><span class="blog-preview-date">Monday, 02 March 2026</span><p>Last week, I was in Montreal for the ConFoo conference, where I gave two talks,
one about Rust and one about PHP. I also attended plenty of fascinating talks
given by others, and absorbed some new ideas along the way. <a href="/Blog/20260302-confoo">Continue reading...</a></p></div></div><div class="des-footer"><div class="des-footer--content">Content is © 2026 Daniel Scherzer</div></div></body></html>
experience.</p><h2 class="subsection-header">Contact</h2><ul><li><a href="https://www.linkedin.com/in/daniel-scherzer-520539263/" target="_blank" class="external-link">LinkedIn</a></li><li>Email: <code>daniel.e.scherzer@gmail.com</code></li></ul><h2 class="subsection-header">Blog</h2><p>I also have a blog. You can see a full index of my posts <a href="/Blog">here</a>. My latest blog post is: </p><div class="blog-preview"><h3>Friends in PHP</h3><span class="blog-preview-date">Monday, 09 March 2026</span><p>As I mentioned in <a href="./20260302-confoo">my last blog post</a>, while at ConFoo I and a few
other developers worked on some ideas for adding friendship support to PHP. To
be clear, these are just ideas at the moment, but I figured they were worth
sharing. <a href="/Blog/20260309-php-friends">Continue reading...</a></p></div></div><div class="des-footer"><div class="des-footer--content">Content is © 2026 Daniel Scherzer</div></div></body></html>
5 changes: 4 additions & 1 deletion tests/data/blog-index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<!DOCTYPE html>
<html lang="en"><head><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"><link rel="stylesheet" type="text/css" href="/resources/default-styles.css"><title>Blog index</title><link rel="stylesheet" type="text/css" href="/resources/blog-styles.css"></head><body><div class="des-navbar"><a href="/Home">Home</a><a href="/files/Resume.pdf">Résumé</a><a href="/OpenSource">Open Source</a><a href="/Work">Work</a><strong class="des-strong-page-link">Blog</strong></div><div class="content-wrapper"><h1>Blog index</h1><div class="blog-preview"><h2>ConFoo 2026</h2><span class="blog-preview-date">Monday, 02 March 2026</span><p>Last week, I was in Montreal for the ConFoo conference, where I gave two talks,
<html lang="en"><head><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"><link rel="stylesheet" type="text/css" href="/resources/default-styles.css"><title>Blog index</title><link rel="stylesheet" type="text/css" href="/resources/blog-styles.css"></head><body><div class="des-navbar"><a href="/Home">Home</a><a href="/files/Resume.pdf">Résumé</a><a href="/OpenSource">Open Source</a><a href="/Work">Work</a><strong class="des-strong-page-link">Blog</strong></div><div class="content-wrapper"><h1>Blog index</h1><div class="blog-preview"><h2>Friends in PHP</h2><span class="blog-preview-date">Monday, 09 March 2026</span><p>As I mentioned in <a href="./20260302-confoo">my last blog post</a>, while at ConFoo I and a few
other developers worked on some ideas for adding friendship support to PHP. To
be clear, these are just ideas at the moment, but I figured they were worth
sharing. <a href="/Blog/20260309-php-friends">Continue reading...</a></p></div><div class="blog-preview"><h2>ConFoo 2026</h2><span class="blog-preview-date">Monday, 02 March 2026</span><p>Last week, I was in Montreal for the ConFoo conference, where I gave two talks,
one about Rust and one about PHP. I also attended plenty of fascinating talks
given by others, and absorbed some new ideas along the way. <a href="/Blog/20260302-confoo">Continue reading...</a></p></div><div class="blog-preview"><h2>Notes for PHP Release Managers</h2><span class="blog-preview-date">Wednesday, 18 February 2026</span><p>Each release of PHP has a team of release managers responsible for managing
the release process. Next month, the other PHP 8.5 release managers and I will
Expand Down