Làm thế nào để gọi một hàm từ một chuỗi được lưu trữ trong một biến?


288

Tôi cần có khả năng gọi một hàm, nhưng tên hàm được lưu trong một biến, điều này có khả thi không? ví dụ:

hàm foo ()
{
  // mã ở đây
}

thanh chức năng ()
{
  // mã ở đây
}

$ functionName = "foo";
// tôi cần gọi hàm dựa trên $ functionName là gì

Câu trả lời:


464

85
Nếu bạn cần gọi hàm của một đối tượng có tên trong một biến, hãy thực hiện điều này: call_user_func (mảng ($ obj, $ func), $ params)
BlackDivine

1
Cũng xem câu trả lời của tôi dưới đây nếu bạn đang làm việc với các lớp và phương thức. Tôi không mong đợi những gì tôi tìm thấy.
Chris K

Làm thế nào tôi có thể bỏ qua lỗi định nghĩa hàm nếu chức năng không được xác định? Nếu ($ functionName) đủ?
SaidbakR

14
@ sємsєм: Bạn nên sử dụng is_callablechức năng. Nó sẽ kiểm tra xem một biến có chứa thứ gì đó có thể được gọi là hàm hay không (có thể là tên của hàm, mảng chứa đối tượng và tên hàm hoặc hàm đóng / ẩn danh)
knittl

6
@Pacerier: Cung cấp tên lớp là thành phần đầu tiên của mảng:call_user_func(array('ClassName', 'method'), $args)
knittl

121

Phiên bản yêu thích của tôi là phiên bản nội tuyến:

${"variableName"} = 12;

$className->{"propertyName"};
$className->{"methodName"}();

StaticClass::${"propertyName"};
StaticClass::{"methodName"}();

Bạn cũng có thể đặt các biến hoặc biểu thức bên trong dấu ngoặc!


1
@marcioAlmada: đăng bình luận thay vì chỉnh sửa câu trả lời của người khác theo cách này; nó làm tăng thêm sự nhầm lẫn - không rõ ai đã nói gì [với người tò mò: xem bản sửa đổi để giải thích]
kryger

4
Ok @kryger. Dòng 2 {"functionName"}();không hợp pháp và sẽ đưa ra lỗi cú pháp.
marcio

4
Cảm ơn các bạn, vì đã trả lời, nó thực sự gây ra lỗi, ngay cả trong php 5.4, do đó cú pháp chỉ hoạt động với các phương thức đối tượng. Chỉnh sửa bài.
Lajos Meszaros


17

Như đã đề cập, có một số cách để đạt được điều này với phương pháp an toàn nhất có thể call_user_func()hoặc nếu bạn phải, bạn cũng có thể đi theo lộ trình $function_name(). Có thể truyền đối số bằng cả hai phương thức này.

$function_name = 'foobar';

$function_name(arg1, arg2);

call_user_func_array($function_name, array(arg1, arg2));

Nếu chức năng bạn đang gọi thuộc về một đối tượng, bạn vẫn có thể sử dụng một trong hai

$object->$function_name(arg1, arg2);

call_user_func_array(array($object, $function_name), array(arg1, arg2));

Tuy nhiên, nếu bạn định sử dụng $function_name()phương thức này, có thể là một ý tưởng tốt để kiểm tra sự tồn tại của hàm nếu tên đó theo bất kỳ cách nào động

if(method_exists($object, $function_name))
{
    $object->$function_name(arg1, arg2);
}

12

Trong trường hợp người khác được google đưa đến đây vì họ đang cố gắng sử dụng một biến cho một phương thức trong một lớp, bên dưới là một mẫu mã sẽ thực sự hoạt động. Không ai ở trên làm việc cho tình huống của tôi. Sự khác biệt chính là &trong tuyên bố $c = & new...&$cđược thông qua call_user_func.

Trường hợp cụ thể của tôi là khi triển khai mã của ai đó phải thực hiện bằng màu sắc và hai phương thức thành viên lighten()darken()từ lớp csscolor.php. Vì bất kỳ lý do gì, tôi muốn có cùng một mã có thể gọi sáng hoặc tối hơn là chọn nó bằng logic. Đây có thể là kết quả của sự bướng bỉnh của tôi khi không chỉ sử dụng if-other hoặc để thay đổi mã gọi phương thức này.

$lightdark="lighten"; // or optionally can be darken
$color="fcc";   // a hex color
$percent=0.15;  
include_once("csscolor.php");
$c = & new CSS_Color($color);
$rtn=call_user_func( array(&$c,$lightdark),$color,$percent);

Lưu ý rằng thử bất cứ điều gì với $c->{...}không làm việc. Sau khi xem nội dung do người đọc đóng góp ở cuối trang php.net call_user_func, tôi đã có thể ghép các phần trên lại với nhau. Ngoài ra, lưu ý rằng $paramsvì một mảng không hoạt động với tôi:

