Как вызвать функцию из класса php
Перейти к содержимому

Как вызвать функцию из класса php

  • автор:

Как вызвать метод в классе с динамически генерируемым именем через рефлексию в PHP

Допустим, у вас есть какой-то объект и вы хотите вызвать его метод, например вызов метода индекс на контроллере:

$controller->indexAction();

С этим все понятно, а что если имя метода неизвестно в тот момент, когда вы написали свой скрипт, и вам нужно получить его из переменной, например $method . Тоже норм:

$method = 'indexAction'; $object->();

но если метод не существует на объекте, это вызовет фатальную ошибку и прекратит выполнение скрипта. Но вы скажете, что можно использовать функцию method_exists , чтобы проверить, существует ли метод класса, и вы правы, это можно сделать:

$method = 'indexAction'; if (method_exists($object,$method)) < $object->(); > else

А что делать, если вы хотите передать какие-то параметры в вызываемый метод? если число параметров и их значений являются постоянными, вы можете легко передать его в обычном виде:

$object->($some,$param);

В случае когда количество параметров наперед не известно, вы можете передать динамический набор параметров создав массив и передав их в вызов функции call_user_func_array как это сделано в следующем примере:

$params = []; $params[] = 1; $params[] = 'second param value'; call_user_func_array([$object,$method]), $params); // это вызовет метод с двумя параметрами

А как же обрабатывать ошибки, которые могут возникнуть в случае, если количество параметров неверно или отсутствуют некоторые параметры? И как правильно вызвать статический метод объекта, который находится в пространстве имен?

call_user_func_array(__NAMESPACE__ .'\Controller::aliasAction',['about']);

Это бы сработало, но эта часть кода выглядит ужасно. Да, действительно, функция call_user_func_array была в пхп еще задолго до того как появились неймспейсы да и вообще обьекты, поэтому она не особо приспособлена для таких вещей. В таких случаях лучше (красивее, мы не говорим здесь об эффективности) использовать Reflection Api для вызова методов на объектах. Давайте посмотрим, как сделать такие вещи:

Вызов метод класса через отражение

Сначала вам нужно получить объект ReflectionMethod , который ссылается на необходимый вам класс и метод. Это можно сделать двумя способами:

    создать экземпляр ReflectionMethod:

 $method = new ReflectionMethod('ClassName','MethodName');
 $class = new ReflectionClass('ClassName'); $method = $class->getMethod('MethodName');

Давайте посмотрим на сложный пример вызова метода с параметрами, при помощи класса ReflectionMethod :

 $controller = new IndexController(); // imagine its a controller from framework with mvc architecture $method = 'indexAction'; // you need to call indexAction on it $params = ['en','about']; try < $reflectionMethod = new ReflectionMethod($controller,$method); // this would throw an exception in case method is not exists on object $reflectionMethod->invokeArgs($controller,$params); // throws exception if parameters are wrong > catch (/ReflectionException $e)

Да, куда лучше и понятнее чем пример выше с помощью call_user_func_array .
Более того, вышеупомянутый пример позволяет вам вызывать любой метод объекта таким образом: статический или обычный, с параметрами или без.

Вызов private метода

Reflection API позволяет вам вызывать даже приватные методы класса. Предположим есть класс:

 class IndexController < private function disabledAction()< echo 'private action result'; >// . rest of code . >

Вам не удасться вызвать этот метод при помощи примера который мы привели выше:

 $controller = new IndexController(); $reflectionMethod = (new ReflectionClass($controller))->getMethod($method); $reflectionMethod->invoke($controller);

Такой код вызовет ексепшн, который может привести программу к несовсем ожидаемым последствиям. Для того чтобы вызвать приватный метод класса в PHP нужно сделать его доступным к исполнению. В арсенале Reflection API PHP есть метод для этого: перед тем как делать invoke нашего метода , вы должны setAccesible его:

 $controller = new IndexController(); $reflectionMethod = (new ReflectionClass($controller))->getMethod($method); $reflectionMethod->setAccessible(true); // вот строчка которая делает метод доступным к вызову даже если он private $reflectionMethod->invoke($controller); // сейчас никаких ексепшнов уже не будет

Плюсы и минусы

Что касается меня, то я скажу это гораздо более элегантный код, чем использование функции call_user_func_array . Конечно, метод, который мы выбрали для вызова метода, зависит от того, чего нам нужно достичь: если нужно использовать как можно меньше ресурсов, то лучше использовать функциональный стиль.

Если нам нужен более элегантный код, более читаемый и написанный в объектно-ориентированном стиле — лучше использовать Reflection Api. Reflection API выдает исключение, и вам не нужно обрабатывать номера ошибок, которые функциональный метод возвращает в случае сбоя операции и так далее. Более того, рефлексия позволяет вам вызывать закрытые и защищенные методы, что невозможно сделать другими способами .

А какой способ больше по душе вам?

Как вызвать функцию из класса php

Callback-функции разрешено обозначать объявлением типа callable .

Функции вроде call_user_func() или usort() принимают определённые пользователем callback-функции в качестве параметра. Callback-функциями бывают как простые функции, так и методы объектов, включая статические методы классов.

Передача

PHP-функция передаётся по имени в виде строки. Разрешено передавать любую встроенную или пользовательскую функцию, исключая языковые конструкции: array() , echo , empty() , eval() , exit() , isset() , list() , print или unset() .

Метод созданного объекта ( object ) передаётся как массив, который в индексе 0 содержит — объект, а в индексе 1 — название метода. Изнутри класса разрешён доступ к закрытым и защищённым методам.

Статические методы класса тоже передаются — или как массив, в индексе 0 которого вместо передачи объекта указывают название класса, или как строка вида ‘ClassName::methodName’ .

Кроме пользовательских функций, в callback-параметр разрешено передавать анонимные и стрелочные функции.

Замечание:

Начиная с PHP 8.1.0 анонимные функции разрешено создавать синтаксисом callable-объектов первого класса.

Объекты, которые реализуют магический метод __invoke(), разрешно передавать в callback-параметр.

Пример #1 Пример callback-функции

// Пример callback-функции
function my_callback_function () echo ‘Привет, мир!’ ;
>

// Пример callback-метода
class MyClass static function myCallbackMethod () echo ‘Привет, мир!’ ;
>
>

// Тип 1: Простой callback
call_user_func ( ‘my_callback_function’ );

// Тип 2: Вызов статического метода класса
call_user_func (array( ‘MyClass’ , ‘myCallbackMethod’ ));

// Тип 3: Вызов метода класса
$obj = new MyClass ();
call_user_func (array( $obj , ‘myCallbackMethod’ ));

// Тип 4: Вызов статического метода класса
call_user_func ( ‘MyClass::myCallbackMethod’ );

// Тип 5: Вызов относительного статического метода
class A public static function who () echo «A\n» ;
>
>

class B extends A public static function who () echo «B\n» ;
>
>

call_user_func (array( ‘B’ , ‘parent::who’ )); // A, устарело, начиная с PHP 8.2.0

// Тип 6: Объекты, которые реализуют магический метод __invoke, разрешено использовать как callable-объекты
class C public function __invoke ( $name ) echo ‘Привет ‘ , $name , «\n» ;
>
>

$c = new C ();
call_user_func ( $c , ‘PHP!’ );

Пример #2 Пример передачи замыкания в callback-параметр

// Замыкание
$double = function( $a ) return $a * 2 ;
>;

// Диапазон чисел
$numbers = range ( 1 , 5 );

// Передаём в параметр замыкание в качестве callback-функции
// для удвоения каждого элемента в созданном выше диапазоне
$new_numbers = array_map ( $double , $numbers );

print implode ( ‘ ‘ , $new_numbers );

Результат выполнения приведённого примера:

2 4 6 8 10

Замечание:

Callback-функции, зарегистрированные такими функциями как call_user_func() и call_user_func_array() , не будут вызваны при наличии не пойманного исключения, брошенного в предыдущей callback-функции.

User Contributed Notes 12 notes

11 years ago

You can also use the $this variable to specify a callback:

public $property = ‘Hello World!’ ;

public function MyMethod ()
<
call_user_func (array( $this , ‘myCallbackMethod’ ));
>

public function MyCallbackMethod ()
<
echo $this -> property ;
>

8 years ago

A note on differences when calling callbacks as «variable functions» without the use of call_user_func() (e.g. » «):

— Using the name of a function as string has worked since at least 4.3.0
— Calling anonymous functions and invokable objects has worked since 5.3.0
— Using the array structure [$object, ‘method’] has worked since 5.4.0

Note, however, that the following are not supported when calling callbacks as variable functions, even though they are supported by call_user_func():

— Calling static class methods via strings such as ‘foo::doStuff’
— Calling parent method using the [$object, ‘parent::method’] array structure

All of these cases are correctly recognized as callbacks by the ‘callable’ type hint, however. Thus, the following code will produce an error «Fatal error: Call to undefined function foo::doStuff() in /tmp/code.php on line 4»:

class foo static function callIt (callable $callback ) $callback ();
>

static function doStuff () echo «Hello World!» ;
>
>

foo :: callIt ( ‘foo::doStuff’ );
?>

The code would work fine, if we replaced the ‘$callback()’ with ‘call_user_func($callback)’ or if we used the array [‘foo’, ‘doStuff’] as the callback instead.

10 years ago

When specifying a call back in array notation (ie. array($this, «myfunc») ) the method can be private if called from inside the class, but if you call it from outside you’ll get a warning:

class mc <
public function go (array $arr ) <
array_walk ( $arr , array( $this , «walkIt» ));
>

private function walkIt ( $val ) <
echo $val . «
» ;
>

public function export () <
return array( $this , ‘walkIt’ );
>
>

$data = array( 1 , 2 , 3 , 4 );

$m = new mc ;
$m -> go ( $data ); // valid

array_walk ( $data , $m -> export ()); // will generate warning

?>

Output:
1
2
3
4

Warning: array_walk() expects parameter 2 to be a valid callback, cannot access private method mc::walkIt() in /in/tfh7f on line 22

11 years ago

Performance note: The callable type hint, like is_callable(), will trigger an autoload of the class if the value looks like a static method callback.

9 years ago

You can use ‘self::methodName’ as a callable, but this is dangerous. Consider this example:

class Foo public static function doAwesomeThings () FunctionCaller :: callIt ( ‘self::someAwesomeMethod’ );
>

public static function someAwesomeMethod () // fantastic code goes here.
>
>

class FunctionCaller public static function callIt (callable $func ) call_user_func ( $func );
>
>

Foo :: doAwesomeThings ();
?>

This results in an error:
Warning: class ‘FunctionCaller’ does not have a method ‘someAwesomeMethod’.

For this reason you should always use the full class name:
FunctionCaller :: callIt ( ‘Foo::someAwesomeMethod’ );
?>

I believe this is because there is no way for FunctionCaller to know that the string ‘self’ at one point referred to to `Foo`.

10 years ago
you can pass an object as a callable if its class defines the __invoke() magic method..
8 years ago

I needed a function that would determine the type of callable being passed, and, eventually,
normalized it to some extent. Here’s what I came up with:

