Home > Blockchain >  psr4 autoloader does not autoload from within a class
psr4 autoloader does not autoload from within a class

Time:04-25

The autoloader works when I use it in index.php, but when I create an object within index.php and this object has to create other objects (which are all in the same namespace), it throws the error Uncaught Error: Class 'xxx' not found in (...).

My composer.json looks like this:

{
    "autoload": {
        "psr-4": {
            "pizzaCase\\": "src",
            "Connection\\": "src/Connection/",
            "Elements\\": "src/Elements/"
        }
    },
    "require": {
        "cboden/ratchet": "^0.4"
    }
}

My index.php looks like this:

<?php
    require_once __DIR__. '/vendor/autoload.php';
    require_once __DIR__."/src/config.php";

    use Connection\Database;
    use Elements\Form;
    use Elements\FormElement;
    use Elements\FormElementRadio;
    
    // Database::init();
    $form = new Form();

    $data["options"] = "soemthing, something else";
    $form->addElement("", "pizza", "", "Choose pizza", "radio", $data);
?>

In the addElement method I then create an object which is also within the src/Elements/ namespace, but it throws the error mentioned above.

The body of my addElement method looks like this:

<?php
namespace Elements;

    class Form
    {
        public static $leftSize = 3;
        protected $elements = [];
    
        public function addElement($table, $name, $value, $label=false, $type = false, $data = false) 
        {
            $type = ucfirst($type);
            $class = "FormElement{$type}";
    
            //FAILS HERE
            if(class_exists($class))
            {
                //CLASS EXISTS, CREATE OBJECT FROM RESPECTIVE CLASS
                $form = new $class($table, $name, $value, $label, $type, $data);
    
                $this->elements[$name] = $form;
            }
        }
    }

What am I doing wrong (or missing)? How come the autoloader can autoload it from index.php, but the object I create cannot create other objects without autoloader failing?

CodePudding user response:

The difference is not to do with where the code is being run; the difference is that the failing code is trying to choose which class to load dynamically.

In PHP, namespaces are essentially a compile-time feature: before any of your code is run, the compiler looks at all references to class names which don't start with \, and prefixes them with the current namespace, or according to rules you've specified with use statements. When the code runs, the current namespace, and use statements, aren't visible at all.

When you specify a class name dynamically, the compiler just sees a string, not a class name, so leaves it alone. Then when the code runs, the class name looked up is assumed to be fully specified, not relative to the current namespace or use statements.

So the solution is simple: specify the full namespace when creating the dynamic class name:

$class = "Elements\FormElement{$type}";

You can also use the magic constant __NAMESPACE__ to have the compiler substitute the current namespace name for you:

$class = __NAMESPACE__ . "\FormElement{$type}";

(That obviously won't account for any use statements, though.)

CodePudding user response:

It could be because you’re having multiple namespaces for the src directory.

Usually you would just create a namespace for src like this

“psr-4": {
    "PizzaCase\\": "src"
}

And then just use PizzaCase\Elements and PizzaCase\Connections as namespaces

  • Related