• PHP面向对象3


    1. namespace:

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

    复制代码
    <?php
    //in Test2.php
    namespace nstest	est2;
    
    class Test2 {
        public static function printMe() {
            print 'This is nstest	est2Test2::printSelf.'."
    ";
        }
    }
    
    <?php
    //in Test1.php
    namespace nstest	est1;
    
    class Test1 {
        public static function printMe() {
            print 'This is nstest	est1Test1::printSelf.'."
    ";
        }
    }
    require "Test2.php";
    nstest	est2Test2::printMe();
    复制代码

        运行结果如下:

    bogon:TestPhp$ php Test1.php 
    PHP Fatal error:  Class 'nstest	est1
    stest	est2Test2' not found in /Users/liulei/PhpstormProjects/TestPhp/Test1.php on line 13

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

    复制代码
    <?php
    //Test2.php
    namespace nstest	est2;
    
    class Test2 {
        public static function printMe() {
            print 'This is nstest	est2Test2::printSelf.'."
    ";
        }
    }
    
    <?php
    //Test1.php
    namespace nstest	est1;
    
    class Test1 {
        public static function printMe() {
            print 'This is nstest	est1Test1::printSelf.'."
    ";
        }
    }
    require "Test2.php";
    
    stest	est2Test2::printMe();
    复制代码

        运行结果如下:

    bogon:TestPhp$ php Test1.php 
    This is nstest	est2Test2::printSelf.

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

    复制代码
    <?php
    //Test2.php
    namespace nstest	est2;
    
    class Test2 {
        public static function printMe() {
            print 'This is nstest	est2Test2::printSelf.'."
    ";
        }
    }
    
    <?php
    //Test1.php
    namespace nstest;
    
    class Test1 {
        public static function printMe() {
            print 'This is nstest	est1Test1::printSelf.'."
    ";
        }
    }
    
    require "Test2.php";
    test2Test2::printMe(); 
    复制代码

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

    复制代码
    <?php
    //Test2.php
    namespace nstest	est2;
    
    class Test2 {
        public static function printMe() {
            print 'This is nstest	est2Test2::printSelf.'."
    ";
        }
    }
    
    <?php
    //Test1.php
    namespace nstest	est1;
    
    class Test1 {
        public static function printMe() {
            print 'This is nstest	est1Test1::printSelf.'."
    ";
        }
    }
    
    require "Test2.php";
    //这里需要特别注意的是,nstest	est2已经表示名字空间绝对路径定位,不需要再加前导斜杠()了。
    //另外这里还有一个隐式规则是test2表示该名字空间的缺省别名,在引用其名字空间内的对象时需要加test2前缀。
    use nstest	est2;
    test2Test2::printMe();
    
    //这里我们也可以给名字空间显式的指定别名,如:
    use nstest	est2 as test2_alias;
    test2_aliasTest2::printMe(); 
    复制代码

        运行结果如下:

    bogon:TestPhp$ php Test1.php 
    This is nstest	est2Test2::printSelf.
    This is nstest	est2Test2::printSelf.

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

    复制代码
    <?php
    class Test {
        public static function printMe() {
            print 'This is Global namespace Test::printSelf.'."
    ";
        }
    }
    
    //下面两行代码表示的是同一对象,即全局名字空间下的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.
    ";
        }
    }
    
    function classInfo(ReflectionClass $c) {
        $details = "";
        //getName将返回实际的类名。
        $name = $c->getName();
        if ($c->isUserDefined()) {
            $details .= "$name is user defined.
    ";
        }
        if ($c->isInternal()) {
            $details .= "$name is built-in.
    ";
        }
        if ($c->isAbstract()) {
            $details .= "$name is abstract class.
    ";
        }
        if ($c->isFinal()) {
            $details .= "$name is final class.
    ";
        }
        if ($c->isInstantiable()) {
            $details .= "$name can be instantiated.
    ";
        } else {
            $details .= "$name cannot be instantiated.
    ";
        }
        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.
    ";
    print classInfo(new ReflectionClass('TestClass'));
    
    print "
    The following is Class Source.
    ";
    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.
    ";
        }
    }
    复制代码

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

    复制代码
    <?php
    class TestClass {
        public $publicVariable;
    
        function __construct() {
    
        }
        private function privateMethod() {
    
        }
        function publicMethod() {
            print "This is publicMethod.
    ";
        }
        function publicMethod2(string $arg1, int $arg2) {
    
        }
    }
    
    //这个函数中使用的ReflectionMethod中的方法都是非常简单直观的,就不再过多赘述了。
    function methodInfo(ReflectionMethod $m) {
        $name = $m->getName();
        $details = "";
        if ($m->isUserDefined()) {
            $details .= "$name is user defined.
    ";
        }
        if ($m->isInternal()) {
            $details .= "$name is built-in.
    ";
        }
        if ($m->isAbstract()) {
            $details .= "$name is abstract.
    ";
        }
        if ($m->isPublic()) {
            $details .= "$name is public.
    ";
        }
        if ($m->isProtected()) {
            $details .= "$name is protected.
    ";
        }
        if ($m->isPrivate()) {
            $details .= "$name is private.
    ";
        }
        if ($m->isStatic()) {
            $details .= "$name is static.
    ";
        }
        if ($m->isFinal()) {
            $details .= "$name is final.
    ";
        }
        if ($m->isConstructor()) {
            $details .= "$name is constructor.
    ";
        }
        if ($m->returnsReference()) {
            $details .= "$name returns a reference.
    ";
        }
        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.
    ";
    foreach ($methods as $method) {
        print methodInfo($method);
        print "
    --------------------
    ";
    }
    
    print "The following is Method[TestClass::publicMethod] source.
    ";
    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.
    ";
        }
    复制代码

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

    复制代码
    <?php
    class ParamClass {
    
    }
    
    class TestClass {
        function publicMethod() {
            print "This is publicMethod.
    ";
        }
        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.
    ";
        if (!empty($class)) {
            $classname = $class->getName();
            $details .= "$$name must be a $classname object
    ";
        }
        if ($p->isPassedByReference()) {
            $details .= "$$name is passed by reference.
    ";
        }
        if ($p->isDefaultValueAvailable()) {
            $def = $p->getDefaultValue();
            $details .= "$$name has default: $def
    ";
        }
        return $details;
    }
    
    $rc = new ReflectionClass('TestClass');
    $method = $rc->getMethod('publicMethod2');
    $params = $method->getParameters();
    
    foreach ($params as $p) {
        print paramInfo($p)."
    ";
    }
    复制代码

        运行结果如下:

    复制代码
    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."
    ";
        }
    
        function publicMethod2($arg1, $arg2) {
            print '$arg1 = '.$arg1.' $arg2 = '.$arg2."
    ";
        }
    }
    
    $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
  • 相关阅读:
    .NET中TreeView控件从数据库获取数据源
    .NET中GridView控件的全选删除
    TreeView无限极分类绑定(从数据库读取数据源)
    .NET中GridView代码更改列名
    .NET读写cookie方法
    .NET中GridView控件的高亮显示和删除前弹框提示
    Repeater控件的多层嵌套,DataList控件的多层嵌套
    .NET一些常用的语句集合(不断更新中)
    解决IE5、IE6、IE7与W3C标准的冲突,使用(IE7.js IE8.js)兼容
    kindeditor富文本编辑器ASP.NET源码下载
  • 原文地址:https://www.cnblogs.com/fyy-888/p/5440317.html
Copyright © 2020-2023  润新知