/**
* The callable types and normalizations are given in the table below:
*
* Callable | Normalization | Type
* ———————————+———————————+—————
* function (. ) use (. ) <. >| function (. ) use (. ) <. >| ‘closure’
* $object | $object | ‘invocable’
* «function» | «function» | ‘function’
* «class::method» | [«class», «method»] | ‘static’
* [«class», «parent::method»] | [«parent of class», «method»] | ‘static’
* [«class», «self::method»] | [«class», «method»] | ‘static’
* [«class», «method»] | [«class», «method»] | ‘static’
* [$object, «parent::method»] | [$object, «parent::method»] | ‘object’
* [$object, «self::method»] | [$object, «method»] | ‘object’
* [$object, «method»] | [$object, «method»] | ‘object’
* ———————————+———————————+—————
* other callable | idem | ‘unknown’
* ———————————+———————————+—————
* not a callable | null | false
*
* If the «strict» parameter is set to true, additional checks are
* performed, in particular:
* — when a callable string of the form «class::method» or a callable array
* of the form [«class», «method»] is given, the method must be a static one,
* — when a callable array of the form [$object, «method»] is given, the
* method must be a non-static one.
*
*/
function callableType ( $callable , $strict = true , callable& $norm = null ) if (! is_callable ( $callable )) switch ( true ) case is_object ( $callable ):
$norm = $callable ;
return ‘Closure’ === get_class ( $callable ) ? ‘closure’ : ‘invocable’ ;
case is_string ( $callable ):
$m = null ;
if ( preg_match ( ‘~^(?[a-z_][a-z0-9_]*)::(?[a-z_][a-z0-9_]*)$~i’ , $callable , $m )) list( $left , $right ) = [ $m [ ‘class’ ], $m [ ‘method’ ]];
if (! $strict || (new \ReflectionMethod ( $left , $right ))-> isStatic ()) $norm = [ $left , $right ];
return ‘static’ ;
>
> else $norm = $callable ;
return ‘function’ ;
>
break;
case is_array ( $callable ):
$m = null ;
if ( preg_match ( ‘~^(:?(?self|parent)::)?(?[a-z_][a-z0-9_]*)$~i’ , $callable [ 1 ], $m )) if ( is_string ( $callable [ 0 ])) if ( ‘parent’ === strtolower ( $m [ ‘reference’ ])) list( $left , $right ) = [ get_parent_class ( $callable [ 0 ]), $m [ ‘method’ ]];
> else list( $left , $right ) = [ $callable [ 0 ], $m [ ‘method’ ]];
>
if (! $strict || (new \ReflectionMethod ( $left , $right ))-> isStatic ()) $norm = [ $left , $right ];
return ‘static’ ;
>
> else if ( ‘self’ === strtolower ( $m [ ‘reference’ ])) list( $left , $right ) = [ $callable [ 0 ], $m [ ‘method’ ]];
> else list( $left , $right ) = $callable ;
>
if (! $strict || !(new \ReflectionMethod ( $left , $right ))-> isStatic ()) $norm = [ $left , $right ];
return ‘object’ ;
>
>
>
break;
>
$norm = $callable ;
return ‘unknown’ ;
>
$norm = null ;
return false ;
>

?>

Hope someone else finds it useful.

Объектно-ориентированное программирование

При создании программы на PHP и отдельных ее блоков нам вполне может хватить той функциональности, которую представляют функции. Однако PHP имеет и другие возможности по созданию программ, которые представляет объектно-ориентированное программирование. В ряде случаев программы, использующие ООП, проще в понимании, их легче поддерживать и изменять.

Ключевыми понятиями парадигмы ООП являются понятия «класс» и «объект». Описанием объекта является класс, а объект представляет экземпляр этого класса. Можно провести следующую аналогию: например, у каждого человека есть имя, определенный возраст, вес, какие-то другие параметры. То есть некоторый шаблон, который содержит набор параметров человека — этот шаблон можно назвать классом. А реально же существующий человек с конкретным именем, возрастом, весом и т.д. является объектом или экземпляром этого класса.

Для создания класса в PHP используется ключевое слово class , после которого идет название класса и фигурные скобки <> — блок кода класса. Например, новый класс, представляющий пользователя:

