文档章节

Laravel5.3之函数处理(Function Handling)

botkenni
 botkenni
发布于 2016/11/15 13:57
字数 890
阅读 9
收藏 0

说明:Laravel中经常使用PHP的Function Handling来设计代码,本文主要学习PHP的Function Handling特性,来提高写代码时的设计质量。PHP提供了一些函数处理操作的内置函数,主要有:

  1. call_user_func_array( )

  2. call_user_func( )

  3. func_get_arg( )

  4. func_get_args( )

  5. func_num_args( )

  6. function_exists( )

开发环境:Laravel5.3 + PHP7

Function Handling

call_user_func_array()/call_user_func()

call_user_func_array()是调用回调函数,并把一个数组作为参数传进去作为回调函数的参数;call_user_func()也是调用回调函数,区别是并没有要求把数组作为参数传进回调函数做参数。在Laravel中大量使用这两个内置函数来设计代码,比如\Illuminate\Foundation\Application::fireAppCallbacks()的源码:

    /** * Call the booting callbacks for the application. * * @param array $callbacks * @return void */
    protected function fireAppCallbacks(array $callbacks)
    {
        foreach ($callbacks as $callback) {
            call_user_func($callback, $this); //执行回调函数,并把Application对象作为参数传进去
        }
    }

call_user_func()和call_user_func_array()可以说是PHP设计好代码的神器,不得不熟悉,这里给下它的PHPUnit测试看看如何使用,爆绿灯:

<?php

namespace MyRightCapital\Container\Tests;

class FunctionHandling extends \PHPUnit_Framework_TestCase
{
    public function testCallUserFunc()
    {
        // Arrange
        $provider = new Provider();
        $app      = new Application($provider);

        // Actual
        $actual = call_user_func('MyRightCapital\Container\Tests\callUserFunc', $app);

        // Assert
        $this->assertSame('This is a service provider.', $actual);
    }

    public function testCallUserFuncArray()
    {
        // Arrange
        $provider = new Provider();
        $app      = new Application($provider);

        // Actual
        $actual = call_user_func_array('MyRightCapital\Container\Tests\callUserFunc', [$app]);

        // Assert
        $this->assertSame('This is a service provider.', $actual);
    }
}

function callUserFunc($app)
{
    return $app->register();
}

class Application
{
    private $provider;

    public function __construct($provider)
    {
        $this->provider = $provider;
    }

    public function register()
    {
        return $this->provider->register();
    }
}

class Provider
{
    public function register()
    {
        return 'This is a service provider.';
    }
}

call_user_func_array()和call_user_func()真是个非常用的函数,值得在设计自己的代码里使用。

func_get_arg()/func_get_args()/func_num_args()

func_get_arg()是从函数的参数列表读取某个指定的参数,func_get_args()是读取函数的整个参数列表作为数组返回,func_num_args()是读取函数的参数的个数。Laravel中的IlluminateFoundationApplication::environment()使用了这三个函数来设计代码,很巧妙:

    /** * Get or check the current application environment. * * @return string|bool */
    public function environment()
    {    
        // 如果传入了参数
        if (func_num_args() > 0) {
            // 如果第一个参数是数组形式就把该数组赋值给$patterns;如果不是就把所有参数作为一个数组赋值给$patterns
            $patterns = is_array(func_get_arg(0)) ? func_get_arg(0) : func_get_args();

            foreach ($patterns as $pattern) {
                if (Str::is($pattern, $this['env'])) {
                    return true;
                }
            }

            return false;
        }

        return $this['env'];
    }

看environment()源码可知道environment()是可以传入参数的,如果不传入参数就返回$this['env']的值即Laravel中的环境变量APP_ENV值,如App::environment()即为读取Laravel当前运行环境变量值;如果传入参数则判断该值是否与环境变量值相等,如App::environment('production','staging', 'development')即判断当前Laravel运行环境是否是'production','staging', 'development'中的一种。很巧妙的设计。

这里写个PHPUnit测试下,爆绿灯:

class FunctionHandling extends \PHPUnit_Framework_TestCase
{
    public function testFuncArgs()
    {
        // Arrange
        $provider = new Provider();
        $app      = new Application($provider);
        
        // Actual
        $arg_number0 = $app->testFuncArg(); 
        $arg_number1 = $app->testFuncArg('Laravel');
        $arg_number2 = $app->testFuncArg(['Laravel', 'PHP']);
        
        // Assert
        $this->assertSame(0, $arg_number0);
        $this->assertSame(1, $arg_number1);
        $this->assertSame(2, $arg_number2);
    }
}

