目标
我在laravel项目中自定义的Model中添加了一个方法,我想像Model的其他方法如all
方法一样支持在实例上调用和静态方式调用:
$flights = (new App\Flight)->all(); //在实例上调用
$flights = App\Flight::all(); //静态方式调用
我们的Model名称为AvatarPic
,我编写的测试代码为:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AvatarPic extends Model
{
protected $table = 'avatar_pic';
/**
* @var string
*/
private $path;
public function test()
{
echo 'test';
}
}
问题
当我在实例上调用时,代码可以正常执行:
(new App\Models\AvatarPic)->test(); // 将会打印出test
当我使用静态方式调用时,代码将会抛出异常:
App\Models\AvatarPic::test(); //将会抛出ErrorException (E_DEPRECATED)
异常描述为:
Non-static method App\Models\AvatarPic::test() should not be called statically
我的环境信息:
- 操作系统:windows10
- PHP版本:PHP 7.2.1
- laravel版本:Laravel Framework 5.5.48
检测
在看过laravel中Illuminate\Database\Eloquent\Model
代码后,我对laravel模型像all
方法的调用方式有了初步了解。
根据Illuminate\Database\Eloquent\Model
中__callStatic
的使用,我编写了测试代码来探测问题发生在哪里。
我编写一个测试文件test.php
:
<?php
class TestService
{
public static function __callStatic($name, $arguments)
{
echo 't';
}
public function test()
{
echo 'test';
}
}
TestService::test();
当我运行这个测试文件时,程序成功打印出了’test’:
这个结果给我了实现目标的希望,几乎同样的代码,在laravel外可以正常执行。为了验证是否由于继承Illuminate\Database\Eloquent\Model
造成的异常,我在laravel项目中编写了一样的代码:
<?php
namespace App\Services;
class TestService
{
public static function __callStatic($name, $arguments)
{
echo '__callStatic';
}
public function test()
{
echo 'test';
}
}
当我运行代码时,程序还是抛出了异常:
为了排除环境因素,我在centos7、php 7.3.1、laravel 5上执行也是如此。
猜想
- 应该是因为laravel内部做了限制。
- 在laravel整个项目中搜索"Non-static method",并没有搜索结果,也就是说这个异常是php产生的,不是laravel定义的。
- 可能laravel内部对php的配置做了修改。
在打印的异常信息中有"ErrorException (E_DEPRECATED)",那么上面的写法在laravel中的异常级别是E_DEPRECATED,使用原生php执行没有抛异常,可能是默认的php异常级别与laravel中配置的异常级别不一致。
验证
- PHP中的异常级别有:PHP: 预定义常量 - Manual
- PHP中的设置错误异常:HP: error_reporting - Manual
- PHP中查看错误级别:
ini_get('error_reporting')
查看laravel中的错误级别:
查看PHP中错误级别:
laravel中设置为报告所有异常错误,PHP设置的报告异常有
我们修改我们在测试代码中添加error_reporting(-1)``,重新执行程序:
<?php
error_reporting(-1);
class TestService
{
public static function __callStatic($name, $arguments)
{
echo 't';
}
public function test()
{
echo 'test';
}
}
TestService::test();
(new TestService)->test();
这时,使用php执行也会抛出异常:
结尾
虽然知道了laravel和原生php执行结果不同的原因,但使用两种方式调用的目标依然没有解决。
要想静态访问可以设置自定义方法的访问限定为protected
;参考:PHP 调用静态方法不存在时首先检查方法是否是静态的而不是走__callStatic? | Laravel China 社区
public
可以在实例上调用;protected
可以静态调用;