That is another consequence I don't like about the quirky namespace approach. It collects more and more problems like a ball of dust that gets bigger and bigger.
The consequence you mention is that one depends more and more on tooling and services.
But you suffer the indirections caused by the fat stack that is needed to deal with the missing import system.
In Python, "import" actually imports the code.
In PHP, the "use" statement you see everywhere does not. It only works because the code has already "magically" been imported.
Usually by composer. Thats why there are tens of thousands of hits for "clear composer cache" on Google. To just point out one issue with the dependecy on a fat stack to deal with a missing import system.
I don't think that will work with any code that came out in recent years. Because it all expects that its dependencies are automagically included via composer.
In practice, every PHP based web application starts with Laravel or Symfony these days. So you are thrown into the composer workflow right away. And it would be a nightmare to fight it.
Web applications come in all shapes and sizes. If you have an application that just mediates data from database and exposes API to frontend then for couple of endpoints you don't even need router or any other packages, PHP has lots of stuff baked in. You just check if the request is GET/POST/PUT/DELETE/PATCH, work with data, output a header 'Content-Type: application/json', send the data and be done with it. Or output a html template with data or whatever. Or you can not use templates and mash it all together in PHP which already is a templating language. You can do it as simple or complicated as you need.
And then if you start needing additional functionality you can start adding packages with composer one by one. Of course it'd be stupid to build your own version of Symfony with it, but the beauty is that you can stop at any point you want if it covers your needs. This sort of thing would be much more hassle with say Java, without using any frameworks.
The autoloader in composer offers several alternatives, and while all of them are namespaced per convention - nothing stops a project from deciding to write their own - something super easy in PHP but impossibly hard in other languages.
I find PHP packaging and dependency management to be painless compared to both Python/Ruby.
Java solved this by using domain names as unique identifiers. I can use com.banffy and be sure only a complete a*hole would make a package in this space.
This is roughly PHP’s approach. In this case, Illuminate is the vendor prefix and Mail is the package name. This often but not always corresponds to the rules around the composer package name, which would be illuminate/mail in this case.
This is similar to NPM’s organisation prefix (@foo/bar).
But yeah, I’m not defending the hacky way namespaces are resolved through autoloading. I do prefer actual language support for a module system (but Ruby suffers from this problem too).
I actually prefer auto-loading, which lets you iterate through a package manager much easier/faster - PHP iterated through PSR-0[0] before landing on PSR-4[1], and you can always build your own (which is what most frameworks pre-composer were doing).
With Rails 7 and Zeitwerk, the Ruby community has landed on a very similar auto-loading system as PHP now[2] with constants translating to paths by convention.
Yeah, I prefer an actual module system over PHP namespaces plus autoloader. It feels like a hacked on module system tbf. BUT I do get lots of real work done using namespaces and a PSR autoloader so it works.
I do wish they would extend auto loading for standalone functions and constants though.
The way I see it is that you alias the import mail as mailer. You hope there are no other libraries called mail in your import path. PHP fixes that by requiring all libraries called mail to be dependent on their own namespace so that there will be no collisions.
I think that neither do make assumptions on how the code is called when used, only one is preventing name collisions.
That is not exactly true, you can tweak composer to put dependencies anywhere you want them. What I feel is that there is a minor inconvenience, as many other languages have, and you try to build a case to bring down a whole ecosystem.
The difference is that in PHP, the developers of "Stuff\Mail" and "Tools\Mail" would have to define "Stuff" and "Tools" as the namespace for their code.
In Python, they don't have to do anything. The project that uses the code decides.
Namespaces in PHP are totally fake, they are not real as it is in C++, where you can actually store data within them.
In PHP the namespace is just a name added to every class construct and nothing else.
This is basically
class MyThing
the same as this
namespace My;
class Thing
However namespaces was a pragmatic design choice that solved the community's problem with code organization and ever increasing project sizes and for that it worked pretty well because introducing namespaces didn't break any existing workflow.
Today though I agree that proper module system would be nicer and now with PHP evolved even further it is perhaps time to introduce them.
Without having deep knowledge about PHP internals it feels like it would be possible to do because you can already today implement your own module system. Here is just something quick & dirty as a proof of concept.
module.php
<?php
return new class {
public const MY_CONST = 47;
private const MY_PRIVATE_CONST = 127;
// with PHP 8.1 you also have readonly properties
public string $myPublic = 'foo';
private int $myPrivate = 17;
public function helloWorld(string $name): void
{
echo "Hello, World {$name}!";
}
};
main.php
<?php
final class ModuleException extends Exception {
}
function module(string $name): object
{
static $modules = [];
if (isset($modules[$name])) {
return $modules[$name];
}
try {
$module = @require $name;
} catch (Error $error) {
throw new ModuleException("No such module {$name}", 0, $error);
}
if (!is_object($module)) {
throw new ModuleException("Module {$name} is not an object");
}
$modules[$name] = $module;
return $module;
}
$my = module('module.php');
$my->helloWorld('Module system');
$other = module('module.php');
assert($my === $other);
The building block in PHP internals is the class - interfaces, traits and enums are all classes in disguise. Could it be possible to use classes for modules as well? Perhaps just add some syntactic sugar on top.
In other languages, you can import code like this:
The external code ("mail") does not have to make assumptions how it will be called when it is used.In PHP, it is the other way round. Every piece of code needs to try avoiding namespace conflicts by prefixing the code with something like this:
Hoping the namespace (here "Illuminate") will not clash with other code.reply