Sage 9 PSR-4 - Not loading or instantiating classes


#1

Hi all,

Have searched the net and forums prior to posting this but can’t seem to find an answer.

I see that with PSR-4 autoloading (based on code within Sage/its dependencies) we only need to “use” a namespace to require the file we need.

However, I can’t seem to load my files or even instantiate them.

I can see that in Sage composer.json we have

"psr-4": {
  "App\\": "app/"
}

So, naturally, I have added a file called ‘post-types.php’ inside of ‘app/’ which is a basic class to load other post types. The problem is that nothing in this file is being executed - unless I manually instantiate the class at the end of the file.

app/themes/fabric/app/post-types.php

<?php

namespace App;

var_dump('outside class scope');

//use App\PostTypes\Event;

class PostTypes
{
    public function __construct()
    {
        //    new Event();
        var_dump('inside class constructor');
    }
}

Neither var_dumps work even though the first is outside of the class scope.
I know I could simply add ‘new PostTypes();’ at the bottom of this file, but surely I should have to do that with the autoloading? Seems to be the case when I look at other classes within vendor/
Can anyone point out what I’m doing wrong here? Feel like it must be something very simple that I’m missing.

Any help appreciated.
Thanks


#2

http://www.php-fig.org/psr/psr-4/

PSR-4 autoloading does not check for dashes. also

All class names MUST be referenced in a case-sensitive fashion.


#3

Thanks for pointing that out Kalen.

I’ve updated ‘post-types.php’ to ‘Post_Types.php’ and the class name from ‘PostTypes’ to ‘Post_Types’ based on the examples in the PSR-4 info page - however, the file still doesn’t seem to be loading.

Am I making an incorrect assumption that this file is supposed to be automatically loaded or do I actually have to reference it somewhere like in functions.php alongside the array at the bottom of this snippet?

array_map(function ($file) use ($sage_error) {
    $file = "../app/{$file}.php";
    if (!locate_template($file, true, true)) {
        $sage_error(sprintf(__('Error locating <code>%s</code> for inclusion.', 'sage'), $file), 'File not found');
    }
}, ['helpers', 'setup', 'filters', 'admin', 'new/files/here.php]);

I am okay with doing that, but was hoping for autoloading.

Thanks again.


#4

I usually add all the files to the import array, only change it to one import per line to make it easier to read.

Have not used autoloading in sage but I don’t believe the files will be automatically loaded, they will be loaded when used/needed. You have to use the class somewhere for it to load. Something like

Post_Types::register()

or

new Post_Types()

in setup.php maybe?

The app folder is pretty full of files in many of my projects so I would probably change it to

"psr-4": {
  "App\\": "app/classes/"
}

#5

@KimH is right that you have to instantiate your class somehow before it actually does anything. Autoloading in PHP basically works like this:

  • Your give your script instructions for how to find things based on their namespacing (in this case, those instructions are PSR-4). i.e. “You can find My\Class in ./src/my/class.php.”
  • This doesn’t actually “do” anything, or cause anything to be executed on its own: It just tells your script where it can find things.
  • When, elsewhere in your script, you write, say, $class = new My\Class($args);, then PHP says “Oh, you told me I can find My\Class in ./src/my/class.php, I’ll go open that up and use the code in there to instantiate your class!”

The end result is similar to doing include('./src/my/class.php') in that it makes your class available for instantiation, but it saves you from having to type all those includes, and only loads files you actually use (in addition to probably have other benefits I’m unaware of). include will execute the entire file immediately; autoloading only executes the file when the class it contains is requested.

I think the reason you’re running into an issue is that autoloading is usually used to load classes, which do exactly nothing until they’re instantiated, and autoloading won’t even open up those files unless you try to instantiate a class. In order to fire the code in your PostTypes class, you’d need to do something like what @KimH suggested. Best practice is not to have functions that fire stuff off in files where you define classes, so I’d recommend something like the following:

// app/PostTypes.php
namespace App;

class PostTypes
{
    public static function register()
    {
         new static();
         // my memory is a little rusty; you might want to use `new self();` instead.
    }

    // Do your other class stuff
}
// app/setup.php

PostTypes::register();

#6

@KimH @alwaysblank thank you for the pointers - I see that my assumptions of how to use this was incorrect. Instantiating the class manually is all I needed and now everything is working as it should :slight_smile:
I’m not sure why I was thinking autoloading would handle this for me, to be honest, but this makes a lot more sense now.

Thanks!