class Person <>

Чтобы создать объект класса Person, применяется ключевое слово new :

 $person = new Person(); print_r($person); ?>

В данном случае переменная $person является объектом класса Person . С помощью функции print_r() можно вывести содержимое объекта, как и в случае с массивами.

При этом неважно, определяется класс до или после создания объекта. Например, мы можем сначала определить переменную класса, а потом определить этот класс:

Свойства и методы

Класс может содержать переменные, которые описывают какие-то признаки объекта, его состояние и которые еще назывют свойствами или атрибутам. И также класс класс может содержать функции, которые еще назвают методами и которые определяют его поведение.

Так, добавим в класс Person несколько свойств и методов:

"; > > $tom = new Person(); $tom->name = "Tom"; // установка свойства $name $tom->age = 36; // установка свойства $age $personName = $tom->name; // получение значения свойства $name echo "Имя пользователя: " . $personName . "
"; $tom->hello(); // вызов метода hello() print_r($tom); ?>

Здесь класс Person содержит два свойства: $name и $age . Свойства объявляются как обычные переменные, перед которыми стоит модификатор доступа — в данном случае модификатор public .

Методы представляют обычные функции, которые выполняют определенные действия. Здесь функция hello() просто выводит приветствие.

После создания объекта класса Person:

$tom = new Person();

Мы можем через имя переменной класса обращаться к его свойствам и методам. Чтобы обратиться к свойствам и методам объекта применяется оператор доступа -> . Например, установить значения свойств:

$tom->name = "Tom"; // установка свойства $name $tom->age = 36; // установка свойства $age

Или получить значение (например, присвоить его переменной):

$personName = $tom->name; // получение значения свойства $name

Или вызвать методы объекта:

$tom->hello(); // вызов метода hello()

В итоге мы получим следующий вывод браузера:

Имя пользователя: Tom Hello! Person Object ( [name] => Tom [age] => 36 )

При этом свойствам можно задать в классе некоторые начальные значения:

"; > > $tom = new Person(); $tom->age = 36; // установка свойства $age echo "Имя пользователя: " . $tom->name . "
"; echo "Возраст пользователя: " . $tom->age . "
"; ?>
Имя пользователя: Undefined Возраст пользователя: 36

Ключевое слово this

Для обращения к свойствам и методам объекта внутри его класса применяется ключевое слово this . Например, определим в классе метод для вывода информации об объекте:

name ."; Age: " . $this->age . "
"; // также можно написать // echo "Name: $this->name; Age: $this->age
"; > > $tom = new Person(); $tom -> name = "Tom"; $tom -> displayInfo(); // Name: Tom; Age: 18 ?>

Для обращения к полям и методам внутри класса также применяется оператор доступа -> , перед которым идет $this . Причем это $this указывает именно на текущий объект. Что это значит в практическом плане? Например:

name; Age: $this->age
"; > > $tom = new Person(); $tom -> name = "Tom"; $tom -> displayInfo(); $bob = new Person(); $bob -> name = "Bob"; $bob -> age = 25; $bob -> displayInfo(); ?>
$tom -> displayInfo();

$this фактически будет указывать на переменную $tom . Тогда как при вызове

$bob -> displayInfo();

$this будет указывать на переменную $bob .

Name: Tom; Age: 18 Name: Bob; Age: 25

Сравнение объектов

При сравнении объектов классов следует принимать во внимание ряд особенностей. В частности, при использовании оператора равенства == два объекта считаются равными, если они представляют один и тот же класс и их свойства имеют одинаковые значения.

А при использовании оператора эквивалентности === оба объекта считаются равными, если обе переменных классах указывают на один и тот же экземпляр класса.

Рассмотрим на примере:

