目標
我在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
可以靜態調用;