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.