允许动态和静态调用PHP函数
Allow PHP function to be called dynamically and statically
我正在构建一个应用程序,我需要能够链接方法,因此我需要能够动态和静态地调用方法。
例如:
$results = Class::where('something')
->where('something else');
在这种情况下,方法'where'需要静态和动态调用,并且仍然允许链接。
我知道 Laravel 与 Eloquent 有类似的东西,但我不知道如何实现这样的东西。
我只是惊讶地证明这 是 确实是可能的:
<?php
class Test {
private static $myself;
public function do() {
echo isset($this) ? 'd' : 's';
if (!isset(self::$myself)) {
self::$myself = new self;
}
return self::$myself;
}
public static function done() {
echo PHP_EOL;
}
}
Test::do()->do()->do()->done();
$myTest = new Test;
$myTest->do()->do()->do()->done();
$myTest::do()->do()->do()->done();
$myTest::do()->do()::do()->done();
Test::do()->do()->do()->done();
输出为:
sdd
ddd
sdd
sds
sdd
但在我眼里是非常糟糕的作风...
出于纯粹的兴趣,我找到了一种使用 __call
和 __callStatic
并将要调用的方法设置为调用它的范围无法访问的方法(即将方法设置为 private
或 protected
).
重要警告:使用__call
或__callStatic
可能会导致意外行为!首先阅读 here 以确保您理解这一点。下面的代码动态执行从 class 外部调用的私有 class 方法,因此您需要确保将要添加此行为的特定方法列入白名单。可能还有其他安全问题我没有处理,所以使用风险自负。
class MethodTest
{
private static $obj;
private $fruit = 'watermelon';
private function handleDynamicCallType($name, $arguments)
{
switch ($name) {
case 'method1':
case 'method2':
$this->{$name}($arguments);
break;
default:
throw new Exception("Invalid name");
}
}
private function method1() {
echo $this->fruit . ' ';
}
private function method2() {
$arg_list = func_get_args();
if (!empty($arg_list[0][0])) {
$this->fruit = $arg_list[0][0];
}
}
public function __call($name, $arguments)
{
$this->handleDynamicCallType($name, $arguments);
return $this;
}
public static function __callStatic($name, $arguments)
{
if (!isset(self::$obj)) {
self::getNewStaticInstance();
}
self::$obj->{$name}($arguments);
return self::$obj;
}
public static function getNewStaticInstance()
{
self::$obj = new MethodTest();
return self::$obj;
}
}
$obj = new MethodTest;
$obj->method1()::method2('orange')->method1();
echo PHP_EOL;
MethodTest::method1()->method2('plum')::method1();
输出:
watermelon orange
orange plum
然而,静态对象在先前调用后保留其属性(注意两个 orange
字符串)。如果这是不希望的,我们可以通过调用 getNewStaticInstance()
:
强制它重置
MethodTest::method1()::method2('plum')::method1()::getNewStaticInstance()::method1();
输出:
watermelon plum watermelon
我正在构建一个应用程序,我需要能够链接方法,因此我需要能够动态和静态地调用方法。
例如:
$results = Class::where('something')
->where('something else');
在这种情况下,方法'where'需要静态和动态调用,并且仍然允许链接。
我知道 Laravel 与 Eloquent 有类似的东西,但我不知道如何实现这样的东西。
我只是惊讶地证明这 是 确实是可能的:
<?php
class Test {
private static $myself;
public function do() {
echo isset($this) ? 'd' : 's';
if (!isset(self::$myself)) {
self::$myself = new self;
}
return self::$myself;
}
public static function done() {
echo PHP_EOL;
}
}
Test::do()->do()->do()->done();
$myTest = new Test;
$myTest->do()->do()->do()->done();
$myTest::do()->do()->do()->done();
$myTest::do()->do()::do()->done();
Test::do()->do()->do()->done();
输出为:
sdd
ddd
sdd
sds
sdd
但在我眼里是非常糟糕的作风...
出于纯粹的兴趣,我找到了一种使用 __call
和 __callStatic
并将要调用的方法设置为调用它的范围无法访问的方法(即将方法设置为 private
或 protected
).
重要警告:使用__call
或__callStatic
可能会导致意外行为!首先阅读 here 以确保您理解这一点。下面的代码动态执行从 class 外部调用的私有 class 方法,因此您需要确保将要添加此行为的特定方法列入白名单。可能还有其他安全问题我没有处理,所以使用风险自负。
class MethodTest
{
private static $obj;
private $fruit = 'watermelon';
private function handleDynamicCallType($name, $arguments)
{
switch ($name) {
case 'method1':
case 'method2':
$this->{$name}($arguments);
break;
default:
throw new Exception("Invalid name");
}
}
private function method1() {
echo $this->fruit . ' ';
}
private function method2() {
$arg_list = func_get_args();
if (!empty($arg_list[0][0])) {
$this->fruit = $arg_list[0][0];
}
}
public function __call($name, $arguments)
{
$this->handleDynamicCallType($name, $arguments);
return $this;
}
public static function __callStatic($name, $arguments)
{
if (!isset(self::$obj)) {
self::getNewStaticInstance();
}
self::$obj->{$name}($arguments);
return self::$obj;
}
public static function getNewStaticInstance()
{
self::$obj = new MethodTest();
return self::$obj;
}
}
$obj = new MethodTest;
$obj->method1()::method2('orange')->method1();
echo PHP_EOL;
MethodTest::method1()->method2('plum')::method1();
输出:
watermelon orange
orange plum
然而,静态对象在先前调用后保留其属性(注意两个 orange
字符串)。如果这是不希望的,我们可以通过调用 getNewStaticInstance()
:
MethodTest::method1()::method2('plum')::method1()::getNewStaticInstance()::method1();
输出:
watermelon plum watermelon