您的位置:首页 > 编程语言 > PHP开发

A Brief Introduction to PHP Namespacing

2015-06-28 14:47 721 查看


A bit of history #

In PHP prior to 5.3 (2009), any class you define lived at the same global level as other classes.

Class
User
,
class
Contact
,
class
StripeBiller
--they're
all together in the global namespace.

This may seem simple, but it makes organization tough, which is why PHP developers started using underscores to separate their class names. For example, if I were developing a package called"Cacher", I might name the class
Mattstauffer_Cacher
so
as to differentiate it from someone else's
Cacher
--or
Mattstauffer_Database_Cacher
,
to differentiate it from an API cacher.

That worked decently, and there were even autoloading standards that separated out the underscores in class names for folders on the file system; for example,
Mattstauffer_Database_Cacher
would
be assumed to live in the file
Mattstauffer/Database/Cacher.php
.

An autoloader is a piece of code that makes it so that, instead of having to
require
or
include
all
of the files that contain your class definitions, PHP knows where to find your class definitions based on a particular convention.

But it was pretty messy, and often ended up with class names like
Zend_Db_Statement_Oracle_Exception
and
worse. Thankfully, in PHP 5.3, real namespaces were introduced.


The basics of namespaces #

Namespaces are like a virtual directory structure for your classes. So
class
Mattstauffer_Database_Cacher
could become
class
Cacher
in the
Mattstauffer\Database
namespace:
<?php

class Mattstauffer_Database_Cacher {}


is now:
<?php namespace Mattstauffer\Database;

class Cacher {}


And we would refer to it elsewhere in the app as
Mattstauffer\Database\Cacher
.


A real example #

Let's take Karani--it's a CRM with a financial component, so it tracks donors and receipts, among many other things.

Let's set
Karani
as
our top-level namespace (sort of like the parent folder--usually named after your app or package). This might have some classes related to Contacts, and some related to Billing, so we're going to create a sub-namespace for each,
Karani\Billing
and
Karani\Contacts
.

Let's make a class or two in each:
<?php namespace Karani\Billing;

class Receipt {}

<?php namespace Karani\Billing;

class Subscription{}

<?php namespace Karani\Contacts;

class Donor {}


So, we're picturing a directory structure like this:
Karani
Billing
Receipt
Subscription
Contacts
Donor

[/code]


Referencing other classes in the same namespace #

So, if a Subscription can send a Receipt, it's easy to refer to it:
<?php namespace Karani\Billing;

class Subscription
{
public function sendReceipt()
{
$receipt = new Receipt;
}
}


Since
Receipt
is
in the same namespace as
Subscription
,
you can just refer to it like you would if you weren't using namespaces.


Referencing other classes in different namespaces #

OK, but what if I want to reference a Receipt inside of a Donor?
<?php namespace Karani\Contacts;

class Donor
{
public function sendReceipt()
{
// This won't work!
$receipt = new Receipt;
}
}


You guessed it: This won't work.

We're in the
Karani\Contacts
namespace,
so when we wrote
new
Receipt
, PHP assumes we're talking about
Karani\Contacts\Receipt
.
But that class doesn't exist, and that's not what we're looking for.

So, you'll get a
Class
Karani\Contacts\Receipt not found
error.

You might be tempted to modify it to instead say
$receipt
= new Karani\Billing\Receipt
--but even that won't work. Since we're in the
Karani\Contacts
namespace
right now, it's seeing anything you write as being relative to the namespace you're in. So that would try to load a class named
Karani\Contacts\Karani\Billing\Receipt
,
which also clearly doesn't exist.


Use
blocks
and Fully-Qualified Class Names

Instead, you have two options:

First, you can precede it with a slash to create its FQCN (Fully Qualified Class Name):
$receipt
= new \Karani\Billing\Receipt;
, which sends the signal to PHP to escape out of the current namespace before looking for this class.

If you precede the full namespace with a slash, creating the FQCN, you can refer to this classanywhere in your app without worrying about your current namespace.

Or, Second, you can
use
the
class at the top of the file, and then just reference it as
Receipt
:
<?php namespace Karani\Contacts;

use Karani\Billing\Receipt;

class Donor
{
public function sendReceipt()
{
$receipt = new Receipt;
}
}


As you can tell,
use
imports
a class from a different namespace into this namespace so we can refer to it more easily. Once you've imported the class, any time you reference
Receipt
in
this class, it'll assume you're pointing to the imported class.


Aliasing #

But, what if you also have a
Receipt
class
in your current namespace? What if your class needs access to both
Karani\Contacts\Receipt
and
Karani\Billing\Receipt
?

You can't just import the
Karani\Billing\Receipt
class,
or you won't be able to use both--they'd both have the same name in this class.

Instead, you'll need to alias it. You can change the
use
statement
to something like
use
Karani\Billing\Receipt as BillingReceipt;
. Now you've aliased the class, and then you can refer to the imported class as
BillingReceipt
throughout
your class.


PSR-0/PSR-4 Autoloading #

You know the folder analogy I just used above?

It's easy to think about your classes that way, but there's actually not any inherent connection between your namespaces and your files' structure. Unless you use an autoloader, PHP doesn't have any idea where those
classes actually live in your directory structure.

Thankfully, PSR-0 (now deprecated) and PSR-4 are
autoloading standards that actually map your namespaces to real folders. So, if you're using PSR-0 or PSR-4--which is extremely likely if you're using Composer or
any modern framework-- and a compatible autoloader, you can assume that the classes actually are in folders.


Composer and PSR-4 Autoloading #

So, let's say I want the
Karani
namespace
to live in my
src
folder.

Here's my folder structure for a generic, framework-independent project:
app
public
src
Billing
Contacts
vendor

[/code]

As you can see, the
src
folder
represents the
Karani
top
level namespace. Since I'm using Composer as my autoloader, all I need to do to get my application to autoload my classes is teach Composer how to map namespaces to folders. Let's do that using PSR-4.

I'm going to open up
composer.json
and
add a PSR-4 autoload section:
{
"autoload": {
"psr-4": {
"Karani\\": "src/"
}
}
}


So you can see: the left side is the namespace that we're defining (note that you need to escape the slash separators here by doubling them), and the right side is the directory.


Conclude. #

As you can see, there's a lot going on here, but it's really pretty simple: 98% of the time, you're going to be working with a PSR-4-structured, Composer-autoloaded, set of classes.

So 98% of the time, you can check your
composer.json
,
figure out where the root of the top level namespace lives, and assume you'll then have a one-to-one map of your namespace and the folders/files in that directory. Done.

And remember: next time you get
Class
SOMETHING not found
, you probably just need to remember to import it with a
use
statement
at the top of your file.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: