Cannot find Class with PHP Namespace
Asked Answered
R

6

34

I posted some questions previously regarding the use of Namespaces in PHP and from what I got, this example code I have below should be working.

However I am getting errors when I try to use Namespace in PHP like this. Here is the first error when running the code below as is...

Fatal error: Class 'Controller' not found in E:\Controllers\testing.php on line 6

E:\Controller\testing.php File

<?php
use \Controller;

include('testcontroller.php');

$controller = new Controller;
$controller->show();
?>

E:\Controller\testcontroller.php File

<?php
use \Library\Registry;

namespace Controller
{
    class Controller
    {
        public $registry;

        function __construct()
        {
            include('E:\Library\Registry.class.php');
            $this->registry = new Registry;
        }

        function show()
        {
            echo $this->registry;
            echo '<br>Registry was ran inside testcontroller.php<br>';
        }
    }
}
?>

E:\Library\Registry.class.php File

<?php
namespace Library\Registry
{
    class Registry
    {
        function __construct()
        {
            return 'Registry.class.php Constructor was ran';
        }
    }
}
?>

As you can see I tried to make it as simple as possible just to get the Namespace part working. I have tried different variations and cannot seem to figure it out.

Rectifier answered 22/12, 2011 at 23:10 Comment(0)
W
48

Even when using use statement, you need to specify the namespace of the class you are trying to instantiate. There are a lot of examples here: http://www.php.net/manual/en/language.namespaces.importing.php

To understand it better, I will describe to you how it works. In your case, when you do use \Controller, the whole Controller namespace becomes available to you, but not the classes that are in this namespace. So, for example:

<?php
include('testcontroller.php');

use \Controller;

// Desired class is in namespace!
$controller = new Controller\Controller();

// Error, because in current scope there is no such class
$controller = new Controller();

$controller->show();
?>

Another example:

testcontoller.php:

<?php
namespace Some\Path\To\Controller;

class Controller
{
    function __construct()
    {

    }

    function show()
    {
        echo '<br>Was run inside testcontroller.php<br>';
    }
}
?>

testing.php:

<?php
include('testcontroller.php');

use \Some\Path\To\Controller;

// We now can access Controller using only Controller namespace,
// not Some\Path\To\Controller
$controller = new Controller\Controller();

// Error, because, again, in current scope there is no such class
$controller = new Controller();

$controller->show();
?>

If you wish to import exactly the Controller class, you need to do use Controller\Controller - then this class will be accessible in your current scope.

