2

I have a DB wrapper class that uses PDO and in the constructor I create a PDO object. The wrapper class is in our namespace and we are using an autoloader. The issue is that the PDO class cannot be found within our namespace, so I tried using the global namespace as described here.

//Class file
namespace Company\Common;
class DB {
    private function __construct(){
        $this->Handle=new PDO(...);
    }
}

With this, I get this (as expected):

Warning: require(...\vendors\Company\Common\PDO.class.php): failed to open stream

If I do this:

namespace Company\Common;
use PDO;

I get this:

Fatal error: Class 'DB' not found in ...\includes\utils.php

And utils.php contains this on the error line, which worked fine before implementing namespaces:

DB::getInstance();

Alternatively I tried this:

namespace Company\Common;
class DB {
    private function __construct(){
        $this->Handle=new \PDO(...);
    }
}

Which tried to load the PDO class within our namespace as it originally did.

How can I resolve this? I thought by doing use PDO or new \PDO it would load the global PDO class, but it doesn't seem to be working?

Community
  • 1
  • 1
Jason Kaczmarsky
  • 1,666
  • 1
  • 17
  • 30

5 Answers5

6

In Namespaced PHP, references to a class must include the namespace of that class, unless you have a use statement that includes that class or part of its namespace.

So, if you have no use statement for it, then PDO and other global classes must be referenced with the leading backslash -- ie $obj = new \PDO();

If you have a use statement that references that class, then you may reference it by just the classname:

use PDO;
....
$obj = new PDO();

If you're referencing a lot of global classes, you'll need to use each of them individually if you want to avoid using the backslash every time.

deceze
  • 510,633
  • 85
  • 743
  • 889
SDC
  • 14,192
  • 2
  • 35
  • 48
0

Normally in our projects we do set include path like this:

set_include_path('PATH_TO_GLOBAL_LIBS; PATH_TO_LIBRARY_1; PATH_TO_LIBRARY_X; PATH_TO_DOCUMENT_ROOT');

By this we tell PHP (Apache) to search these paths for any classes that should be included by autoloader.

Then assuming we have some Library_1 in /var/www/Libs/library_1 and that this path is added to the include_path we could do this:

namespace Company\Common;

Class DB {
    private function __construct() {
        $this->Handle = new \Library_1();
    }
}

which should be the same as

namespace Company\Common;

use \Library_1;

Class DB {
    private function __construct() {
        $this->Handle = new Library_1();
    }
}
shadyyx
  • 15,825
  • 6
  • 60
  • 95
  • This was all working fine before implementing namespaces. I had already been using an autoloader to load all of this. Would the include_path still be an issue then? – Jason Kaczmarsky Jan 11 '13 at 16:25
0

This works, your problem is elsewere.

index.php

<?php
include('_db.php');
use Company\Common\DB;
new DB;

_db.php

<?php
namespace Company\Common;
use \PDO;

class DB {
    public function __construct() {
        $this->Handle = new PDO;
    }
}
Glavić
  • 42,781
  • 13
  • 77
  • 107
  • The only thing I've noticed is that if I do DB::getInstance() after registering the autoloader it works fine, the DB class is included without issue and can be used. If I try to use DB::getInstance() within utils.php(not a class), it says it cannot find class DB. – Jason Kaczmarsky Jan 11 '13 at 16:30
  • Like i said, the problem is elsewere. Sounds like that autoloader is not loaded when you are within utils.php ... – Glavić Jan 11 '13 at 16:31
  • It seems as if the `use Company\Common\DB;` only applies to the file I used it in. If I have it within utils.php as well, it works fine. Is it true the the `use` statement only applies to the current file and doesn't cascade throughout included files? Edit, Yup ("Importing rules are per file basis, meaning included files will NOT inherit the parent file's importing rules. ") – Jason Kaczmarsky Jan 11 '13 at 16:36
  • @JasonKaczmarsky, yep, what is true. – Glavić Jan 11 '13 at 17:11
0

To access global objects like PDO and DateTime, you need to prefix them with a backslash. So you then have two options.

First, either use the classes you wish to use in your name-spaced class file:

<?php
namespace Vendor;

use \PDO;
use \PDOException;

class MyClass
{
    public function __construct()
    {
        try {
            $db = new PDO($dsn, $user, $pass);
            $db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        }
        catch (PDOException $e) {
            echo $e->getMessage();
            exit;
        }
    }
}

Or just use the backslash-prefixed declarations within your class:

<?php
namespace Vendor;

class MyClass
{
    public function __construct()
    {
        try {
            $db = new \PDO($dsn, $user, $pass);
            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        catch (\PDOException $e) {
            echo $e->getMessage();
            exit;
        }
    }
}

Personally, I prefer the first approach.

Martin Bean
  • 38,379
  • 25
  • 128
  • 201
0

Solved it. I didn't realize that aliasing a namespace only applies to the current file, and not any future included files. Found this on PHP.net which also applies to aliasing:

Importing rules are per file basis, meaning included files will NOT inherit the parent file's importing rules.

Jason Kaczmarsky
  • 1,666
  • 1
  • 17
  • 30