name; Age: $this->age
"; > > $tom = new Person(); $tom -> name = "Tom"; $tom -> age = 36; $tomas = new Person(); $tomas -> name = "Tom"; $tomas -> age = 36; if($tom == $tomas) echo "переменные tom и tomas равны
"; else echo "переменные tom и tomas НЕ равны
"; if($tom === $tomas) echo "переменные tom и tomas эквивалентны"; else echo "переменные tom и tomas НЕ эквивалентны"; ?>

Здесь сравниваются две переменных — $tom и $tomas. Они представляют один и тот же класс Person, и их свойства имеют одни и те же значения. Однако они представляют разные объекты. Поэтому при сравнении оператор == возвратит true , а оператор === — false :

переменные tom и tomas равны переменные tom и tomas НЕ эквивалентны

Возьмем другой пример, когда обе переменных представляют один и тот же объект:

$person = new Person(); $tom = $person; $tom -> name = "Tom"; $tom -> age = 36; $tomas = $person; if($tom == $tomas) echo "переменные tom и tomas равны
"; else echo "переменные tom и tomas НЕ равны
"; if($tom === $tomas) echo "переменные tom и tomas эквивалентны"; else echo "переменные tom и tomas НЕ эквивалентны";

Здесь объект класса Person создается только один раз: $person = new Person(); . И затем обе переменных $tom и $tomas будут указывать на этот объект. При этом не имеет значения, для какой именно переменной мы устанавливаем свойства. Так как в реальности это будет один и тот же объект. В итоге и оператор == , и оператор === при сравнении возвратят true

переменные tom и tomas равны переменные tom и tomas эквивалентны

forward_static_call_array

Вызывает пользовательскую функцию или метод, заданные в параметре callback . Эта функция должна вызываться в контексте метода и не может быть вызвана вне класса. Она использует позднее статическое связывание. Все аргументы пересылаются в метод по значению и в виде массива, аналогично call_user_func_array() .

Список параметров

Функция или метод для вызова. Этот параметр может быть массивом с именем класса и метода, либо строкой с именем функции.

Массив, содержащий все аргументы вызова функции.

Замечание:

Помните, что аргументы forward_static_call_array() не передаются по ссылке.

Возвращаемые значения

Возвращает результат функции или false в случае возникновения ошибки.

Примеры

Пример #1 Пример использования forward_static_call_array()

class A
const NAME = ‘A’ ;
public static function test () $args = func_get_args ();
echo static:: NAME , » » . join ( ‘,’ , $args ). » \n» ;
>
>

class B extends A
const NAME = ‘B’ ;

public static function test () echo self :: NAME , «\n» ;
forward_static_call_array (array( ‘A’ , ‘test’ ), array( ‘more’ , ‘args’ ));
forward_static_call_array ( ‘test’ , array( ‘other’ , ‘args’ ));
>
>

function test () $args = func_get_args ();
echo «C » . join ( ‘,’ , $args ). » \n» ;
>

Результат выполнения приведённого примера:

B B more,args C other,args

Смотрите также

  • forward_static_call() — Вызов статического метода
  • call_user_func() — Вызывает callback-функцию, заданную в первом параметре
  • call_user_func_array() — Вызывает callback-функцию с массивом параметров
  • is_callable() — Проверяет, что значение может быть вызвано как функция в текущей области видимости

User Contributed Notes 2 notes

7 years ago

Be sure to include fully namespaced class path:

forward_static_call_array (
array( ‘NAMESPACE\CLASS_NAME’ , ‘STATIC_METHOD’ ),
$params
);

5 years ago

one of academic example, when forward_static_call() can be useful

class A
public static function test ()
var_dump ( ‘we were here’ );
return static::class;
>
>

class B extends A
public static function test ()
return self ::class;
>
>

class C extends B
public static function test ()
$grandParent = get_parent_class ( parent ::class); // $grandParent is A
return forward_static_call ([ $grandParent , __FUNCTION__ ]); // calls A::test()
>
>

// prints
// string(12) «we were here»
// string(1) «C»
var_dump ( C :: test ());

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *