Laravel5.3之函數處理(Function Handling)

說明:Laravel中常用PHP的Function Handling來設計代碼,本文主要學習PHP的Function Handling特性,來提升寫代碼時的設計質量。PHP提供了一些函數處理操做的內置函數,主要有:php

  1. call_user_func_array( )api

  2. call_user_func( )數組

  3. func_get_arg( )app

  4. func_get_args( )ide

  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()的源碼:this

/** * 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測試看看如何使用,爆綠燈:spa

<?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中也大量使用了這個技術來巧妙設計代碼。下次遇到好的技術在分享,到時見。

相關文章
相關標籤/搜索