Josh Collins
Josh Collins

Reputation: 11

How can I tie my namespaces to my folder hierarchy in PHP?

I am currently learning about namespaces, the usage of use and the likes, but for some reason, I can't quite get it.

I have this structure:

main_project
  -inc
    -..
    -..
    -Social
      -interfaces
         -[*]social_scrape.php
         -[*]social_post.php
      -[*]instagram.php
      -[*]posts.php
    -..

In short, I have the folder inc from which I auto-load (it's a for loop that require_once a bunch of files from a list) only these files:

~path/inc/Social/instagram.php + posts.php

Do keep in mind, I am not loading the files in the Interfaces folder. Now, my current instagram.php looks exactly like this:

<?php

namespace Social_Life;


interface Scrape_Interface {

    public function build_endpoint();

    public function get_json();

    public function get_data();
}

class Scrape_Instagram implements Scrape_Interface {}

But this is bad in terms of OOP. The interface should be a contract that other classes should be able to use, as such, it should be agnostic of my instagram.php file. So, I decided to move it into:

~path/inc/Social/Interfaces/social_scrape.php and initialize it (without loading the file from the autoloader) like this:

use Social_Life\Interfaces\Social_Scrape\Scrape_Interface; where my new scrape_interface.php file looks like this:

<?php

namespace Social_Life\Interfaces\Social_Scrape;

interface Scrape_Interface {

    public function build_endpoint();

    public function get_json();

    public function get_data();
}

But this fails:

Fatal error: Interface 'Social_Life\Interfaces\Social_Scrape\Scrape_Interface' not found in C:\xampp\htdocs\wordpress\wp-content\themes\s-master\inc\Social\instagram.php on line 7

Upvotes: 1

Views: 218

Answers (1)

Sammitch
Sammitch

Reputation: 32272

You've only solved 2/3 of the problem, namespacing and file hierarchy, which are meaningless without something that maps a namespaced class name to a file in that hierarchy and loads in automatically. An autoloader.

You can write your own and register it with spl_autoload_register(), but what I would suggest instead of that is bringing your namespacing and file hierarchy into line with PSR-4 and use Composer as the autoloader. Now your code is more easily reusable and redistributable, and you can leverage Composer to pull in and update your dependencies, or publish your code for others to use.

So, for example, we'd want to adjust the hierarchy slightly:

main_project/
  -inc/
    -Social/
      -Interfaces/
         -Social_Scrape.php // joshc\social_scrape\Social\Interfaces\Social_Scrape
         -Social_Post.php   // joshc\social_scrape\Social\Interfaces\Social_Post
      -Instagram.php        // joshc\social_scrape\Social\Instagram
      -Posts.php            // joshc\social_scrape\Social\Posts
  -composer.json
  -vendor/
  -example.php

Note that the filename casing needs to match the class and declared namespace.

And your minimal composer.json would look something like:

{
    "name": "joshc/social_life",
    "autoload": {
        "psr-4": {
            "joshc\\social_life\\": "inc/"
        }
    }
}

Which defines that the namespace joshc\social_life is rooted in the inc/ directory relative to where composer.json is located. Note that PSR-4 mandates that you declare a vendor namespace [eg: joshc] and convention is that the next level is the package name. [eg: social_life]

Running composer install generates the composer autoloader.

An example class file, Social_Scrape.php:

<?php
namespace joshc\social_life\Social\Interfaces;

interface Social_Scrape { ... }

And example.php:

<?php
require(__DIR__.'/vendor/autoload.php');
use joshc\social_life\Social\Interfaces\Social_Scrape;

class Scrapey implements Social_Scrape { ... }

Now all you need to worry about is that vendor/autoload.php is included somewhere in your application code and you don't have to loop through every file in your hierarchy at any point.

Upvotes: 2

Related Questions