// This doesn't work:
$params=Array($color,$percent);
$rtn=call_user_func( array(&$c,$lightdark),$params);

Nỗ lực trên này sẽ đưa ra cảnh báo về phương thức mong đợi đối số thứ 2 (phần trăm).


"$ c = & CSS_Color mới" làm gì? Tôi đang nói về amp (&). Điều này & thay đổi là gì?
Danon

@danon & là "địa chỉ của" hoặc toán tử "tham chiếu". Tôi đang kích hoạt một đối tượng mới của lớp CSS_Color và sau đó gán tham chiếu của nó (còn gọi là địa chỉ) cho $ c. Tôi ghét lặp lại mã, vì vậy tôi thường sử dụng tên biến.
Chris K

1
Tôi không hiểu Không phải đối tượng được thông qua tham chiếu ở mọi nơi? Ý tôi là, nếu bạn chuyển $ c cho một hàm và thay đổi nó, đối tượng ban đầu cũng sẽ bị ảnh hưởng, phải không? Cho dù bạn có sử dụng thứ này & không, tôi có đúng không?
Danon

Tôi đã thử các kết hợp biểu tượng khác nhau và chỉ khi đọc các bình luận của người dùng trên php.org tôi mới có thể nhận được mã làm việc. Bạn sẽ cần bắt đầu cuộc thảo luận đó với những người chịu trách nhiệm về hành vi của php.
Chris K

Ví dụ thứ hai bạn đọc sai, bạn đang tìm kiếm call_user_func_array
Tofandel ngày

12

Một vài năm muộn, nhưng đây là cách tốt nhất bây giờ imho:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();

3
Cách để OOP cho tôi, nhưng tôi đã cho nó +1, vì chưa ai đề cập đến các lớp Reflection.
Lajos Meszaros

Đừng hiểu câu trả lời này. Vấn đề là nó giải quyết, chính xác?
David Spector

9

Để hoàn thiện, bạn cũng có thể sử dụng eval () :

$functionName = "foo()";
eval($functionName);

Tuy nhiên, call_user_func()là cách thích hợp.


8
Cần lưu ý rằng eval () sẽ cho phép hành vi tương tự nhưng nó cũng sẽ cho phép thực thi bất kỳ mã PHP nào. Vì vậy, biến có thể được sử dụng để chiếm quyền điều khiển hệ thống. Call_user_func () không mang lại các vấn đề bảo mật như vậy.
Giăng

4
evalkhông phải là một giải pháp cho câu hỏi này.
ankr

1
Xin đừng làm điều này bởi vì kẻ tấn công có thể xóa sạch hoàn toàn máy chủ của bạn.
andreszs

9

Giải pháp: Sử dụng PHP7

Lưu ý: Đối với phiên bản tóm tắt, xem TL; DR ở cuối câu trả lời.

Phương pháp cũ

Cập nhật : Một trong những phương pháp cũ được giải thích ở đây đã bị xóa. Tham khảo các câu trả lời khác để giải thích về các phương pháp khác, nó không được đề cập ở đây. Nhân tiện, nếu câu trả lời này không giúp ích gì cho bạn, bạn nên quay lại nâng cấp công cụ của mình. Hỗ trợ PHP 5.6 đã kết thúc vào tháng 1 năm 2019 (hiện tại ngay cả PHP 7.0 và 7.1 cũng không được hỗ trợ). Xem các phiên bản được hỗ trợ để biết thêm thông tin.

Như những người khác đã đề cập, trong PHP5 (và cả trong các phiên bản mới hơn như PHP7), chúng ta có thể sử dụng các biến làm tên hàm, sử dụng call_user_func()call_user_func_array()(cá nhân, tôi ghét các hàm đó), v.v.

Phương pháp mới

Kể từ PHP7, có những cách mới được giới thiệu:

Lưu ý: Mọi thứ trong <something>ngoặc có nghĩa là một hoặc nhiều biểu thức để tạo thành một cái gì đó, ví dụ: <function_name>có nghĩa là các biểu thức tạo thành một tên hàm.

Gọi chức năng động: Tên chức năng đang hoạt động

Chúng ta có thể sử dụng một hoặc nhiều biểu thức bên trong dấu ngoặc đơn làm tên hàm chỉ trong một lần, dưới dạng:

(<function_name>)(arguments);

Ví dụ:

function something(): string
{
    return "something";
}

$bar = "some_thing";

(str_replace("_", "", $bar))(); // something

// Possible, too; but generally, not recommended, because makes your code more complicated
(str_replace("_", "", $bar))()(); 

