Jump to content

Directory structure


NotionCommotion

Recommended Posts

I would like to continue https://forums.phpfreaks.com/topic/305804-critique-script/, but specifically how the directory structure should be arranged, and with an empathis on namespace. 

 

The /var/www/ has the following files and directories, and represents my EmbeddedWebserver project.



bin/
config/
docs/
public/
   index.php
   resources/
      css/
      js/
   vendors/
      jquery/
resources/
scr/
   MyPhpFiles.php
templates/
   some_template.html
tests/
vendors/
   michael/
      simple-router/
         src/
            SimpleRouterFiles.php
      my-larger-package/
         SomeSubPart1/
            SomeClasses.php
         SomeSubPart2/
            SomeOtherClasses.php
         Exceptions/
            SomeExceptions.php
   some_other_vendor/
   Pimple.php


 

Say, I was the author of simple-router and I use it on multiple projects and maybe even host it publicly and have a composer.json file to configure it.  It seems to be it should be located in vendors/ along with any other 3rd party packages.  Am I mistaken?  Assuming I am not, my classes for simple-router will use the Michael\SimpleRouter\ name space or maybe even some sub-namespace under it.

 

Pimple which doesn't have any namespace declared will be located directly in vendors/.  Side note, but will it be possible to configure a composer.json file to load it here?

 

My files in src/ will use the Michael\EmbeddedWebserver\ namespace or maybe some sub-namespace.

 

In regards to organizing directories in src/ (or vendors/michael/simple-router/src/ for that mater), I assume there really can't be much of a standard as each project is different.

 

Does it ever make sense to have a common Exceptions directory for multiple sub-packages as I have shown?

 

Should a cache/ or tmp/ directory ever be located in the root?

 

Am I somewhat on the right track?

 

Thanks

 

Link to comment
Share on other sites

Say, I was the author of simple-router and I use it on multiple projects and maybe even host it publicly and have a composer.json file to configure it.  It seems to be it should be located in vendors/ along with any other 3rd party packages.  Am I mistaken?

You're right. But it's typically "vendor". Singular.

 

Assuming I am not, my classes for simple-router will use the Michael\SimpleRouter\ name space or maybe even some sub-namespace under it.

Correct. "Michael" isn't really that great for a namespace, though - lots of Michaels in the world.

 

Pimple which doesn't have any namespace declared will be located directly in vendors/.

Right.

 

Side note, but will it be possible to configure a composer.json file to load it here?

I suspect Pimple v1 isn't set up for Composer. You already need a place in the framework that loads the Composer autoload, so I'd require_once the Pimple code there too.

 

My files in src/ will use the Michael\EmbeddedWebserver\ namespace or maybe some sub-namespace.

Fine.

 

In regards to organizing directories in src/ (or vendors/michael/simple-router/src/ for that mater), I assume there really can't be much of a standard as each project is different.

There is a standard of sorts: classes named A\B\C are located in /A/B/C.php. Case sensitive. Beyond that the names of the classes aren't standardized, yes, but certain concepts have commonly-used names that would be helpful to reuse.

 

Does it ever make sense to have a common Exceptions directory for multiple sub-packages as I have shown?

If you want to put exceptions in a \Exception\ namespace then yes. I would only if you had more than one or two exception classes in a given namespace.

 

Should a cache/ or tmp/ directory ever be located in the root?

As long as it isn't web accessible. Remember that you can use the system's temporary directory if you need a temporary file.
Link to comment
Share on other sites

Thanks requnix,  I think I actually get this namespace thing (other than using plural when it should be singular that is.  Good catch).

 

So, instead of Michael as my vendor name, we will go with NotionCommotion or something.  I have the autoloader for my src/ directory figured out, and can create new NotionCommotion/EmbeddedWebserver();  But then there is new NotionCommotion/SimpleRouter(); which needs to access the vendor directory.

spl_autoload_register(function ($class) {
    $prefix = 'NotionCommotion\\';
    $base_dir = __DIR__ . '/../src/';
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }
    $relative_class = substr($class, $len);
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});

 

But 

Link to comment
Share on other sites

Side note, but will it be possible to configure a composer.json file to load it here?

Yes, but not using PSR-4. Composer supports multiple styles of autoloading, even simple require_once statements that are run on start up.

 

 

My files in src/ will use the Michael\EmbeddedWebserver\ namespace or maybe some sub-namespace.

As mentioned, you could use something more unique than Michael\. It's not a big deal though so I wouldn't fret about it too much.

 

I tend to use Kicken\ for my code for two reasons.

1) It's more unique than just Keith\

2) That is my github url.

 

Since github is the standard place to find code these days and lots of tools interact with it, using your github url as the namespace is common.

 