Woodcut answered 23/12, 2011 at 9:33 Comment(6)
Good example, I figured this out last night but good for you to describe it. So I think I would be better off to put all my Library classes into the same namespace like Library and then on the testcontroller above I could use use Library\Classname for each class in my library that I need in that controller so I can then assess them like this new Registry etc... on php.net/manual/en/language.namespaces.importing.php I think it is saying this is importing a global class so I wasn't sure if it was a bad ideaRectifier
I`m trying to limit dependecies of each class, thats why for me it is not hard to use full qualified name for class.Woodcut
Thats wrong: With use you also define an alias for a concrete class. In the example use \Controller; new Controller; its not very obvious, but you can try use Vendor\Package\Component\Controller; new Controller. What I mean: Its wrong, that you need to define the namespace of a class in every case. See the link in jasondeavis comment. Its described, how PHP resolves relative classnames.Counterpoise
Read whole my comment. Especially, "If you wish to import exactly Controller class, you need to do use Controller\Controller, and, then, this class would be accessible in your current scope." part.Woodcut
I am just confuse, why include('testcontroller.php'); when you importing class already by use \Some\Path\To\Controller;?Keitt
@rai using a class doesn't import it automatically. You need to have some kind of class loader. Example was provided the way that would allow running it in any environment and no class loader was provided in it.Woodcut
C
10

Its not that good idea to name the namespace, like the class, because it is confusing (and I think this is what happens here). There moment you define the alias via use Controller this referenes to either a class \Controller, or the namespace \Controller, but your class, because it is within the namespace, is named \Controller\Controller 1

use Controller;
$class = new Controller\Controller;

or

$class = new \Controller\Controller;

or

use Controller\Controller;
$class = new Controller;

The idea is, that the moment you try to access a class with its relative name it tries to map the "first part" against any alias defined using use (remeber use MyClass is the same as use MyClass as MyClass. The thing after as is the alias).

namespace MyNamespace\MyPackage\SomeComponent\And\So\On {
  class MyClass {}
}
namespace Another {
  use MyNamespace\MyPackage\SomeComponent; // as SomeComponent
  $class =              new SomeComponent\An\So\On\MyClass;
}

As you can see PHP finds SomeComponent as the first part and maps it against the SomeComponent-alias the line above.

You can read more about it in the manual about namespaces.

1 Its called "Full-qualified classname", if you name a class with its complete name.

Counterpoise answered 23/12, 2011 at 7:7 Comment(0)
A
6

When you put a class Controller in the namespace Controller, then you have to reference it that way:

$controller = new Controller\Controller();

\Controller would be a class in the global (default) namespace, i.e. as if you used no namespace at all.

Atworth answered 22/12, 2011 at 23:16 Comment(6)
dont think you need to do that because of the use Controller; statementVesicle
@Cassy yeah that is why I used use Controller; so hopefully I wouldn't have to do that uglier methodRectifier
@Casey I tried it anyway just to see what would happen and it changed the error to Fatal error: Class 'Controller\Registry' Rectifier
This is because you are refering to Registry in the global namespace and not in the namespace where it is used. Also your class is actually called Library\Registry\RegistryChampignon
@Dan, changing $this->registry = new Registry; to new \Library\Registry\Registry did in fact get rid of the errors. There has to be a way to do what I am trying to do though, do you know of a way? The whol point of use is to avoid thisRectifier
Comments in the php manual say you have to stick after your namespace definition but before the curly bracers. If you dont have multiple namespaces in one file you can even remove the curly bracers around the namespace alltogether.Champignon
R
3

Strangely I have found that in my example code from the Question above, if I change all the Namespace's that are defined to something like MyLibrary so it would be like this code below...

E:\Library\Registry.class.php File

<?php
namespace MyLibrary
{
    class Registry
    {
        function __construct()
        {
            echo 'Registry.class.php Constructor was ran';
        }
    }
}
?>

Then when I use use MyLibrary\Registry; in another file, I am able to access it how I had planned...

$this->registry = new Registry;

The reason this is very strange to me is this now makes a class name appear to be a Namespace as well. So I would not need to set a Namespace to 'MyLibrary\Library' to access the Registry instead I would do it like I showed in this answer to be able to access it with just calling the name of the class.

I hope this makes sense and helps someone else. I will not accept this as the answer as I am hoping someone with more know-how will come in and post a better Answer with explanation

Rectifier answered 23/12, 2011 at 0:22 Comment(0)
V
1

try

<?php
use \Library\Registry;

namespace Controller;
class Controller
{
    public $registry;
    function __construct()
    {
        include('E:\Library\Registry.class.php');
        $this->registry = new Registry;
    }
    function show()
    {
        echo $this->registry;
        echo '<br>Registry was ran inside testcontroller.php<br>';
    }
}
?>

and

<?php
namespace Library\Registry;
class Registry
{
    function __construct()
    {
        return 'Registry.class.php Constructor was ran';
    }
}
?>
Vesicle answered 22/12, 2011 at 23:18 Comment(0)
H
1

First off, I believe you are using composer or composer is initialised in your project. If so, check composer.json file for your autoload, psr-4 definition. For example, if the root of your application is "App", then in your psr-4, you should be doing "autoload": { "psr-4": { "App\\": "./" } }, Furthermore, remember to clear composer cache and dump-autoload from the terminal as follows:

composer clear-cache
composer dump-autoload
Haeres answered 30/7, 2022 at 16:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.