Lưu ý: Mặc dù loại bỏ các dấu ngoặc đơn xung quanh str_replace()không phải là một lỗi, nhưng việc đặt dấu ngoặc đơn làm cho mã dễ đọc hơn. Tuy nhiên, đôi khi bạn không thể làm điều đó, ví dụ như trong khi sử dụng. toán tử. Để thống nhất, tôi khuyên bạn nên đặt dấu ngoặc luôn.

Gọi phương thức động: Tên phương thức đang hoạt động

Giống như các lệnh gọi hàm động, chúng ta có thể thực hiện tương tự với các lệnh gọi phương thức, được bao quanh bởi dấu ngoặc nhọn thay vì dấu ngoặc đơn (đối với các biểu mẫu bổ sung, điều hướng đến phần TL; DR):

$object->{<method_name>}(arguments);
$object::{<method_name>}(arguments);

Xem nó trong một ví dụ:

class Foo
{
    public function another(): string
    {
        return "something";
    }
}

$bar = "another thing";

(new Something())->{explode(" ", $bar)[0]}(); // something

Gọi phương thức động: Cú pháp mảng

Một cách thanh lịch hơn được thêm vào trong PHP7 là như sau:

[<object>, <method_name>](arguments);
[<class_name>, <method_name>](arguments); // Static calls only

Ví dụ:

class Foo
{
    public function nonStaticCall()
    {
        echo "Non-static call";
    }
    public static function staticCall()
    {
        echo "Static call";
    }
}

$x = new X();

[$x, "non" . "StaticCall"](); // Non-static call
[$x, "static" . "Call"](); // Static call

Lưu ý: Lợi ích của việc sử dụng phương thức này so với phương pháp trước đó là, bạn không quan tâm đến loại cuộc gọi (nghĩa là nó có tĩnh hay không).

Ví dụ bổ sung: Sử dụng các lớp ẩn danh

Làm cho mọi thứ hơi phức tạp, bạn có thể sử dụng kết hợp các lớp ẩn danh và các tính năng ở trên:

$bar = "SomeThing";

echo (new class {
    public function something()
    {
        return 512;
    }
})->{strtolower($bar)}(); // 512

TL; DR (Kết luận)

Nói chung, trong PHP7, sử dụng các hình thức sau đây đều có thể:

// Everything inside `<something>` brackets means one or more expressions
// to form something

// Dynamic function call
(<function_name>)(arguments)

// Dynamic method call on an object
$object->{<method_name>}(arguments)
$object::{<method_name>}(arguments)

// Dynamic method call on a dynamically-generated object
(<object>)->{<method_name>}(arguments)
(<object>)::{<method_name>}(arguments)

// Dynamic method call, statically
ClassName::{<method_name>}(arguments)
(<class_name>)::{<method_name>}(arguments)

// Dynamic method call, array-like (no different between static and non-static calls
[<object>, <method_name>](arguments)

// Dynamic method call, array-like, statically
[<class_name>, <method_name>](arguments)

Dựa chủ yếu vào bài nói chuyện PHP này .


1
Danh sách các biểu thức hay, cũng bao gồm:(expressions)->$bar()
Necro

1
@Necro Cảm ơn, tôi đã thêm nó!
MAChitgarha

7

Tên hàm và không gian tên động

Chỉ cần thêm một điểm về tên hàm động khi sử dụng không gian tên.

Nếu bạn đang sử dụng không gian tên, các mục sau sẽ không hoạt động trừ khi chức năng của bạn nằm trong không gian tên toàn cầu:

namespace greetings;

function hello()
{
    // do something
}

$myvar = "hello";
$myvar(); // interpreted as "\hello();"

Phải làm sao?

Bạn phải sử dụng call_user_func()thay thế:

// if hello() is in the current namespace
call_user_func(__NAMESPACE__.'\\'.$myvar);

// if hello() is in another namespace
call_user_func('mynamespace\\'.$myvar);

3

Bổ sung câu trả lời của @Chris K nếu bạn muốn gọi phương thức của đối tượng, bạn có thể gọi nó bằng một biến duy nhất với sự trợ giúp của bao đóng:

function get_method($object, $method){
    return function() use($object, $method){
        $args = func_get_args();
        return call_user_func_array(array($object, $method), $args);           
    };
}

class test{        

    function echo_this($text){
        echo $text;
    }
}

$test = new test();
$echo = get_method($test, 'echo_this');
$echo('Hello');  //Output is "Hello"

Tôi đã đăng một ví dụ khác ở đây


3

Những gì tôi học được từ câu hỏi này và câu trả lời. Cảm ơn tất cả!

Giả sử tôi có các biến và hàm này:

$functionName1 = "sayHello";
$functionName2 = "sayHelloTo";
$functionName3 = "saySomethingTo";

$friend = "John";
$datas = array(
    "something"=>"how are you?",
    "to"=>"Sarah"
);

function sayHello()
{
echo "Hello!";
}

