質問

I've been struggling a bit with namespaces in PHP lately. Beginning a new project in an object oriented fashion, I want to prepare the project neat and clean from the structure to the functionnality.

As I said, it is developped in object oriented PHP. Using Doctrine as ORM, the global structure of the project looks like this :

/admin
  |- /index.php
/css
/fonts
/img
/js
/src
  |- /controller
  |- /model
  |- /system
    |- /utils
      |- /Singleton.php
  |- /view
/vendor
/index.php

Ok, so let's take a quick example.

I have the Singleton class which lies in the following path : src/system/utils/Singleton.php

<?php

namespace system\utils;

/**
 * Base class for singleton use cases
 */
class Singleton 
{
    /**
     * Returns the *Singleton* instance of this class.
     * @staticvar Singleton $instance The *Singleton* instance of this class.
     * @return Singleton the *Singleton* instance.
     */
    public static function getInstance() {
        static $instance = null;
        if ($instance === null) {
            $instance = new static();
        }
        return $instance;
    }

   // Private constructor, only local construction allowed
    protected function __construct() {
    }

    // Private cloner, only current instance 
    private function __clone() {
    }

    // Private wakeup function, to prevent unserializing
    private function __wakeup() {
    }
}

As you can see, this class uses the namespace system\utils. Then, in the /index.php file in the main folder, to use this class I do the following :

<?php
  require_once 'bootstrap.php';
  use system\utils\Singleton;

  $singleton = Singleton::getInstance();
  var_dump($singleton);
?>

(Note : the bootstrap.php is the configuration file for Doctrine. I know it uses some kind of autoloading, and that probably could be a part of the problem I'm exposing here, so you can find the content of it at the end of this post)

This example works perfectly fine. Great. BUT. If I do the exact same test not in /index.php but /admin/index.php, it fails and PHP says it can't find the class Singleton.

I'm getting really confused here, because I thought namespaces were some kind of "unique" identifier for each class in a project architecture and then could be used to retrieve one class anywhere in the structure of the project. Obviously, I was wrong and googling around that didn't help me at all.

My best guess goes towards autoloading and that kind of stuff, since I know Doctrine uses it. Maybe I missed something while configuring the ORM. As I said earlier, here is the content of my /bootstrap.php containing all the configuration settings.

<?php
// bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;

require_once "vendor/autoload.php";

// Create a simple "default" Doctrine ORM configuration for Annotations
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/model/entities/"), $isDevMode);

// database configuration parameters
$conn = array(
    'driver' => 'pdo_mysql',
    'host' => 'host',
    'port' => 'port',
    'charset' => 'utf8',
    'dbname' => 'database',
    'user' => 'database',
    'password' => 'database'
);

// obtaining the entity manager
$entityManager = EntityManager::create($conn, $config);

I don't know if this could help, but I can use namespaces correctly anywhere inside the /src folder but it seems like it doesn't work in another subfolder of the main directory.

If someone has a clear explanation about what happens here and how namespaces should be managed in PHP, and eventually how I could apply corrections to my code, it'd be really nice of you to share your knowledge.


EDIT :

Okay so it definitely has to do with autoloading. I managed to get it working by writing the correct use statement for the namespace AND declaring an __autoload function requiring files once in ../src/.

<?php
    use system\utils\Singleton;

    $singleton = Singleton::getInstance();
    $singleton->manageSession();

    function __autoload($className)
    {
        echo "Autoloading $className.php in ../src/";
        require_once("../src/$className.php");
    }

    require_once '../bootsrap.php';
?>

This outputs Autoloading system\utils\Singleton.php in ../src/

So it works and I can continue with that solution. The thing is I see that as a workaround, not a solution. I still believe I'm doing something wrong and it could be optimized, but can't figure out how... If you have some knowledge to share on this, I'm all ears!


Thanks a bunch in advance and sorry for the big post. If you need any kind of additional info, just ask.

役に立ちましたか?

解決

The problem is that you're doing exactly the same in the admin/index.php, which in this case is wrong.

The issue lies at the following line:

require_once 'bootstrap.php';

This tells php look for the file bootstrap.php first relative to the file which is including/requiring the file and if that fails, in the configured include paths.

It's not included in your directory structure, but from the looks of it, bootstrap.php sits at the same level as index.php. To fix your issue replace the above line in admin/index.php with the following:

require_once '../bootstrap.php';
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top