> On Jun 28, 2024, at 3:07 AM, Michael Morris <tendo...@gmail.com> wrote:
> On Thu, Jun 27, 2024 at 8:16 PM Mike Schinkel <m...@newclarity.net 
> <mailto:m...@newclarity.net>> wrote:
> node_modules IMO is one of the worse things about the JavaScript ecosystem. 
> Who has not seen the meme about node_modules being worse than a black hole?
> 
> Fair enough. Or maybe import maps would be a better way forward.

Import maps are really a small part of what PHP actually needs. For example, is 
it a class, an interface, or a function? For a module, it is a property?  

I envision basically that this file, whatever it would be called would be a 
pre-compilation of everything that PHP can pre-compile about the files that are 
contained within the module/directory.

See below where I talk about a pre-compiled `.php.module`

>  But ensuring that it is possible to disallow loading needs to be 
> contemplated in the design. PHP has to be able to know what is a module and 
> what isn't without expensive processes.
> 
> One possible solution is that if modules do not have <?php ?> tags, ever, and 
> someone directly tries to load a module through http(s) the file won't 
> execute. Only files with <?php ?> tags are executable by the web sapi.

Except that would require parsing all of the entire files in the directory to 
know (unless everything were pre-compiled as I am advocating.). 

Still, I think it would be better to be explicit, and for that I would propose 
the first line in the file needs to start with "module" and have the name of 
the module.

> I've only touched the surface on how GoLang does things. Some of it was 
> confusing to me at first. It's also been awhile so I'd need to refresh my 
> memory to speak to it.

In Go modules or, in this context more correctly named "packages" are:

- A collection of files grouped into a directory and thus all files in that 
directory are in the same package.

- Public or private scope are determined by case of symbols; lowercase are 
private and uppercase are public. People coming from other languages tend to 
hate this, but I have come to love it because it makes code less dense while 
employing the same information as a "public" and "private" keywords. It also 
makes code across different developers more consistent.

- Packages can be nested in package directories, but...

- There is no concept of a "sub" package, meaning there are no hierarchies when 
packages are used in code (there is a file path hierarchy but that is only 
relevant for importing the package.) When I started working with Go I thought 
that was unfortunate. Now after 5+ years working with Go I see it as a really 
good decision.

- Package files must have a "package" statement at the top, and all files in 
the directory must have the same "package" statement, with one caveat.

- That caveat is that package files can have `package <packagename>_test` as a 
package name and that file is assumed to contains a test but it cannot see 
private members in `package <packagename>`.  

- Test files are typically named to pair with a `<filename>.go` and would be 
named `<filename>_test.go`.  That file's package name can either be just 
`<packagename>` or `<packagename>_test`, depending on if you want to reach into 
private members or not. 

- You can also find `test` packages that contain all `<filename>_test.go` files.

- Testing is build into Go with `go test ./...` to run all tests in current and 
all subdirectories. (Idiomatic testing in Go is so much easier that idiomatic 
testing in PHP resulting in a culture of testing among almost all Go 
developers.)

- Package files can have `type`s, `var`s, `const`s, and `func`s as well as 
imports and directives, of course. 

- Types in Go can be `struct` (which is the closest Go has to a class), slice 
of type e.g. `[]<type>`, array of type e.g. `[<n>]<type>`, `map[<key>]<value>`, 
and a few more that I won't go into as I think they are out of scope for this 
explanation.

- Packages can have one or more `init()` functions that are all called before 
the program's `main()` func is called. There can also be multiple `init()` 
functions even in the same file.

- `var`s can be initialized and those initializations are run before the 
program's `main()` func is called.

- `const`s are initialized before the program's `main()` func is called but can 
only be initialized by literal scalar types. Unfortunately.

- `import`s take the form of `import "<package>"` for standard library types 
and where a `<package>` can contain parent paths.

