Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

My problem with PHP is that it solves the issue of code reuse the wrong way round.

In other languages, you can import code like this:

    import mail as mailer
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:

    namespace Illuminate\Mail;
Hoping the namespace (here "Illuminate") will not clash with other code.


view as:

You can `use Illuminate\Mail as Mailer` if you want

That is not what I mean.

It would not prevent a clash with another "Illuminate" namespace.

The problem is that the developer of "Illuminate\Mail" had to introduce the "Illuminate" namespace and hope nobody else uses it.

Additionally it makes the code more complex. Every file needs to carry this "namespace ...." line now.


You can hope all you want but you won't be able to submit any duplicated namespace to Packagist.

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.


Did you actually get these issues in a real use case? I've been developing PHP software for +15 years and I never had this namespace dilema.

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.


The fact that composer comes with an autoloader doesn't means that you have to autoload everything.

You can manual include, define your own extra auto loader and use interfaces to quick swap for custom implementations.

Like I said, +15 years. Composer is 9 years old. How do you think we handle this in our own Jurassic times?


    You can manual include
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.


Nope. I don't use these frameworks. But thanks for trying to assume how php is on 2022 and how all php devs work.

How do you build web applications these days?

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.

[0]: https://github.com/php-fig/fig-standards/blob/master/accepte...

[1]: https://www.php-fig.org/psr/psr-4/

[2]: https://github.com/fxn/zeitwerk#the-idea-file-paths-match-co...


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.


    You hope there are no other libraries
    called mail in your import path.
Let's take Python for example. With Python, I am in control of the import path.

I can put those two mail modules in:

    stuff/mail
    tools/mail
And then use them like this:

    from stuff import mail as mail1
    from tools import mail as mail2
I don't have to hope for anything.

Isn’t this same thing? Your dir in python structure is dictating the namespace isn’t it?

The dir structure is defined by me.

Namespaces are defined by the 3rd party developer.


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.

That example would become `use Stuff\Mail as Mail1` and `use Tools\Mail as Mail2` in PHP. What's the difference?

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.


There is 'use as'.

But it is infuriating having to namespace your code when a folder name/heirarchy could do.


Doesn't that shield you from FS/OS impl though? I often found it the only reasonable explanation (for java too)

I still haven't worked out how to phar.

Is this problematic for archive distribution?


Other languages? It's the same kind of namespacing that Java or C++ has.

None

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.

Nice.

Is the strict_types dance necessary for this?

I really hope that if PHP introduces an import system, it does not combine it with stricter type handling.


> Is the strict_types dance necessary for this?

No, just that PhpStorm adds this for me and I always have it my files. I have removed it now to avoid any distraction to what I wanted to show.


Legal | privacy