摘要:说明中经常使用的反射特性来设计代码,本文主要学习的反射特性,来提高写代码时的设计质量。提供一套检测的两个工具包和,类似于探针一样的东西来探测这些一等公民。限于篇幅,下篇再聊下反射。
说明:Laravel中经常使用PHP的反射特性来设计代码,本文主要学习PHP的反射特性,来提高写代码时的设计质量。PHP提供一套检测class, interface, trait, property, method的两个工具包:Introspection Functions和Reflection API,类似于探针一样的东西来探测这些一等公民。本文先看下Introspection Functions的使用。
开发环境: Laravel5.3 + PHP7
Introspection FunctionsIntrospection Functions是用来操作object class的一些函数,PHP提供了大量的Introspection Functions来操作class, interface, trait, method, property:
class_exists()
interface_exists()
method_exists()
property_exists()
trait_exists()
class_alias()
get_class()
get_parent_class()
get_called_class()
get_class_methods()
get_class_vars()
get_object_vars()
is_subclass_of()
is_a()
class_exists()Laravel源码中好多个地方使用到class_exists()方法来判断指定类是否存在,如IlluminateDatabaseConnection::isDoctrineAvailable()的源码:
public function isDoctrineAvailable() { return class_exists("DoctrineDBALConnection"); // DoctrineDBALConnection::class类是否存在,大小写不敏感 }
写个PHPUnit测试下(爆绿灯,说明是正确的,这里不截图了。后面所有Introspection的测试都放在IntrospectionTest这个单元测试里):
namespace MyRightCapitalContainerTests; class IntrospectionTest extends PHPUnit_Framework_TestCase { public function testClassExists() { // Arrange // Actual $class_exists = class_exists(TestClassExists::class); // Assert $this->assertTrue($class_exists); } } class TestClassExists { }interface_exists()
interface_exists()是用来检查接口是否存在,写个PHPUnit测试下,爆绿灯:
namespace MyRightCapitalContainerTests; class IntrospectionTest extends PHPUnit_Framework_TestCase { public function testInterfaceExists() { // Arrange // Actual $interface_exists = interface_exists(TestInterfaceExists::class); // Assert $this->assertTrue($interface_exists); } } interface TestInterfaceExists { }method_exists()
检查类的方法(private,protected,public)是否存在于指定的类对象或类名中,Laravel中很多处用到了这个函数,如Application中的register()检查service provider中register是否存在,和bootProvider()中检查service provider中boot()方法是否存在:
public function register($provider, $options = [], $force = false) { ... if (method_exists($provider, "register")) { $provider->register(); } ... } protected function bootProvider(ServiceProvider $provider) { if (method_exists($provider, "boot")) { return $this->call([$provider, "boot"]); } }
这里写个PHPUnit测试下,爆绿灯:
public function testMethodExists() { // Arrange $test_class_exists = new TestClassExists(); // Actual $object_method_exists1 = method_exists($test_class_exists, "testPrivateMethodExists"); $object_method_exists2 = method_exists($test_class_exists, "testProtectedMethodExists"); $object_method_exists3 = method_exists($test_class_exists, "testPublicMethodExists"); $classname_method_exists1 = method_exists(TestClassExists::class, "testPrivateMethodExists"); $classname_method_exists2 = method_exists(TestClassExists::class, "testProtectedMethodExists"); $classname_method_exists3 = method_exists(TestClassExists::class, "testPublicMethodExists"); // Assert $this->assertTrue($object_method_exists1); $this->assertTrue($object_method_exists2); $this->assertTrue($object_method_exists3); $this->assertTrue($classname_method_exists1); $this->assertTrue($classname_method_exists2); $this->assertTrue($classname_method_exists3); } class TestClassExists { private function testPrivateMethodExists() { } protected function testProtectedMethodExists() { } public function testPublicMethodExists() { } }property_exists()
检查该属性(private, protected, public)是否存在于类对象或类名中,Laravel很多地方用到了该函数,如IlluminateFoundationAuthRedirectsUsers::redirectPath()源码:
public function redirectPath() { return property_exists($this, "redirectTo") ? $this->redirectTo : "/home"; }
写个PHPUnit测试下该函数,爆绿灯:
// class IntrospectionTest public function testPropertyExists() { // Arrange $test_class_exists = new TestClassExists(); // Actual $private_property1 = property_exists($test_class_exists, "testPrivatePropertyExists"); $private_property2 = property_exists(TestClassExists::class, "testPrivatePropertyExists"); $protected_property1 = property_exists($test_class_exists, "testProtectedPropertyExists"); $protected_property2 = property_exists(TestClassExists::class, "testProtectedPropertyExists"); $public_property1 = property_exists($test_class_exists, "testPublicPropertyExists"); $public_property2 = property_exists(TestClassExists::class, "testPublicPropertyExists"); // Assert $this->assertTrue($private_property1); $this->assertTrue($private_property2); $this->assertTrue($protected_property1); $this->assertTrue($protected_property2); $this->assertTrue($public_property1); $this->assertTrue($public_property2); } class TestClassExists { private $testPrivatePropertyExists; protected $testProtectedPropertyExists; public $testPublicPropertyExists; }trait_exists()
检查trait是否存在,写下PHPUnit测试,爆绿灯:
// class IntrospectionTest public function testTraitExists() { // Arrange // Actual $test_trait_exists = trait_exists(TestTraitExists::class); // Assert $this->assertTrue($test_trait_exists); } trait TestTraitExists { }class_alias()
给指定类取别名,Laravel中只有一处使用了class_alias(),用来给config/app.php中$aliases[ ]注册别名,可看下Laravel学习笔记之bootstrap源码解析,看下Laravel中如何使用的:
public function load($alias) { if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } }
写个PHPUnit测试,爆绿灯:
public function testClassAlias() { // Arrange class_alias(TestClassExists::class, "MyRightCapitalContainerTestsAliasTestClassExists"); $test_class_exists = new TestClassExists(); // Actual $actual = new AliasTestClassExists(); //Assert $this->assertInstanceOf(TestClassExists::class, $actual); $this->assertInstanceOf(AliasTestClassExists::class, $test_class_exists); }get_class()
get_class()获取对象的类名,这个函数在Laravel中大量地方在用了,如Application::getProvider($provider)方法,是个很好用的方法:
public function getProvider($provider) { $name = is_string($provider) ? $provider : get_class($provider); return Arr::first($this->serviceProviders, function ($value) use ($name) { return $value instanceof $name; }); }
写个PHPUnit测试,爆绿灯:
public function testGetClass() { // Arrange $test_class_exists = new TestClassExists(); // Actual $class_name = get_class($test_class_exists); // Assert $this->assertSame(TestClassExists::class, $class_name); }get_parent_class()
get_parent_class()是用来获取类的父类名,目前Laravel中还没用到这个函数,传入的可以是子类对象或者子类名,写个PHPUnit测试下:
// namespace MyRightCapitalContainerTests; // class IntrospectionTest extends PHPUnit_Framework_TestCase public function testGetParentClass() { // Arrange $child_class = new ChildClass(); // Actual $parent_class1 = get_parent_class($child_class); $parent_class2 = get_parent_class(ChildClass::class); // Assert $this->assertSame(ParentClass::class, $parent_class1); $this->assertSame(ParentClass::class, $parent_class2); } class ChildClass extends ParentClass { } class ParentClass { }get_called_class()
get_called_class()获取后期静态绑定类即实际调用类的名称,Laravel中还没使用到该函数,不妨写个测试看下如何使用:
// namespace MyRightCapitalContainerTests; // class IntrospectionTest extends PHPUnit_Framework_TestCase public function testGetCalledClass() { // Arrange $child_class = new ChildClass(); $parent_class = new ParentClass(); // Actual $child_called_class = $child_class->testGetCalledClass(); $parent_called_class = $parent_class->testGetCalledClass(); // Assert $this->assertSame(ChildClass::class, $child_called_class); $this->assertSame(ParentClass::class, $parent_called_class); } class ChildClass extends ParentClass { } class ParentClass { public function testGetCalledClass() { return get_called_class(); } }get_class_methods()
get_class_methods()用来获取类的方法名组成一个数组(测试只能是public),Laravel只有一处用到了该方法IlluminateDatabaseEloquentModel::cacheMutatedAttributes() :line 3397,这里写个PHPUnit测试,爆绿灯:
public function testGetClassMethod() { // Arrange $get_class_methods1 = get_class_methods(ChildClass::class); $get_class_methods2 = get_class_methods(new ChildClass()); // Actual // Assert $this->assertFalse(in_array("testPrivateGetClassMethod", $get_class_methods1, true)); $this->assertFalse(in_array("testPrivateGetClassMethod", $get_class_methods2, true)); $this->assertFalse(in_array("testProtectedGetClassMethod", $get_class_methods1, true)); $this->assertFalse(in_array("testProtectedGetClassMethod", $get_class_methods2, true)); $this->assertTrue(in_array("testPublicGetClassMethod", $get_class_methods1, true)); $this->assertTrue(in_array("testPublicGetClassMethod", $get_class_methods2, true)); $this->assertTrue(in_array("testGetCalledClass", $get_class_methods1, true)); $this->assertTrue(in_array("testGetCalledClass", $get_class_methods2, true)); } class ChildClass extends ParentClass { private function testPrivateGetClassMethod() { } protected function testProtectedGetClassMethod() { } public function testPublicGetClassMethod() { } }get_class_vars()
get_class_vars()只会读取类的public属性组成一个数组,类似于get_class_methods(),若属性没有默认值就为null,目前Laravel中还未使用,看下PHPUnit测试:
public function testGetClassVars() { // Arrange // Actual $class_vars = get_class_vars(ChildClass::class); // Assert $this->assertArrayNotHasKey("privateNoDefaultVar", $class_vars); $this->assertArrayNotHasKey("privateDefaultVar", $class_vars); $this->assertArrayNotHasKey("protectedNoDefaultVar", $class_vars); $this->assertArrayNotHasKey("protectedDefaultVar", $class_vars); $this->assertEmpty($class_vars["publicNoDefaultVar"]); $this->assertEquals("public_laravel", $class_vars["publicDefaultVar"]); } class ChildClass extends ParentClass { private $privateNoDefaultVar; private $privateDefaultVar = "private_laravel"; protected $protectedNoDefaultVar; protected $protectedDefaultVar = "protected_laravel"; public $publicNoDefaultVar; public $publicDefaultVar = "public_laravel"; }get_object_vars()
get_object_vars()只会读取对象的public属性组成一个数组,类似于get_class_vars(), get_class_methods(),且属性没有默认值就是null,Laravel中只有一处使用到IlluminateMailJobsHandleQueuedMessage::__sleep() :line 78,写个PHPUnit测试下,爆绿灯:
public function testGetObjectVars() { // Arrange $get_object_vars = new TestGetObjectVars(1, 2, 3); // Actual $object_vars = get_object_vars($get_object_vars); // Assert $this->assertArrayNotHasKey("x", $object_vars); $this->assertArrayNotHasKey("y", $object_vars); $this->assertEquals(3, $object_vars["z"]); $this->assertArrayNotHasKey("dot1", $object_vars); $this->assertArrayNotHasKey("dot2", $object_vars); $this->assertArrayNotHasKey("circle1", $object_vars); $this->assertArrayNotHasKey("circle2", $object_vars); $this->assertEquals(10, $object_vars["line1"]); $this->assertEmpty($object_vars["line2"]); } class TestGetObjectVars { private $x; protected $y; public $z; private $dot1 = 10; private $dot2; protected $circle1 = 20; protected $circle2; public $line1 = 10; public $line2; public function __construct($x, $y, $z) { $this->x = $x; $this->y = $y; $this->z = $z; } }is_subclass_of()
is_subclass_of()用来判断给定类对象是否是另一给定类名的子类,Laravel中有用到,这里写下PHPUnit测试,爆绿灯:
public function testIsSubclassOf() { // Arrange $child_class = new ChildClass(); // Actual $is_subclass = is_subclass_of($child_class, ParentClass::class); // Assert $this->assertTrue($is_subclass); }is_a()
is_a()用来判定给定类对象是否是另一给定类名的对象或是子类,和is_subclass_of()有点类似,只是is_a()还可以判定是不是该类的对象,is_a()类似于instanceof操作符,Laravel中还没用到这个方法,这里写个PHPUnit测试,爆绿灯:
public function testIsA() { // Arrange $child_class = new ChildClass(); // Actual $is_object = is_a($child_class, ChildClass::class); $is_subclass = is_a($child_class, ParentClass::class); // Assert $this->assertTrue($is_object); $this->assertTrue($is_subclass); }
最后,给下整个PHPUnit的测试代码:
assertTrue($class_exists); } public function testInterfaceExists() { // Arrange // Actual $interface_exists = interface_exists(TestInterfaceExists::class); // Assert $this->assertTrue($interface_exists); } public function testMethodExists() { // Arrange $test_class_exists = new TestClassExists(); // Actual $object_method_exists1 = method_exists($test_class_exists, "testPrivateMethodExists"); $object_method_exists2 = method_exists($test_class_exists, "testProtectedMethodExists"); $object_method_exists3 = method_exists($test_class_exists, "testPublicMethodExists"); $classname_method_exists1 = method_exists(TestClassExists::class, "testPrivateMethodExists"); $classname_method_exists2 = method_exists(TestClassExists::class, "testProtectedMethodExists"); $classname_method_exists3 = method_exists(TestClassExists::class, "testPublicMethodExists"); // Assert $this->assertTrue($object_method_exists1); $this->assertTrue($object_method_exists2); $this->assertTrue($object_method_exists3); $this->assertTrue($classname_method_exists1); $this->assertTrue($classname_method_exists2); $this->assertTrue($classname_method_exists3); } public function testPropertyExists() { // Arrange $test_class_exists = new TestClassExists(); // Actual $private_property1 = property_exists($test_class_exists, "testPrivatePropertyExists"); $private_property2 = property_exists(TestClassExists::class, "testPrivatePropertyExists"); $protected_property1 = property_exists($test_class_exists, "testProtectedPropertyExists"); $protected_property2 = property_exists(TestClassExists::class, "testProtectedPropertyExists"); $public_property1 = property_exists($test_class_exists, "testPublicPropertyExists"); $public_property2 = property_exists(TestClassExists::class, "testPublicPropertyExists"); // Assert $this->assertTrue($private_property1); $this->assertTrue($private_property2); $this->assertTrue($protected_property1); $this->assertTrue($protected_property2); $this->assertTrue($public_property1); $this->assertTrue($public_property2); } public function testTraitExists() { // Arrange // Actual $test_trait_exists = trait_exists(TestTraitExists::class); // Assert $this->assertTrue($test_trait_exists); } public function testClassAlias() { // Arrange class_alias(TestClassExists::class, "MyRightCapitalContainerTestsAliasTestClassExists"); $test_class_exists = new TestClassExists(); // Actual $actual = new AliasTestClassExists(); //Assert $this->assertInstanceOf(TestClassExists::class, $actual); $this->assertInstanceOf(AliasTestClassExists::class, $test_class_exists); } public function testGetClass() { // Arrange $test_class_exists = new TestClassExists(); // Actual $class_name = get_class($test_class_exists); // Assert $this->assertSame(TestClassExists::class, $class_name); } public function testGetParentClass() { // Arrange $child_class = new ChildClass(); // Actual $parent_class1 = get_parent_class($child_class); $parent_class2 = get_parent_class(ChildClass::class); // Assert $this->assertSame(ParentClass::class, $parent_class1); $this->assertSame(ParentClass::class, $parent_class2); } public function testGetCalledClass() { // Arrange $child_class = new ChildClass(); $parent_class = new ParentClass(); // Actual $child_called_class = $child_class->testGetCalledClass(); $parent_called_class = $parent_class->testGetCalledClass(); // Assert $this->assertSame(ChildClass::class, $child_called_class); $this->assertSame(ParentClass::class, $parent_called_class); } public function testInArray() { $this->assertTrue(in_array("a", ["a", "b", 1], true)); } public function testGetClassMethod() { // Arrange $get_class_methods1 = get_class_methods(ChildClass::class); $get_class_methods2 = get_class_methods(new ChildClass()); // Actual // Assert $this->assertFalse(in_array("testPrivateGetClassMethod", $get_class_methods1, true)); $this->assertFalse(in_array("testPrivateGetClassMethod", $get_class_methods2, true)); $this->assertFalse(in_array("testProtectedGetClassMethod", $get_class_methods1, true)); $this->assertFalse(in_array("testProtectedGetClassMethod", $get_class_methods2, true)); $this->assertTrue(in_array("testPublicGetClassMethod", $get_class_methods1, true)); $this->assertTrue(in_array("testPublicGetClassMethod", $get_class_methods2, true)); $this->assertTrue(in_array("testGetCalledClass", $get_class_methods1, true)); $this->assertTrue(in_array("testGetCalledClass", $get_class_methods2, true)); } public function testGetClassVars() { // Arrange // Actual $class_vars = get_class_vars(ChildClass::class); // Assert $this->assertArrayNotHasKey("privateNoDefaultVar", $class_vars); $this->assertArrayNotHasKey("privateDefaultVar", $class_vars); $this->assertArrayNotHasKey("protectedNoDefaultVar", $class_vars); $this->assertArrayNotHasKey("protectedDefaultVar", $class_vars); $this->assertEmpty($class_vars["publicNoDefaultVar"]); $this->assertEquals("public_laravel", $class_vars["publicDefaultVar"]); } public function testGetObjectVars() { // Arrange $get_object_vars = new TestGetObjectVars(1, 2, 3); // Actual $object_vars = get_object_vars($get_object_vars); // Assert $this->assertArrayNotHasKey("x", $object_vars); $this->assertArrayNotHasKey("y", $object_vars); $this->assertEquals(3, $object_vars["z"]); $this->assertArrayNotHasKey("dot1", $object_vars); $this->assertArrayNotHasKey("dot2", $object_vars); $this->assertArrayNotHasKey("circle1", $object_vars); $this->assertArrayNotHasKey("circle2", $object_vars); $this->assertEquals(10, $object_vars["line1"]); $this->assertEmpty($object_vars["line2"]); } public function testIsSubclassOf() { // Arrange $child_class = new ChildClass(); // Actual $is_subclass = is_subclass_of($child_class, ParentClass::class); // Assert $this->assertTrue($is_subclass); } public function testIsA() { // Arrange $child_class = new ChildClass(); // Actual $is_object = is_a($child_class, ChildClass::class); $is_subclass = is_a($child_class, ParentClass::class); // Assert $this->assertTrue($is_object); $this->assertTrue($is_subclass); } } class TestGetObjectVars { private $x; protected $y; public $z; private $dot1 = 10; private $dot2; protected $circle1 = 20; protected $circle2; public $line1 = 10; public $line2; public function __construct($x, $y, $z) { $this->x = $x; $this->y = $y; $this->z = $z; } } class ChildClass extends ParentClass { private $privateNoDefaultVar; private $privateDefaultVar = "private_laravel"; protected $protectedNoDefaultVar; protected $protectedDefaultVar = "protected_laravel"; public $publicNoDefaultVar; public $publicDefaultVar = "public_laravel"; private function testPrivateGetClassMethod() { } protected function testProtectedGetClassMethod() { } public function testPublicGetClassMethod() { } } class ParentClass { public function testGetCalledClass() { return get_called_class(); } } class TestClassExists { private $testPrivatePropertyExists; protected $testProtectedPropertyExists; public $testPublicPropertyExists; private function testPrivateMethodExists() { } protected function testProtectedMethodExists() { } public function testPublicMethodExists() { } } interface TestInterfaceExists { } trait TestTraitExists { }
PHP不仅提供了检测class, interface, trait, property, method这些函数Introspection Functions,还提供了一整套的API即反射来检测class, interface, trait, property, method,这些API是好几个类组成的,提供了很多好用的方法。限于篇幅,下篇再聊下反射API。
总结:本文主要聊了下PHP提供的一套检测class, interface, trait, property, method的两个工具包:Introspection Functions和Reflection API,这里先聊到Introspection Functions。下篇再聊下Reflection API的使用,到时见。
欢迎关注Laravel-China。
RightCapital招聘Laravel DevOps
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/30477.html
摘要:控制反转容器控制反转使依赖注入变得更加便捷。有瑕疵控制反转容器是实现的控制翻转容器的一种替代方案。容器的独立使用即使没有使用框架,我们仍然可以在项目中使用安装组件来使用的控制反转容器。在没有给定任何信息的情况下,容器是无法实例化相关依赖的。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味...
摘要:反射提供给面向对象编程可以自省的能力,即反射。在简单工厂模式中,根据传递的参数来返回不同的类的实例简单工厂模式又称为静态工厂方法模式。也就是简单工厂模式工厂工厂类。PHP高级特性-反射以及工厂设计模式的结合使用 [结合 Laravel-Admin 代码实例讲解]利用反射来实现工厂模式的生产而无需创建特定的工厂类本文地址http://janrs.com/?p=833转载无需经过作者本人授权转载...
摘要:基于反射对象进行查询模块反射这里我们不再使用而是使用扩展模块的获取所有的对象名获取表对象进行操作反射关联关系可以反射并建立表之间的但是建立关联列的命名为例如关于更多信息请详细参看官方文档 示例数据库下载:http://chinookdatabase.codepl...在SQLALchemy中,我们使用反射技术来获取相关database schema信息,如tables,views,in...
摘要:依赖注入依赖注入一词是由提出的术语,它是将组件注入到应用程序中的一种行为。就像说的依赖注入是敏捷架构中关键元素。类依赖于,所以我们的代码可能是这样的创建一个这是一种经典的方法,让我们从使用构造函数注入开始。 showImg(https://segmentfault.com/img/remote/1460000018806800); 文章转自:https://learnku.com/la...
阅读 3063·2021-11-24 10:34
阅读 3322·2021-11-22 13:53
阅读 2630·2021-11-22 12:03
阅读 3598·2021-09-26 09:47
阅读 3005·2021-09-23 11:21
阅读 4772·2021-09-22 15:08
阅读 3289·2021-07-23 10:59
阅读 1258·2019-08-29 18:31