myol myol - 1 month ago 10
PHP Question

PSR-4 namespace conventions for folders on same level as src

The current convention for PSR-4 is to namespace the

src
folder as
Vendor\Package
. Any file within is then namespaced using the directory structure. so

src/Model/MyModel.php


uses

namespace Vendor\Package\Model;

class MyModel {...}


This is intuitive for any folders within the
src
folder, but what is the convention for folders on the same level as
src
? For example
tests
,
public
,
config
etc etc

(I understand some people will comment about the point of namespacing tests but image a large project with lots of individual packages each with their own tests but with common tests that can be reused between packages.)

I have seen suggestions of using
Vendor\Package\Tests
but to me, following the
src
convention, this gives the impression that there is a
Tests
folder within
src
, which is not the case. Although if there was a
Tests
folder within
src
then those namespaces would conflict?

Answer

Namespaces should group code that logically and semantically belong together. In my mind, configurations, routers, and tests all belong together as part of the application as a whole, and thus my configurations tend to have them all share namespace:

autoload: { "psr-4": { "Vendor\\Package\\": [ "public", "conf", "src" ] } }
autoload-dev: { "psr-4": { "Vendor\\Package\\": [ "tests" ] } }

Of course, by sharing namespaces, that means each artifact has to be classified (often by its "kind") to prevent class name collisions. For example, Vendor\Package\Foo might involve the following related artifacts:

  • Vendor\Package\FooTest, the unit test for foo in tests/FooTest.php
  • Vendor\Package\FooIntegrationTest, an integration test specifically covering foo in tests/FooIntegrationTest.php
  • Vendor\Package\AbstractFoo, the base for family members of foo in src/AbstractFoo.php
  • Vendor\Package\Fooable, the interface for foo kind of things in src/Fooable.php

Along these lines, you might consider separating out your different kinds of code into different directories, rather than one "catch-all" src. For large projects, this makes finding particular kinds of files easier, but is likely overkill for libraries or small applications:

autoload: { 
    "psr-4": {
        "Vendor\\Package\\": [
            "public", "conf", "lib", "view", "contract", "exception"
        ]
    }
}

As for "convention", I particularly find sharing namespaces between source code and tests to be convenient and easy, because when I want to test Foo I don't have to juggle namespaces to get at it.

// tests/Something/FooTest.php
namespace Vendor\Package\Test\Something;
use Vendor\Package\Test\BaseTestCase;
use Vendor\Package\Something\Foo; // extra work I don't want to do

class FooTest extends BaseTestCase {
    public function testX() {
        $sut = new Foo;
    }
}

One extra line in every test file, plus the cognitive load of having to find the SUT when writing a test meant more work for writing tests, which lowered my desire to write tests. So, you might say that sharing namespaces between source and tests lowers the barriers to writing tests.

In the end, questions of source code organization really fall to each project to establish a layout that promotes efficient development and reasonable builds. I'd say it can't hurt to try one, and refactor if it's not working out.

Comments