skip to Main Content

I have a file. Let’s call it Foo.php. It looks like this:

namespace AppHelpers;

enum FooTypes: string {
    case A = "A";
    case B = "B";
}

class Foo {

   const BAR = "Bar";
   // ...
}

This will work:

use AppHelpers{ Foo, FooTypes };

echo Foo::BAR;
echo FooTypes::A->value;

This will fail with a "class FooTypes not found" error:

use AppHelpers{ Foo, FooTypes };

// echo Foo::BAR;
echo FooTypes::A->value;

It looks like I have to use the "main" class Foo before I can use any of the enums decalred in the same file. But that seems kind of absurd. What is happening? How do I resolve this? I could maybe seperate the enum into a seperate file but it really doesn’t make sense to me in my use case.

2

Answers


  1. because use statement does not really "load" the file until you call what’s in it,

    when you call Foo::BAR it loads the file AppHelpersFoo.php thus you able to use FooTypes which also from that file.

    Here another example, it will work fine, but if you comment the 1st return statement, it will throw an error

    // assume file is in appHelpersFoo.php
    use AppHelpers;
    
    // this nonsense doesn't exist, but as long as its not called, theres no problem;
    use SomethingThatDontExist;
    
    Route::get('/something', function() {    
        
        //Works Fine as it loads appHelpersFoo.php first and able to find FooTypes inside that file
        return [HelpersFoo::BAR, HelpersFooTypes::A];
    
         //trows an error because it tries to load appHelpersFooTypes.php first
        return [HelpersFooTypes::A, HelpersFoo::BAR];
    
    });
    
    Login or Signup to reply.
  2. The use statement is purely a way to provide a short-hand within a particular file for a symbol (class, function, enum, etc) which is in a different namespace. See Using namespaces: aliasing/importing in the PHP manual. It lets you type echo FooTypes::A->value; instead of echo AppHelpersFooTypes::A->value; but that is all it does.

    This is completely unrelated to how the definition of a particular symbol is loaded. For that, you are probably relying on autoloading, which runs a callback function when a particular class / enum / etc is first used. The callback function then generally includes a file defining that symbol.

    To be completely clear, defining an alias for a symbol with a use statement, does not trigger the autoloader to be run. Only actually executing code that needs that definition will trigger the autoloader.

    The most common convention is to put each definition into a separate file, and then configure the autoloader to load the file based on that name; but that is just a convention. If you want to have a different convention, e.g. that any name ending with "Types" is in the same file as the class without that ending, you can write an autoload function which implements that. For instance:

    function my_autoload_callback(string $nameToLoad): void {
        $filePath = PROJECT_ROOT . '/' . str_replace('\', '/', $nameToLoad) . '.php';
        if ( file_exists($filePath) ) {
            require_once $filePath;
        }
        else {
            // try without "Types" on the end
            // should probably have another file_exists check here
            require_once str_replace('Types.php', '.php', $filePath);
            // we could check if this has found the right class
        }
        // if the function reaches here without loading the required class,
        //   you will get a "class not found" error from PHP
    }
    

    Note that the autoloader does not know whether it is looking for a class, an interface, a trait, or an enum; the only input you will get is the fully-qualified name, such as "AppHelpersFooTypes".

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search