Laravel学习笔记之Callback Type

说明:本文主要学习PHP的回调类型callback type,通常在使用函数进行回调时,如使用call_user_func($callback, $parameters)进行回调时,需要传入回调callback,实际上有几种callback type的,在Laravel中也大量使用回调,并根据场景不同传入不同的回调类型。

开发环境:Laravel5.3 + PHP7

(Function/Static Class Method) Callback Type

Function Callback Type是把函数名作为callable类型传进去作为回调类型,形式如:($function_name, $dependency)。Static Class Method Callable Type与其类似,是把类名+静态方法名传进去作为回调类型,形式如:("$class_name::$static_method_name", $dependency)。看下PHPUnit测试,看看如何使用callback type,爆绿灯:

<?php

namespace MyRightCapital\Container\Tests;

class Callback extends \PHPUnit_Framework_TestCase
{
    public function testFunctionCallback()
    {
        // Arrange
        $expected = 'container';
        
        // Actual
        $actual = call_user_func('MyRightCapital\Container\Tests\functionCallback', 'container'); // ($function_name, $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testStaticClassMethodCallback()
    {
        // Arrange
        $expected = 'container';
        
        // Actual
        $actual = call_user_func('MyRightCapital\Container\Tests\StaticClassMethodCallback::staticClassMethod', 'container'); // ("$class_name::$static_method_name", $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
}

function functionCallback($app)
{
    return $app;
}

class StaticClassMethodCallback
{
    public static function staticClassMethod($app)
    {
        return $app;
    }
}

(Class Method/Relation Class Method) Callback Type

Class Method Callback Type也是类名+静态方法名,只不过形式稍不一样,形式如:([$class_name, $method_name], $dependency)。Relation Class Method Callback Type是可以使用父类的方法传进去作为回调,形式如:([$class_name, "parent::$method_name"], $dependency)。写个PHPUnit测试看下使用:

    public function testClassMethodCallback()
    {
        // Arrange
        $expected = 'container';
        
        // Actual
        $actual = call_user_func([ClassMethodCallback::class, 'classMethod'], 'container'); // ([$class_name, $method_name], $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testRelationClassMethodCallback()
    {
        // Arrange
        $expected = 'container/container';
        
        // Actual
        $actual = call_user_func([ClassMethodCallback::class, 'parent::classMethod'], 'container'); // ([$class_name, "parent::$method_name"], $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    
    class ClassMethodCallback extends ParentClassMethodCallback
    {
        public static function classMethod($app)
        {
            return $app;
        }
    }
    
    class ParentClassMethodCallback
    {
        public static function classMethod($app)
        {
            return $app . '/' . $app;
        }
    }

Object Method Callback Type

Object Method Callback Type是把对象方法作为参数传进去作为回调,形式如:([$object, $method_name], $dependency),写个PHPUnit测试:

    public function testObjectMethodCallback()
    {
        // Arrange
        $class_method_callback = new ClassMethodCallback();
        $expected = 'container';
        
        // Actual
        $actual = call_user_func([$class_method_callback, 'objectMethod'], 'container'); // ([$object, $method_name], $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    class ClassMethodCallback extends ParentClassMethodCallback
    {
        public static function classMethod($app)
        {
            return $app;
        }
        
        public function objectMethod($app)
        {
            return $app;
        }
    }

Closure

把Closure作为参数传进去作为回调参数这种方式在Laravel中大量使用,比如Laravel的Pipeline源码就大量使用这种方式,Pipeline的源码可看:Laravel学习笔记之Middleware源码解析。写下PHPUnit测试看下:

    public function testClosureCallback()
    {
        // Arrange
        
        // Actual
        $actual       = call_user_func(getClosure(), 'stack', 'pipe');
        $actual_value = call_user_func($actual, 'request');
        // Assert
        $this->assertInstanceOf(\Closure::class, $actual);
        $this->assertSame('request/stack/pipe', $actual_value);
    }
    
    
    function getClosure()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                return $passable . '/' . $stack . '/' . $pipe;
            };
        };
    }
    

最后,给出整个源码和测试结果:

<?php

namespace MyRightCapital\Container\Tests;

class Callback extends \PHPUnit_Framework_TestCase
{
    public function testFunctionCallback()
    {
        // Arrange
        $expected = 'container';
        
        // Actual
        $actual = call_user_func('MyRightCapital\Container\Tests\functionCallback', 'container'); // ($function_name, $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testStaticClassMethodCallback()
    {
        // Arrange
        $expected = 'container';
        
        // Actual
        $actual = call_user_func('MyRightCapital\Container\Tests\StaticClassMethodCallback::staticClassMethod', 'container'); // ("$class_name::$static_method_name", $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testClassMethodCallback()
    {
        // Arrange
        $expected = 'container';
        
        // Actual
        $actual = call_user_func([ClassMethodCallback::class, 'classMethod'], 'container'); // ([$class_name, $method_name], $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testRelationClassMethodCallback()
    {
        // Arrange
        $expected = 'container/container';
        
        // Actual
        $actual = call_user_func([ClassMethodCallback::class, 'parent::classMethod'], 'container'); // ([$class_name, "parent::$method_name"], $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testObjectMethodCallback()
    {
        // Arrange
        $class_method_callback = new ClassMethodCallback();
        $expected              = 'container';
        
        // Actual
        $actual = call_user_func([$class_method_callback, 'objectMethod'], 'container'); // ([$object, $method_name], $dependency)
        
        // Assert
        $this->assertSame($expected, $actual);
    }
    
    public function testClosureCallback()
    {
        // Arrange
        
        // Actual
        $actual       = call_user_func(getClosure(), 'stack', 'pipe');
        $actual_value = call_user_func($actual, 'request');
        // Assert
        $this->assertInstanceOf(\Closure::class, $actual);
        $this->assertSame('request/stack/pipe', $actual_value);
    }
}

function getClosure()
{
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            return $passable . '/' . $stack . '/' . $pipe;
        };
    };
}

function functionCallback($app)
{
    return $app;
}

class StaticClassMethodCallback
{
    public static function staticClassMethod($app)
    {
        return $app;
    }
}

class ClassMethodCallback extends ParentClassMethodCallback
{
    public static function classMethod($app)
    {
        return $app;
    }
    
    public function objectMethod($app)
    {
        return $app;
    }
}

class ParentClassMethodCallback
{
    public static function classMethod($app)
    {
        return $app . '/' . $app;
    }
}

图片描述

总结:本文主要总结下PHP的Callback Type,便于提高自己的代码设计能力。遇到好的技术再聊,到时见。

欢迎关注Laravel-China

RightCapital招聘Laravel DevOps


为五斗米折腰

3.1k 声望
920 粉丝
0 条评论
推荐阅读
Kubernetes学习笔记之CSI External Provisioner源码解析
最近在部署K8s持久化存储插件时,需要按照CSI官网说明部署一个Deployment pod,由于我们的自研存储类型是文件存储不是块存储,所以部署pod不需要包含容器 external-attacher ,只需要包含 external-provisioner s...

lx10363阅读 3.6k

One 一个简洁的博客、微博客系统
代码:[链接]文档:[链接]系统预览首页:微博列表:微博详细:文章列表:文章详细:归档:搜索,目前只能依据分类、标签搜索😀:管理后台:

Eyeswap45阅读 2.3k评论 1

怎样用 PHP 来实现枚举?
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见,...

唯一丶25阅读 6.3k评论 4

图片防盗链破解 解决图片防盗链问题 反向代理
当客户端(浏览器)向服务器请求内容的时候,会提交一个header,这个header中包含了如:浏览器信息、cookie等内容,那么有一个叫referer的东东,也包含在这里面。

TANKING7阅读 11.2k评论 5

Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2k评论 2

封面图
微信公众号开发:自动回复文本/图片/图文消息/关键词回复/上传素材/自定义菜单
对接流程1、申请微信公众号测试账号URL:[链接]2、登录,配置开发者服务器URL和Token开发者服务器配置代码:config.php {代码...} URL是config.php在你服务器的URLToken是上面代码自己设置的Token搞定之后,就能完...

TANKING2阅读 10k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go4阅读 944评论 2

封面图

为五斗米折腰

3.1k 声望
920 粉丝
宣传栏