Laravel 的辅助函数 optional() 我们经常使用到,它是避免我们直接从一个 null 变量上读取属性导致报错的问题,我们可以这样:

optional($user)->name

如果 $usernull 的话,那么直接 $user->name 系统会报错的,但是使用 optional($user)->name$usernull 的情况下整个语句返回 null 而不是报错,这样减少了我们代码量和报错几率了。但是 optional 不仅仅只有上面这个常用的功能。

功能一:可以调用方法

public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

        if (is_object($this->value)) {
            return $this->value->{$method}(...$parameters);
        }
    }

上面是 Optional 类的方法,定义了魔术方法 __call,所以我们可以直接 optional($user)->getName() 这样去使用,如果 getName() 方法存在那么就调用这个方法,如果不存在那就为空。所以这是其中一种方法。

功能二:使用宏

还是上面的代码,我们可以看到这个判断:

 if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

这段代码首先检测了当前 optional 是否注册了宏命令,如果存在那就直接使用宏命令(什么是宏命令?自个google去)。那么 static::hasMacro 是怎么来的呢,看下面的代码:

<?php

namespace Illuminate\Support;

use ArrayAccess;

class Optional implements ArrayAccess
{
    use Traits\Macroable {
        __call as macroCall;
    }

Optional 使用了 Macoable trait,我们进去看下:

<?php

namespace Illuminate\Support\Traits;

use Closure;
use ReflectionClass;
use ReflectionMethod;
use BadMethodCallException;

trait Macroable
{
    /**
     * The registered string macros.
     *
     * @var array
     */
    protected static $macros = [];

    /**
     * Register a custom macro.
     *
     * @param  string $name
     * @param  object|callable  $macro
     *
     * @return void
     */
    public static function macro($name, $macro)
    {
        static::$macros[$name] = $macro;
    }

    /**
     * Mix another object into the class.
     *
     * @param  object  $mixin
     * @return void
     */
    public static function mixin($mixin)
    {
        $methods = (new ReflectionClass($mixin))->getMethods(
            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
        );

        foreach ($methods as $method) {
            $method->setAccessible(true);

            static::macro($method->name, $method->invoke($mixin));
        }
    }

    /**
     * Checks if macro is registered.
     *
     * @param  string  $name
     * @return bool
     */
    public static function hasMacro($name)
    {
        return isset(static::$macros[$name]);
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    public static function __callStatic($method, $parameters)
    {
        if (! static::hasMacro($method)) {
            throw new BadMethodCallException("Method {$method} does not exist.");
        }

        if (static::$macros[$method] instanceof Closure) {
            return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
        }

        return call_user_func_array(static::$macros[$method], $parameters);
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    public function __call($method, $parameters)
    {
        if (! static::hasMacro($method)) {
            throw new BadMethodCallException("Method {$method} does not exist.");
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
            return call_user_func_array($macro->bindTo($this, static::class), $parameters);
        }

        return call_user_func_array($macro, $parameters);
    }
}

Optional 通过了 Macroable trait 实现了宏命令的调用。

功能三:当作数组使用

我们可以到这个代码:

class Optional implements ArrayAccess

Optional 实现了 ArrayAccess 接口,这就意味着我们可以像使用数组一样使用 optional 了,所以可以这样:

optional($user)['name']

下面是 ArrayAccess 的具体实现:

/**
     * Determine if an item exists at an offset.
     *
     * @param  mixed  $key
     * @return bool
     */
    public function offsetExists($key)
    {
        return Arr::accessible($this->value) && Arr::exists($this->value, $key);
    }

    /**
     * Get an item at a given offset.
     *
     * @param  mixed  $key
     * @return mixed
     */
    public function offsetGet($key)
    {
        return Arr::get($this->value, $key);
    }

    /**
     * Set the item at a given offset.
     *
     * @param  mixed  $key
     * @param  mixed  $value
     * @return void
     */
    public function offsetSet($key, $value)
    {
        if (Arr::accessible($this->value)) {
            $this->value[$key] = $value;
        }
    }

    /**
     * Unset the item at a given offset.
     *
     * @param  string  $key
     * @return void
     */
    public function offsetUnset($key)
    {
        if (Arr::accessible($this->value)) {
            unset($this->value[$key]);
        }
    }

这就是 Optional 另外三个功能,非常使用。