If you decide to release code as a library for use with composer then your package name is typically $GithubName/$repositoryName. To keep things consistent then, you may as well namespace it similar to $GithubName\$repositoryName\

 

 

Should a cache/ or tmp/ directory ever be located in the root?

I follow similar to what symfony does, and put that kind of stuff under $ProjectRoot/var. That way it's easy to wipe out the applications runtime state data with a simple rm -rf $ProjectRoot/var.

 

More permanent files that I wouldn't want to accidently wipe out, such as uploaded files, I generally put into some subdirectory of $ProjectRoot/storage.

Link to comment
Share on other sites

Thanks kicken,  Of course an upload (aka storage) folder will be often needed, but interestingly I never saw one in the various want to be standards write ups I read (and which I too failed to list).  If sub-directories can exist in it, I assume you create that directory upon write if it doesn't exist. What do you typically put in $projectRoot/var?  

Link to comment
Share on other sites

If sub-directories can exist in it, I assume you create that directory upon write if it doesn't exist.

If it makes sense to sub-divide the storage area into different zones, then I'll typically create such zone directories manually, eg:

storage/
  emailAttachments/
  userDocuments/
  .../
Any further division are generated automatically. For example, userDocuments might have a folder for each user id.

 

 

What do you typically put in $projectRoot/var?

Anything that the application uses to operate, but could safely be deleted and re-generated. For example: cache files, log files, temporary files, session data files, etc.

Link to comment
Share on other sites

I am currently not using composer to package the applications I create, but wish to structure them so in preparation and thus wrote my own autoloader.  

 

Any flaws in my vendor autoloader?  What I don't like about it is when I compare it to other examples in vendor\.  For instance, new() React\EventLoop\Factory() would require $ProjectRoot/vendor/react/event-loop/src/Factory.php, and if I followed suit, new() NotionCommotion\SimpleRouter\Router() would require $ProjectRoot/vendor/notion-commotion/simple-router/src/Router.php but actually requires $ProjectRoot/vendor/NotionCommotion/SimpleRouter/src/Router.php.  Not a deal breaker, but definitely not the same as what appears to be typical.  Maybe I need to take this a step further and include the typical path structure in a JSON file?

 

Also, is it typical to position the autoloader which occurs most often first to reduce amount of code which needs to be executed?

spl_autoload_register(function ($class) {
    $parts=explode('\\',$class);
    array_splice( $parts, count($parts)-1, 0, ['src'] );
    $file = __DIR__ . '/../vendor/'.implode('/',$parts).'.php';
    if (file_exists($file)) {
        require $file;
    }
});
spl_autoload_register(function ($class) {
    $prefix = 'NotionCommotion\\';
    $base_dir = __DIR__ . '/../src/';
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }
    $relative_class = substr($class, $len);
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});

 

Link to comment
Share on other sites

Any flaws in my vendor autoloader?

Well, your autoloader doesn't do what you described. If it needs to load React\EventLoop\Factory, then it transforms that class into file path $ProjectRoot/vendor/React/EventLoop/src/Factory.php, not $ProjectRoot/vendor/react/event-loop/src/Factory.php as described. There is no code to lower-case and hyphenate the namespace.

 

This is where PSR-4 and namespace prefix => directory maps come into play. You'd map the namespace prefix React\EventLoop to the directory $ProjectRoot/vendor/react/event-loop/src then use the remaining namespace/classname to finish the path. Do the same for your NotionCommotion\SimpleRouter namespace.

 

Also, is it typical to position the autoloader which occurs most often first to reduce amount of code which needs to be executed?

Couldn't hurt, but it's not really something I'd worry about a whole lot. Reducing the number autoload functions would probably make more of a difference. As far as I know, function calls in PHP are a bit heavy so the fewer the better.

Link to comment
Share on other sites

Well, your autoloader doesn't do what you described. If it needs to load React\EventLoop\Factory, then it transforms that class into file path $ProjectRoot/vendor/React/EventLoop/src/Factory.php, not $ProjectRoot/vendor/react/event-loop/src/Factory.php as described. There is no code to lower-case and hyphenate the namespace.

 

This is where PSR-4 and namespace prefix => directory maps come into play. You'd map the namespace prefix React\EventLoop to the directory $ProjectRoot/vendor/react/event-loop/src then use the remaining namespace/classname to finish the path. Do the same for your NotionCommotion\SimpleRouter namespace.

 

Actually, it does exactly what I described, which happens to be exactly what you described.  I requires different the file to be located at $ProjectRoot/vendor/NotionCommotion/SimpleRouter/src/Router.php, and that is what I didn't like about my approach.  If you look at how Composer does it, they don't appear to use strtolower or insert hyphens, but use the json file to make the connection.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.