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_Cacherso
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_Cacherwould
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
requireor
includeall
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_Exceptionand
worse. Thankfully, in PHP 5.3, real namespaces were introduced.
The basics of namespaces #
Namespaces are like a virtual directory structure for your classes. Soclass Mattstauffer_Database_Cachercould become
class Cacherin the
Mattstauffer\Databasenamespace:
<?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
Karanias
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\Billingand
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
Receiptis
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\Contactsnamespace,
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 founderror.
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\Contactsnamespace
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
usethe
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,
useimports
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
Receiptin
this class, it'll assume you're pointing to the imported class.
Aliasing #
But, what if you also have a Receiptclass
in your current namespace? What if your class needs access to both
Karani\Contacts\Receiptand
Karani\Billing\Receipt?
You can't just import the
Karani\Billing\Receiptclass,
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
usestatement
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
BillingReceiptthroughout
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 Karaninamespace
to live in my
srcfolder.
Here's my folder structure for a generic, framework-independent project:
app public src Billing Contacts vendor
[/code]
As you can see, the
srcfolder
represents the
Karanitop
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.jsonand
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
usestatement
at the top of your file.
相关文章推荐
- PHP中的魔术方法和魔术常量
- 系统吞吐量、TPS(QPS)、用户并发量、性能测试概念和公式
- PHP命令行下的世界
- Yii查询生成器(Query Builder)用法实例教程
- vsftpd.conf 详解与实例配置
- YII-1.16增加注册模块
- 使用php搭建自己的MVC框架
- php socket 处理不过来数据流,该如何避免(好像是阻塞了)
- 基于CakePHP实现的简单博客系统实例
- php namespace 和composer
- 【翻译自nikic大神】PHP中原生类型的方法
- 转载 PHP学习笔记 - 在Eclipse中使用XDebug调试代码 | Using XDebug debug code in eclipse
- 探讨PHP simplexml详细使用方法
- php页面静态化—静态化详细介绍
- php页面静态化—优化页面响应时间
- 解决安装laravel/homestead vagrant环境报"A VirtualBox machine with the name 'homestead' already exists."的错误
- 基于CakePHP实现的简单博客系统实例
- Yii2.0 框架下载
- 使用php模拟post的几种方法
- php工作经验谈-1