class Application
{
    private $provider;

    public function __construct($provider)
    {
        $this->provider = $provider;
    }

    public function register()
    {
        return $this->provider->register();
    }

    public function testFuncArg()
    {
        if (func_num_args() > 0) {
            $patterns = is_array(func_get_arg(0)) ? func_get_arg(0) :func_get_args();
            return count($patterns);
        }
        
        return 0;
    }
}
 

function_exists()

function_exists()判断指定函数是否已经定义,这个函数在Laravel中大量使用,尤其是造辅助函数时使用,参考Illuminate/Foundation/helpers.php,Illuminate/Support/helpers.php。这里做个PHPUnit测试,爆绿灯:

class FunctionHandling extends \PHPUnit_Framework_TestCase
{
    public function testFunctionExists()
    {
        // Arrange
        $expected = 'Container';

        // Actual
        $actual = functionExists('Container');

        // Assert
        $this->assertSame($expected, $actual);
    }
}
        
if (!function_exists('functionExists')) {
    function functionExists($container)
    {
        return $container;
    }
}
    

总结:本文主要学习了PHP的Function Handling,这个技术可以用来提高自己的代码设计能力,同时Laravel中也大量使用了这个技术来巧妙设计代码。下次遇到好的技术在分享,到时见。

© 著作权归作者所有

共有 人打赏支持
botkenni
粉丝 18
博文 409
码字总数 434882
作品 0
西城
程序员
私信 提问
Laravel5.3之Callback Type

说明:本文主要学习PHP的回调类型callback type,通常在使用函数进行回调时,如使用calluserfunc($callback, $parameters)进行回调时,需要传入回调callback,实际上有几种callback type的,在...

botkenni
2016/11/15
9
0
【原译】javascript中的正确错误处理

【原译】javascript中的正确错误处理 翻译原文:http://ouvens.github.io/article-translation/2016/05/12/proper-error-handler-in-javascript.html   这是关于JavaScript中异常处理的故事...

ouven
2016/05/12
2.9K
2
Laravel5.3之Middleware源码解析

说明:本文主要学习Laravel的Middleware的源码设计思想,并将学习心得分享出来,希望对别人有所帮助。Laravel5.3之Decorator Pattern已经聊过Laravel使用了Decorator Pattern来设计Middlewar...

botkenni
2016/10/27
37
0
linux C多线程下信号的使用

在Linux的多线程中使用信号机制,与在进程中使用信号机制有着根本的区别,可以说是完全不同。在进程环境中,对信号的处理是,先注册信号处理函数,当信号异步发生时,调用处理函数来处理信号...

So_care_about_y
2017/08/18
0
0
React - 事件处理

类似于DOM事件处理,有几点不同之处: React的事件名字是首字母小写的驼峰式写法,而不是小写的。 在JSX里面,事件处理器是一个函数,而不是一个字符串。 例子,HTML: React: 在React里面,...

OSC首席键客
2016/11/11
71
0

没有更多内容

加载失败,请刷新页面

加载更多

什么时候使用字节流、什么时候使用字符流,二者的区别

在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。 InputStream 和OutputStream,...

watermelon11
今天
4
0
Alpakka Kafka,反应式Kafka客户端

Alpakka Kafka 是一个要用于 Java 和 Scala 语言的开源的流感知和反应式集成数据线项目。它建立在 Akka Stream之上,提供了 DSL 来支持反应式和流式编程,内置回压功能。Akka Streams 是 Re...

羊八井
今天
4
0
PHP7源码编译安装详解

$ yum groupinstall "development tools"$ yum install -y gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel freetype \freetype-devel libpng libpng-devel libxml2 ......

问题终结者
今天
5
0
Django学习笔记-从创建虚拟环境到启用服务

1 前置条件 windows系统下,目前已经完成anaconda3或miniconda3的安装,这也意味着已经完成了python3的安装。 2 创建虚拟环境 1、通过Anaconda Prompt,使用conda命令创建虚拟环境(base) e...

davidwbnu
昨天
4
0
python学习04:函数的定义及基本使用

python可以像c++一样,可以直接定义函数。具体格式如下: def(关键字) 函数名(形参01,形参02...): do_something... #如果有返回值,则调用以下语句 return 返回值...

太空堡垒
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部