function sayHelloTo($to)
{
echo "Dear $to, hello!";
}

function saySomethingTo($something, $to)
{
echo "Dear $to, $something";
}
  1. Để gọi hàm không có đối số

    // Calling sayHello()
    call_user_func($functionName1); 

    Xin chào!

  2. Để gọi hàm với 1 đối số

    // Calling sayHelloTo("John")
    call_user_func($functionName2, $friend);

    John thân mến, xin chào!

  3. Để gọi hàm với 1 hoặc nhiều đối số Điều này sẽ hữu ích nếu bạn tự động gọi các hàm của mình và mỗi hàm có số lượng đối số khác nhau. Đây là trường hợp của tôi mà tôi đã tìm kiếm (và giải quyết). call_user_func_arraylà chìa khóa

    // You can add your arguments
    // 1. statically by hard-code, 
    $arguments[0] = "how are you?"; // my $something
    $arguments[1] = "Sarah"; // my $to
    
    // 2. OR dynamically using foreach
    $arguments = NULL;
    foreach($datas as $data) 
    {
        $arguments[] = $data;
    }
    
    // Calling saySomethingTo("how are you?", "Sarah")
    call_user_func_array($functionName3, $arguments);

    Sarah thân mến, bạn có khỏe không?

Tạm biệt!



1

Mã sau có thể giúp viết hàm động trong PHP. bây giờ tên hàm có thể được thay đổi động theo biến '$ current_page'.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();

Các hàm ẩn danh (tức là các hàm động) khác với các hàm biến (tức là các hàm gọi động). Ngoài ra, bạn đang thay thế $functionbiến mà không có lý do.
MAChitgarha

0

Cách dễ nhất để gọi một hàm một cách an toàn bằng cách sử dụng tên được lưu trữ trong một biến là,

//I want to call method deploy that is stored in functionname 
$functionname = 'deploy';

$retVal = {$functionname}('parameters');

Tôi đã sử dụng như dưới đây để tạo các bảng di chuyển trong Laravel một cách linh hoạt,

foreach(App\Test::$columns as $name => $column){
        $table->{$column[0]}($name);
}

-5

Một cách tiếp cận khác thường xuất hiện trong đầu tôi là, trừ khi bạn đang tạo toàn bộ mã thông qua một số AI siêu tự trị tự viết , có nhiều khả năng các hàm mà bạn muốn gọi "động", đã được xác định trong mã của bạn căn cứ. Vậy tại sao không kiểm tra chuỗi và làm điều khét tiếngifelse điệu nhảy để triệu tập ... bạn hiểu ý tôi.

ví dụ.

if($functionName == 'foo'){
  foo();
} else if($functionName == 'bar'){
  bar();
}

Thậm chí switch-casecó thể được sử dụng nếu bạn không thích mùi vị nhạt nhẽo của ifelsethang.

Tôi hiểu rằng có những trường hợp " tự động gọi hàm " sẽ là một điều cần thiết tuyệt đối ( Giống như một số logic đệ quy tự sửa đổi ). Nhưng hầu hết các trường hợp sử dụng tầm thường hàng ngày chỉ có thể tránh được.

Nó loại bỏ rất nhiều sự không chắc chắn từ ứng dụng của bạn, đồng thời cho bạn cơ hội thực hiện chức năng dự phòng nếu chuỗi không khớp với bất kỳ định nghĩa nào của các hàm có sẵn. IMHO.


Nếu giá trị của biến đã là tên của hàm thì tại sao phải làm thêm? ngoài ra, sẽ rất tốt nếu bạn nhận được phản hồi chắc chắn để xem hàm có tồn tại bởi php hay không trước khi bạn chạy nó: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro

2
Yup điểm của bạn là hợp lệ, nhưng vì tên hàm là động, nên trong logic của bạn, bất kỳ chuỗi nào sẽ được thực thi nếu một hàm theo tên đó tồn tại, bất kể hàm đó có phù hợp trong tình huống hiện tại hay không. Cách tiếp cận của tôi đảm bảo rằng chỉ một nhóm chức năng nhất định có thể được thực thi nếu không thì có thể ném lỗi. Nó có thể được đơn giản hóa bởi các mảng và như vậy.
Mohd Abdul Mujib

-11

Tôi không biết tại sao bạn phải sử dụng nó, nghe có vẻ không tốt cho tôi chút nào, nhưng nếu chỉ có một lượng nhỏ chức năng, bạn có thể sử dụng cấu trúc if / otherif. Tôi không biết nếu một giải pháp trực tiếp là có thể.

một cái gì đó như $ foo = "thanh"; $ test = "foo"; kiểm tra tiếng vang $$;

nên trả về thanh, bạn có thể thử xung quanh nhưng tôi không nghĩ rằng nó sẽ hoạt động cho các chức năng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.