- For local types `import`s take the form of `import "<module>/<package>"` 
where a `<module>` is defining by having a `go.mod` file in the directory or a 
parent directory, and a `<package>` can contain parent paths.  A `go.mod` file 
has a `module` directive, a `go` version, and one or more `require` statements 
(I'm ignoring a bit of minutia here.)

- Modules allow grouping of packages together and were added in recent years to 
provide versioning for the collective dependencies of a module. The version 
information is stored in `go.sum` and is managed automatically with Go CLI 
commands.

- For external third party modules `import`s take the form of `import 
"<domain>/<package>"` where `<package>` can contain parent paths and almost 
always does. An example is "github.com/stretchr/testify/assert 
<http://github.com/stretchr/testify/assert>"  

- External modules are by definition HTTP(S) GETable, and Go developers use `go 
get <module>` on the command line to download the module. Go does not have or 
need a 3rd party package manager as that can become a single point of failure 
and is definitely a single point of control.  To download `testify` for use in 
their Go module a Go dev would run `go get github.com/stretchr/testify 
<http://github.com/stretchr/testify/assert>`

- Most external third party modules for Go are hosted on Github but can be 
hosted on a custom domain, Bitbucket, GitLab, etc.

- The Go team manages a standard proxy for `go get` but organizations can run 
their own if desired.

- `import`s are referenced by name internally where the package name is the 
last segment of the import after a `/`, or just the name if no slash.  So 
"github.com/stretchr/testify/assert 
<http://github.com/stretchr/testify/assert>" is referenced in code as `assert`. 
 For example, `assert.Equal(t,1,value)` would assert that `value!=1` then it 
would use the testing variable `t` to mark this assertion as an error and 
generate appropriate output.

- `import`s can be aliases so you could `import check 
"github.com/stretchr/testify/assert 
<http://github.com/stretchr/testify/assert>"` and then call 
`check.Equal(t,1,value) instead of `assert.Equal(t,1,value)` but needing to 
alias a package frequently is a code smell for a badly named package.

- You can use `.` as an alias and then not need to use the alias, so we could 
`import ` "github.com/stretchr/testify/assert 
<http://github.com/stretchr/testify/assert>"` and then just call 
`Equal(t,1,value) instead of `assert.Equal(t,1,value)` but this is frowned on 
in the Go community except for in very specific use-cares.

- You can use `_` to bring in a package even if you are not referencing it in 
case it has an `init()` function that you need to run.  If that applied to 
testify it would look like this: `import _ ` 
"github.com/stretchr/testify/assert 
<http://github.com/stretchr/testify/assert>".`

- All of `import`, `var`, and `const` support a multiline for using parenthesis 
like so:

        var (
                x = 1
                y = 2
        )

- Module names are idiomatically one word w/o underscores and lowercase.

- There is no need to import specific symbols from a Go package like there is 
in JavaScript. I have programmed in both Go and JS, and I have not found a real 
benefit to having to reference everything explicitly in the import — since you 
have to mention the package name everywhere you use any package symbol — but I 
have noticed a benefit to not having nearly as much boilerplate at the top of 
the file for import when working with Go vs. working with Javascript. And my 
GoLang IDE just manages imports for me whereas WebStorm just calls out when I 
haven't imported function names in Javascript.

I am sure there is more I missed, but that should cover the highlights.

The takeaways that I think would be useful are PHP modules are:

1. Imports
2. Import aliases 
3. Module-level consts
4. Module-level init() functions
5. Module-level vars with initialization
6. Module-level functions
7. One directory == one module
8. No hierarchy for modules
9. Single word module names in lowercase.
10. Module sytax being <module><operator><symbol>, e.g. mymodule->MySymbol

Takeaways I wish the PHP community would consider but doubt there is any chance:

1. Having modules be HTTP(S) GETtable with `php get <module>`
2. Uppercase being public, lowercase being private, and no need for protected
3. Test packages with testing build into PHP e.g. `php test ./...`

> >> I'm not fond of this either.
> >
> > There will need to be a way to define the entrypoint php.  I think 
> > index.php is reasonable, and if another entry point is desired it can be 
> > called out -> "mypackage/myentry.php"
> 
> Why is an entry point needed?  If there is a module metadata file as I am 
> proposing PHP can get all the information it needs from that file. Maybe that 
> is the `.phm` file?
> 
> Maybe. Again, I need to look over this meta data format. Also, how does it 
> get created?

As I am envisioning, PHP at the command line would have the ability to 
pre-compile a module — aka all files in the module directory — and then write a 
module-specific file, maybe `.php.module`? That could ideally be optimized for 
loading by PHP and have everything it needs to know to run the code in that 
module.

That file could be completely self-contained include all source code similar to 
a `.phar` file, or it could just have a complete symbol table and still require 
the PHP source code to exist as well. I have not pondered all the pros and cons 
of these alternatives yet.

Clearly though even if it compiled to a self-contained file the .PHP files 
would still be needed during development.  Thus I envision that the PHP CLI 
would need a `--watch` option to watch directories and recompile the 
`.php.module` file upon PHP file change.  IDEs like PhpStorm could run `php 
--watch` for users and non-IDE users could run it themselves.

When PHP would come across an `import` statement pointing to a module directory 
it would first look for the compiled `.php.module` file and if found use it but 
if not found it would recreate it. Maybe it could write to disk, or generate an 
error if it cannot write to disk. OTOH writing to disk might be a security 
issue in which case it could issue a warning that the `.php.module` file does 
not exists and then compile the module to memory and continue on.

It would be nice if there was a mode where PHP would check the timestamps of 
all PHP files in the module directory and if the compiled `.php.module` was 
earlier than any of the .php file then recompile but you'd want that off for 
production. That could be a new function `set_dev_mode(boolean)` or a CLI 
option to create a `.phpdev.module` instead of a `.php.module`.

Clearly anyone using deployments could have their build generate all the 
required `.php.module` files for deployment, and hosting companies that host 
apps that don't use deployments like WordPress could have processes that build 
the `.php.module` files for their users.

I think I have thought through this enough to identify there are no technical 
blockers, but I could certainly have missed something so please call it out if 
anyone can identify something that would keep this from working and/or 
significantly change the nature of PHP development.

BTW, this pre-compiling would ONLY apply to modules, so people not using 
modules would not have to be concerned about any of this at all.

-Mike

P.S. 
> I remember when the choice to use \ was made.  I've rarely been so angry 
> about a language design choice before or since.  I've gotten used to it, but 
> seeing \\ all over the place in strings is still.. yuck.

Ditto.

Reply via email to