找不到带有PHP命名空间的Class

问题描述:

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.

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.

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.

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';
    }
}
?>

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

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.