Hi Internals, sorry for the potential long email, but I can't sleep and I
want to dump this out of my brain.

Just a quick intro, let's recap PHP simple nature with this index.php file:

```
<?php

interface Foo {}

final class Bar implements Foo {}

function blah() {}

enum Options {}
```

This is legitimate and valid PHP code today. Now if I were to have a
sample.php:

```
<?php
class T implements Foo {} // Fatal error: Uncaught Error: Interface "Foo"
not found

new Bar(); // Fatal error: Uncaught Error: Class "Bar" not found

blah(); // Fatal error: Uncaught Error: Call to undefined function blah()

Options::Option1; // Fatal error: Uncaught Error: Class "Options" not found
```

Nothing new or fancy here. Simple and pure PHP. If you want to fix all
these errors you can:
1- Use include/require
2- Custom Autoloading (not for functions but bear with me)
3- Use Composer/PSR-4

Nowadays we barely use options 1 and 2 because Composer/PSR takes care of
all that for us, but autoloading seems to be a matter of "I haven't found
this symbol X, would you like to include/require a file you think has this
symbol before I give up?"

Now let's talk about Function Autoloading (
https://wiki.php.net/rfc/core-autoloading) and Callable Interfaces (
https://externals.io/message/120083) and particularly this comment
https://externals.io/message/120083#120088 which I'll take a snippet here:

Mainly the type alias question. Every time I see callable types discussed,
> it immediately sidetracks into "how do we make that less fugly to write,
> because callable types are naturally very verbose?" That leads directly to
> typedefs/type aliases, which take one of two forms:



type TwoInts = callable(int $x, int $y): int
> type LinkedResponse = ResponseInterface&LinkCollectionInterface


> which raises autoloading questions and means a dependency on a type
> defined in another package, in many cases


Here I want to raise the discussion about the little I know about PHP
symbols. If a symbol `interface Foo` is discovered and registered by PHP,
it will be usable as an interface throughout its entire execution. If the
symbol is not discovered and is used, it will trigger autoloading as a
last-chance before fatal error.

Can `type Number = int|number;` be a symbol?
Can `type TwoInts = callable(int $x, int $y): int;` be a symbol?
and lastly, can `function bar() {}` be a symbol?

Here is how I see that unfolding:

file-1.php
```
<?php
class Foo {}

class Bar {}

type Blah = Foo|Bar;
?>
```

file-2.php
```
<php

function foo(Blah $blah) {}

foo(new Foo()); // Class Foo not found
?>
```

file-3.php
```
<php
require 'file-1.php';

function foo(Blah $blah) {}

foo(new Foo()); // Works!
?>
```

How do we solve this?
- Include/Require
- Custom Autoloading
- Composer/PSR-4

Sounds familiar?

PHP already has a namespace to solve for grouping of types such that
`\Foo\Bar` and `\Bar\Bar` can already co-exist because they are two
different symbols. We already rely on classes/interfaces/enums defined on
third-party packages, why not make type aliases work the same?
And if functions could be symbols, it would work out the same as well.

One limitation I see is that symbols cannot have conflicts.
Enum/Interfaces/Classes cannot be named exactly the same under the exact
same namespace. Type Alias would follow the same limitation. Function would
be able to escape one part of that limitation if the engine could prefix
every function name with `f_` or `fn_` when registering its symbol, making
it backward compatible and allowing `class Foo` and `function Foo`
co-exist, but not two functions called `Foo` (which is already an error
anyway).

Now one questionable user experience I see is defining:
- 1 Class = 1 File
- 1 Interface = 1 File
- 1 Enum = 1 File (this is already annoying)
- 1 function = 1 file
- 1 symbol = 1 file

But this user experience does not come from PHP's nature, but instead it
comes from Composer/PSR-4 because PSR-4 maps namespaces to directories and
symbols to files. We need a static analyser to scan our entire project,
discover every symbol and create a mapping such as:
- Symbol Class_Foo -> x.php
- Symbol Interface_Bar -> x.php
- Symbol Enum_Options -> y.php
- Symbol Enum_ExtraOptions -> y.php
- Symbol fn_foo -> z.php

so that our registered autoload can include/require the necessary file when
the symbol triggers an autoload. This static analyser already exists and is
called `composer dump-autoload`. It just has not implemented a PSR-X which
allows for this pattern?

In conclusion

PHP Scripts are very simple and dummy things and it already has a
limitation of symbol discovery. We have already built tools to work with
that limitation around autoloading/psr/composer. We could extend the PHP
Symbol system to include functions and type alises. If this can be done in
a backward-compatible manner, we can already integrate those into PSR-4
from day one. Lastly, we lift the social-construct limitation of 1 file = 1
symbol with PSR and Composer since PHP never really had this limitation
built-in. We come out of it with 3 major wins (from my pov):

- Function autoloading
- Type Aliasing
- Never creating 3 files for 3 Enums again

If you managed to read up to here, I apologize for late confessing I know
nearly nothing about PHP internals codebase. Is this obviously wrong and am
I just hallucinating a new awesome PHP version here?

-- 
Marco Deleu

Reply via email to