面向对象namespace

1. namespace:

和C++中的名字空间很像,作用也一样,都是为了避免在引用较多第三方库时而带来的名字冲突问题。通过名字空间,即便两个class的名称相同,但是因为位于不同的名字空间内,他们仍然可以被精确定位和区分。第一次看到PHP的名字空间语法时,感觉和C++相比在语法上是非常非常相似的,然而在写点儿小例子做做实验的时候才发现,他们的差别还是很大的,为了避免以后忘记,所以这里特别将其记录了下来。见如下代码:

<?php
//in Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print ‘This is nstest\test2\Test2::printSelf.‘."\n";
    }
}

<?php
//in Test1.php
namespace nstest\test1;

class Test1 {
    public static function printMe() {
        print ‘This is nstest\test1\Test1::printSelf.‘."\n";
    }
}
require "Test2.php";
nstest\test2\Test2::printMe();

运行结果如下:

bogon:TestPhp$ php Test1.php
PHP Fatal error:  Class ‘nstest\test1\nstest\test2\Test2‘ not found in /Users/liulei/PhpstormProjects/TestPhp/Test1.php on line 13

是不是这个结果比较出乎意料,原因在哪呢?HOHO,原来PHP在进行名字空间引用的时候,如果名字空间的第一个字符不是前导斜杠(\),那么就被自动识别为相对名字空间,在上面的代码中,Test1自身所在的名字空间是namespace nstest\test1,因此在以nstest\test2\Test2::printMe()方式调用Test2::printMe()时,PHP将自动解析为nstest\test1\nstest\test2\Test2::printMe(),即认为nstest\test2是在当前名字空间内部的。修正该问题非常简单,只需在引用时加上前导斜杠(\)即可,见以下修复后的代码:

<?php
//Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print ‘This is nstest\test2\Test2::printSelf.‘."\n";
    }
}

<?php
//Test1.php
namespace nstest\test1;

class Test1 {
    public static function printMe() {
        print ‘This is nstest\test1\Test1::printSelf.‘."\n";
    }
}
require "Test2.php";
\nstest\test2\Test2::printMe();

运行结果如下:

bogon:TestPhp$ php Test1.php
This is nstest\test2\Test2::printSelf.

还有一种改动方式,可以示意一下PHP中名字空间中的相对引用。这里我们可以将Test1的名字空间改为namespace nstest,其他的修改见以下代码中红色高亮部分:

<?php
//Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print ‘This is nstest\test2\Test2::printSelf.‘."\n";
    }
}

<?php
//Test1.php
namespace nstest;

class Test1 {
    public static function printMe() {
        print ‘This is nstest\test1\Test1::printSelf.‘."\n";
    }
}

require "Test2.php";
test2\Test2::printMe(); 

运行结果等于上面正确的结果。最重要的差别就是该例使用了PHP名字空间中的相对定位。相信熟悉C++的开发者一定会想到use关键字,PHP也提供了该关键字,他们的功能是一致的,都是为了避免在后面的代码中,无需再通过全限定符(类名前加名字空间前缀)来引用其他名字空间中的类了。至于具体的语法规则,还是看看下面具体的代码和关键性注释吧。

<?php
//Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print ‘This is nstest\test2\Test2::printSelf.‘."\n";
    }
}

<?php
//Test1.php
namespace nstest\test1;

class Test1 {
    public static function printMe() {
        print ‘This is nstest\test1\Test1::printSelf.‘."\n";
    }
}

require "Test2.php";
//这里需要特别注意的是,nstest\test2已经表示名字空间绝对路径定位,不需要再加前导斜杠(\)了。
//另外这里还有一个隐式规则是test2表示该名字空间的缺省别名,在引用其名字空间内的对象时需要加test2前缀。
use nstest\test2;
test2\Test2::printMe();

//这里我们也可以给名字空间显式的指定别名,如:
use nstest\test2 as test2_alias;
test2_alias\Test2::printMe(); 

运行结果如下:

bogon:TestPhp$ php Test1.php
This is nstest\test2\Test2::printSelf.
This is nstest\test2\Test2::printSelf.

最后介绍一下PHP中全局名字空间的引用方式,见如下代码和关键性注释:

<?php
class Test {
    public static function printMe() {
        print ‘This is Global namespace Test::printSelf.‘."\n";
    }
}

//下面两行代码表示的是同一对象,即全局名字空间下的Test类,然而如果因为名字空间冲突导致第一种方式不能被PHP
//编译器正常识别,那么就可以使用第二种方式显式的通知PHP,自己要引用的是全局名字空间中的Test类。
Test::printMe();
\Test::printMe();

运行结果如下:

bogon:TestPhp$ php Test1.php
This is Global namespace Test::printSelf.
This is Global namespace Test::printSelf.

2. Reflection:

PHP中的反射和Java中java.lang.reflect包提供的功能一样,更有意思的是,就连很多方法命名和调用方式也是非常雷同的。他们都是由一些列可以分析类、类方法和方法参数的PHP内置类组成。我们这里主要介绍的是如下几个常用的内置类:(Reflection、RelectionClass、ReflectionMethod、ReflectionParameter和ReflectionProperty)。现在我们还是一步一步来理解,即从ReflectionClass开始给出示例代码和关键性注释:

<?php
class TestClass {
    public $publicVariable;

    function publicMethod() {
        print "This is publicMethod.\n";
    }
}

function classInfo(ReflectionClass $c) {
    $details = "";
    //getName将返回实际的类名。
    $name = $c->getName();
    if ($c->isUserDefined()) {
        $details .= "$name is user defined.\n";
    }
    if ($c->isInternal()) {
        $details .= "$name is built-in.\n";
    }
    if ($c->isAbstract()) {
        $details .= "$name is abstract class.\n";
    }
    if ($c->isFinal()) {
        $details .= "$name is final class.\n";
    }
    if ($c->isInstantiable()) {
        $details .= "$name can be instantiated.\n";
    } else {
        $details .= "$name cannot be instantiated.\n";
    }
    return $details;
}

function classSource(ReflectionClass $c) {
    $path = $c->getFileName();
    $lines = @file($path);
    //获取类定义代码的起始行和截至行。
    $from = $c->getStartLine();
    $to = $c->getEndLine();
    $len = $to - $from + 1;
    return implode(array_slice($lines,$from - 1,$len));
}

print "The following is Class Information.\n";
print classInfo(new ReflectionClass(‘TestClass‘));

print "\nThe following is Class Source.\n";
print classSource(new ReflectionClass(‘TestClass‘));

运行结果如下:

bogon:TestPhp$ php reflection_test.php
The following is Class Information.
TestClass is user defined.
TestClass can be instantiated.

The following is Class Source.
class TestClass {
    public $publicVariable;

    function publicMethod() {
        print "This is publicMethod.\n";
    }
}

下面让我们仍然以代码示例和关键性注释的方法继续ReflectionMethod的学习之旅。

<?php
class TestClass {
    public $publicVariable;

    function __construct() {

    }
    private function privateMethod() {

    }
    function publicMethod() {
        print "This is publicMethod.\n";
    }
    function publicMethod2(string $arg1, int $arg2) {

    }
}

//这个函数中使用的ReflectionMethod中的方法都是非常简单直观的,就不再过多赘述了。
function methodInfo(ReflectionMethod $m) {
    $name = $m->getName();
    $details = "";
    if ($m->isUserDefined()) {
        $details .= "$name is user defined.\n";
    }
    if ($m->isInternal()) {
        $details .= "$name is built-in.\n";
    }
    if ($m->isAbstract()) {
        $details .= "$name is abstract.\n";
    }
    if ($m->isPublic()) {
        $details .= "$name is public.\n";
    }
    if ($m->isProtected()) {
        $details .= "$name is protected.\n";
    }
    if ($m->isPrivate()) {
        $details .= "$name is private.\n";
    }
    if ($m->isStatic()) {
        $details .= "$name is static.\n";
    }
    if ($m->isFinal()) {
        $details .= "$name is final.\n";
    }
    if ($m->isConstructor()) {
        $details .= "$name is constructor.\n";
    }
    if ($m->returnsReference()) {
        $details .= "$name returns a reference.\n";
    }
    return $details;
}

function methodSource(ReflectionMethod $m) {
    $path = $m->getFileName();
    $lines = @file($path);
    $from = $m->getStartLine();
    $to = $m->getEndLine();
    $len = $to - $from + 1;
    return implode(array_slice($lines, $from - 1, $len));
}

$rc = new ReflectionClass(‘TestClass‘);
$methods = $rc->getMethods();
print "The following is method information.\n";
foreach ($methods as $method) {
    print methodInfo($method);
    print "\n--------------------\n";
}

print "The following is Method[TestClass::publicMethod] source.\n";
print methodSource($rc->getMethod(‘publicMethod‘));

运行结果如下:

bogon:TestPhp$ php reflection_test.php
The following is method information.
__construct is user defined.
__construct is public.
__construct is constructor.

--------------------
privateMethod is user defined.
privateMethod is private.

--------------------
publicMethod is user defined.
publicMethod is public.

--------------------
publicMethod2 is user defined.
publicMethod2 is public.

--------------------
The following is Method[TestClass::publicMethod] source.
    function publicMethod() {
        print "This is publicMethod.\n";
    }

让我们继续ReflectionParameter吧,他表示的是成员函数的参数信息。继续看代码吧。

<?php
class ParamClass {

}

class TestClass {
    function publicMethod() {
        print "This is publicMethod.\n";
    }
    function publicMethod2(ParamClass $arg1, &$arg2, $arg3 = null) {

    }
}

function paramInfo(ReflectionParameter $p) {
    $details = "";
    //这里的$declaringClass将等于TestClass。
    $declaringClass = $p->getDeclaringClass();
    $name = $p->getName();
    $class = $p->getClass();
    $position = $p->getPosition();
    $details .= "\$$name has position $position.\n";
    if (!empty($class)) {
        $classname = $class->getName();
        $details .= "\$$name must be a $classname object\n";
    }
    if ($p->isPassedByReference()) {
        $details .= "\$$name is passed by reference.\n";
    }
    if ($p->isDefaultValueAvailable()) {
        $def = $p->getDefaultValue();
        $details .= "\$$name has default: $def\n";
    }
    return $details;
}

$rc = new ReflectionClass(‘TestClass‘);
$method = $rc->getMethod(‘publicMethod2‘);
$params = $method->getParameters();

foreach ($params as $p) {
    print paramInfo($p)."\n";
}

运行结果如下:

bogon:TestPhp$ php reflection_test.php
$arg1 has position 0.
$arg1 must be a ParamClass object

$arg2 has position 1.
$arg2 is passed by reference.

$arg3 has position 2.
$arg3 has default: 

上面介绍的都是通过PHP提供的Reflection API来遍历任意class的具体信息,事实上和Java等其他语言提供的反射功能一样,PHP也同样支持通过反射类调用实际对象的方法,这里将主要应用到两个方法,分别是ReflectionClass::newInstance()来创建对象实例,另一个是ReflectionMethod::invoke(),根据对象实例和方法名执行该方法。见如下代码:

<?php
class TestClass {
    private $privateArg;
    function __construct($arg) {
        $this->privateArg = $arg;
    }
    function publicMethod() {
        print ‘$privateArg = ‘.$this->privateArg."\n";
    }

    function publicMethod2($arg1, $arg2) {
        print ‘$arg1 = ‘.$arg1.‘ $arg2 = ‘.$arg2."\n";
    }
}

$rc = new ReflectionClass(‘TestClass‘);
$testObj = $rc->newInstanceArgs(array(‘This is private argument.‘));
$method = $rc->getMethod(‘publicMethod‘);
$method->invoke($testObj);

$method2 = $rc->getMethod(‘publicMethod2‘);
$method2->invoke($testObj,"hello","world");

运行结果如下:

bogon:TestPhp$ php reflection_test.php
$privateArg = This is private argument.
$arg1 = hello $arg2 = world

事实上ReflectionClass、ReflectionMethod和ReflectionParameter提供给我们的可用方法还有更多,这里只是给出几个最典型的方法,以便我们可以更为直观的学习和了解PHP Reflection API。相信再看完以后的代码示例之后,我们都会比较清楚,如果今后需要用到和class相关的功能,就从ReflectionClass中查找,而member function的信息则一定来自于ReflectionMethod,方法参数信息来自于ReflectionParameter。

注:该Blog中记录的知识点,是在我学习PHP的过程中,遇到的一些PHP和其他面向对象语言相比比较独特的地方,或者是对我本人而言确实需要簿记下来以备后查的知识点。虽然谈不上什么深度,但是还是希望能与大家分享。

时间: 2024-10-14 22:15:10

面向对象namespace的相关文章

2、C#面向对象:封装、继承、多态、String、集合、文件(上)

面向对象封装 一.面向对象概念 面向过程:面向的是完成一件事情的过程,强调的是完成这件事情的动作. 面向对象:找个对象帮你完成这件事情. 二.面向对象封装 把方法进行封装,隐藏实现细节,外部直接调用. 打包,便于管理,为了解决大型项目的维护与管理. 三.什么是类? 将相同的属性和相同方法的对象进行封装,抽象出 “类”,用来确定对象具有的属性和方法. 类.对象关系:人是类,张三是人类的对象. 类是抽象的,对象是具体的.对象可以叫做类的实例,类是不站内存的,对象才占内存. 字段是类的状态,方法是类执

C++ Primer 学习笔记_74_面向对象编程 --再谈文本查询示例[续/习题]

面向对象编程 --再谈文本查询示例[续/习题] //P522 习题15.41 //1 in TextQuery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <set> #include <map&g

PHP系列(四)PHP面向对象程序设计

php面向对象程序设计 面向对象的优势:可读性.可重用性.稳定性.维护性.可测试性 简单格式: [修饰符]class 类名{ [成员属性] [成员方法] } 完整格式: [修饰符]class 类名 [extends 父类]  [implements 接口 ] { [成员属性] [成员方法] } 成员方法格式: [修饰符] function 方法名(参数){ [方法体] [return返回值] } 修饰符:public  protected  private static  abstract  f

设计模式2 面向对象设计原则

面向对象设计原则  原则的目的 面向对象设计原创表  单一职责原则案例 开闭原则 案例 依赖倒转原则 案例 面向对象设计原则  对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一.在面向对象设计中,可维护性的复用是以设计原则为基础的.每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平.  面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含

C++面向对象多线程入门

第1节   背景 为了更好的理解多线程的概念,先对进程,线程的概念背景做一下简单介绍. 早期的计算机系统都只允许一个程序独占系统资源,一次只能执行一个程序.在大型机年代,计算能力是一种宝贵资源.对于资源拥有方来说,最好的生财之道自然是将同一资源同时租售给尽可能多的用户.最理想的情况是垄断全球计算市场.所以不难理解为何当年IBM预测“全球只要有4台计算机就够了”. 这种背景下,一个计算机能够支持多个程序并发执行的需求变得十分迫切.由此产生了进程的概念.进程在多数早期多任务操作系统中是执行工作的基本

算法也是很过瘾的~~用面向对象实现~夜过吊桥~算法

问题描述 1.五个人打算过一座吊桥,开始时他们都位于该桥的一侧. 2.天很黑,五个人手里只有一个手电筒. 3.该桥一次最多只能同时过两个人,无论是一个人还是两个人过桥,都需要携带手电筒看路.而且手电筒只能通过人携带过桥的方式传递. 4.第一个人过桥需要1分钟时间,第二个人过桥需要2分钟,第三个人需要5分钟,第四个需要7分钟,第五个需要10分钟.由于速度不同,两个人一起过桥的话,速度以慢的人为准. 问题:求最快过桥时间.要求写出求解的算法. 分析题目 1.从左边到右边,需要有一个人拿着手电筒,到达

面向对象 类 练习

添加5个学生的信息到集合中,每个学生都有:学号,姓名,成绩,3个内容,添加完毕后将学生的分数从高到低排列并打印出来,用添加类的项目 和结构体一样,只是在项目中有添加了一个类的项目,用来定义变量,方便其他项目调用 作为一个单独的类,直接在class后面添加类名,写法同样用public + 数值类型+变量名,后面调用时需要实例化一下 如:类名  一个变量=new 类名(),因为类属于使用方法,需要加括号,将类中的所有属性赋值到这个变量中,用这个变量 可以直接点出里面的属性使用 1.在项目中添加一个类

面向对象 -教案

面向对象思想:万物皆对象 面向对象三大特性:封装:        封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就    是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员. 简介 在面向对象编程中,封装(encapsulation)是将对象运行所需的资源封装在程序对象中——基本上,是方法和数据.对象是“公布其接口”.其他附加到这些接口上的对象不需要    关心对

PHP中的命名空间(namespace)及其使用详解

PHP中的命名空间(namespace)及其使用详解 晶晶 2年前 (2014-01-02) 8495次浏览 PHP php自5.3.0开始,引入了一个namespace关键字以及__NAMESPACE__魔术常量(当然use关键字或use as嵌套语句也同时引入):那么什么是命名空间呢?php官网已很明确的进行了定义并形象化解释,这里直接从php官网copy一段文字[来源]. “什么是命名空间?从广义上来说,命名空间是一种封装事物的方法.在很多地方都可以见到这种抽象概念.例如,在操作系统中目录