Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния

Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΠΎΠ΅ Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ β€” ссылка Π½Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈΠ»ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄, которая пСрСдаётся Π² Π΄Ρ€ΡƒΠ³ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΊΠ°ΠΊ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚. ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽΡ‚ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния, ΠΎΠ±ΡŠΡΠ²Π»ΡΡŽΡ‚ Ρ‚ΠΈΠΏ callable.

<?php

function foo(callable $callback) {
$callback();
}

ΠžΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π½Π°ΠΏΠΎΠ΄ΠΎΠ±ΠΈΠ΅ array_map(), usort() ΠΈΠ»ΠΈ preg_replace_callback() ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽΡ‚ callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΊΠ°ΠΊ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹.

ОбъявлСниС Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ

Π’ΠΈΠΏ callable прСдставляСт значСния ΠΈΠ»ΠΈ выраТСния, доступныС для Π²Ρ‹Π·ΠΎΠ²Π° ΠΊΠ°ΠΊ функция. Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния Π²Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‚ ΠΊΠ°ΠΊ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠ»ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ ΠΊΠ°ΠΊ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ Π² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠ»ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠΆΠΈΠ΄Π°ΡŽΡ‚ Π² ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π΅ callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ. Бвойствам классов нСльзя ΠΎΠ±ΡŠΡΠ²Π»ΡΡ‚ΡŒ Ρ‚ΠΈΠΏ callable; Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹ΠΌ свойствам ΠΎΠ±ΡŠΡΠ²Π»ΡΡŽΡ‚ Ρ‚ΠΈΠΏ Closure.

Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹ΠΌΠΈ значСниями становятся:

  • ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ класса Closure
  • Π‘Ρ‚Ρ€ΠΎΠΊΠΈ (string) с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠ»ΠΈ ΠΊΠ²Π°Π»ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΌ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°
  • ΠœΠ°ΡΡΠΈΠ²Ρ‹ (array) с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ класса ΠΈΠ»ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ (object) Π² элСмСнтС с индСксом 0 ΠΈ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² элСмСнтС с индСксом 1
  • ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ (object), Π² классС ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… объявили магичСский ΠΌΠ΅Ρ‚ΠΎΠ΄ __invoke()

ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Closure ΡΠΎΠ·Π΄Π°ΡŽΡ‚ синтаксисом Π°Π½ΠΎΠ½ΠΈΠΌΠ½Ρ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ, стрСлочных Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ, синтаксисом пСрвоклассных Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ ΠΈΠ»ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ Closure::fromCallable().

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅: Бинтаксис пСрвоклассных callable-Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ доступСн Ρ‚ΠΎΠ»ΡŒΠΊΠΎ с PHP 8.1.0.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #1 ΠŸΡ€ΠΈΠΌΠ΅Ρ€ объявлСния Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠ³ΠΎ Π²Ρ‹Π·ΠΎΠ²Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ становятся ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌΠΈ класса Closure

<?php

// Бинтаксис Π°Π½ΠΎΠ½ΠΈΠΌΠ½Ρ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ
$double1 = function ($a) {
return
$a * 2;
};

// Бинтаксис пСрвоклассных Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ
function double_function($a) {
return
$a * 2;
}
$double2 = double_function(...);

// Бинтаксис стрСлочных Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ
$double3 = fn($a) => $a * 2;

// ΠœΠ΅Ρ‚ΠΎΠ΄ Closure::fromCallable
$double4 = Closure::fromCallable('double_function');

// Π’Ρ‹Π·ΠΎΠ² замыкания ΠΊΠ°ΠΊ callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ
// ΡƒΠ΄Π²Π°ΠΈΠ²Π°Π΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта Π² Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π΅
$new_numbers = array_map($double1, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double2, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double3, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double4, range(1, 5));
print
implode(' ', $new_numbers);

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π² PHP 8.1:

2 4 6 8 10
2 4 6 8 10
2 4 6 8 10
2 4 6 8 10

Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния ΠΎΠ±ΡŠΡΠ²Π»ΡΡŽΡ‚ ΠΊΠ°ΠΊ строку с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠ»ΠΈ статичСского ΠΌΠ΅Ρ‚ΠΎΠ΄Π°. PHP ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ встроСнных ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ, Π½ΠΎ Π½Π΅ языковых конструкций: array(), echo, empty(), eval(), isset(), list(), print ΠΈΠ»ΠΈ unset().

Для ссылки Π½Π° статичСский ΠΌΠ΅Ρ‚ΠΎΠ΄ класса Π½Π΅ потрСбуСтся ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (object) этого класса, достаточно ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ массив с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ класса Π² элСмСнтС с индСксом 0 ΠΈ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² элСмСнтС с индСксом 1 ΠΈΠ»ΠΈ ΡΠΎΡΠ»Π°Ρ‚ΡŒΡΡ Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄ Ρ‡Π΅Ρ€Π΅Π· синтаксис с ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΠΎΠΌ ::, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Ρ€Π°Π·Ρ€Π΅ΡˆΠ°Π΅Ρ‚ ΠΎΠ±Π»Π°ΡΡ‚ΡŒ дСйствия ΠΌΠ΅Ρ‚ΠΎΠ΄Π°: 'ClassName::methodName'.

На ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² (object) Π² Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… выраТСниях ΡΡΡ‹Π»Π°ΡŽΡ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· массив: ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (object) ΡƒΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚ Π² элСмСнтС с индСксом 0, Π° Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° β€” Π² элСмСнтС с индСксом 1.

Π’ΠΈΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°Ρ… Closure провСряСтся ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ мСста объявлСния, Π° Π½Π΅ Π²Ρ‹Π·ΠΎΠ²Π° замыкания Closure, Ρ‚ΠΎΠ³Π΄Π° ΠΊΠ°ΠΊ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² Π² выраТСниях с псСвдотипом callable провСряСтся ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ мСста Π²Ρ‹Π·ΠΎΠ²Π° выраТСния, поэтому ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ° Π²Ρ‹Π·ΠΎΠ²Π° callable-выраТСния, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ссылаСтся Π½Π° нСдоступный Π² Ρ‚ΠΎΡ‡ΠΊΠ΅ Π²Ρ‹Π·ΠΎΠ²Π° ΠΌΠ΅Ρ‚ΠΎΠ΄, выдаст ΠΎΡˆΠΈΠ±ΠΊΡƒ. На ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ классов Π»ΡƒΡ‡ΡˆΠ΅ ΡΡΡ‹Π»Π°Ρ‚ΡŒΡΡ Ρ‡Π΅Ρ€Π΅Π· замыкания Closure, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² Ρ‚ΠΎΡ‡ΠΊΠ΅ Π²Ρ‹Π·ΠΎΠ²Π° замыкания Π½Π΅ влияСт Π½Π° Π΄ΠΎΡΡ‚ΡƒΠΏΠ½ΠΎΡΡ‚ΡŒ Π²Ρ‹Π·ΠΎΠ²Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Π°.

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅: ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Closure ΡΠ²ΡΠ·Ρ‹Π²Π°ΡŽΡ‚ΡΡ с ΠΎΠ±Π»Π°ΡΡ‚ΡŒΡŽ видимости объявлСния, Ρ‚ΠΎΠ³Π΄Π° ΠΊΠ°ΠΊ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния со ссылкой Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄ класса Ρ‡Π΅Ρ€Π΅Π· строку ΠΈΠ»ΠΈ массив Ρ€Π°Π·Ρ€Π΅ΡˆΠ°ΡŽΡ‚ΡΡ Π² области видимости Π²Ρ‹Π·ΠΎΠ²Π°. Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ со ссылкой Π½Π° Π·Π°ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ ΠΈΠ»ΠΈ Π·Π°Ρ‰ΠΈΡ‰Ρ‘Π½Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ класса, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ потрСбуСтся Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΠ·Π²Π½Π΅ области видимости класса, ΡΠΎΠ·Π΄Π°ΡŽΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ Closure::fromCallable() ΠΈΠ»ΠΈ синтаксисом пСрвоклассных Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ.

PHP ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΊΠ°ΠΊ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚, Π½ΠΎ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ. К Ρ‚Π°ΠΊΠΈΠΌ выраТСниям относятся Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ значСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ зависят ΠΎΡ‚ контСкста ΠΈ ΡΡΡ‹Π»Π°ΡŽΡ‚ΡΡ Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄ класса Π² ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ наслСдования Ρ‡Π΅Ρ€Π΅Π· ΠΊΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ слова: 'parent::method' ΠΈΠ»ΠΈ ["static", "method"].

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅: Π‘ PHP 8.2.0 контСкстно-зависимыС Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ выраТСния устарСли. НСзависимыС ΠΎΡ‚ контСкста ссылки Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°ΡŽΡ‚ ΠΏΡƒΡ‚Ρ‘ΠΌ Π·Π°ΠΌΠ΅Π½Ρ‹ Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π°ΠΏΠΎΠ΄ΠΎΠ±ΠΈΠ΅ 'parent::method' выраТСниями parent::class . '::method' ΠΈΠ»ΠΈ синтаксисом пСрвоклассных Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #2 ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ Π²Ρ‹Π·ΠΎΠ²Π° callable-Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ call_user_function()

<?php

// ΠŸΡ€ΠΈΠΌΠ΅Ρ€ callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ
function my_callback_function()
{
echo
"ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!", PHP_EOL;
}

// ΠŸΡ€ΠΈΠΌΠ΅Ρ€ callback-ΠΌΠ΅Ρ‚ΠΎΠ΄Π°
class MyClass
{
static function
myCallbackMethod()
{
echo
"ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!", PHP_EOL;
}
}

// Π’ΠΈΠΏ 1: ΠŸΡ€ΠΎΡΡ‚ΠΎΠΉ Π²Ρ‹Π·ΠΎΠ² callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ
call_user_func('my_callback_function');

// Π’ΠΈΠΏ 2: Π’Ρ‹Π·ΠΎΠ² статичСского ΠΌΠ΅Ρ‚ΠΎΠ΄Π° класса
call_user_func(['MyClass', 'myCallbackMethod']);

// Π’ΠΈΠΏ 3: Π’Ρ‹Π·ΠΎΠ² ΠΌΠ΅Ρ‚ΠΎΠ΄Π° ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° класса
$obj = new MyClass();
call_user_func([$obj, 'myCallbackMethod']);

// Π’ΠΈΠΏ 4: Π’Ρ‹Π·ΠΎΠ² статичСского ΠΌΠ΅Ρ‚ΠΎΠ΄Π° класса
call_user_func('MyClass::myCallbackMethod');

// Π’ΠΈΠΏ 5: Π’Ρ‹Π·ΠΎΠ² статичСского ΠΌΠ΅Ρ‚ΠΎΠ΄Π° класса с Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ названия класса Ρ‡Π΅Ρ€Π΅Π· ΡΠ·Ρ‹ΠΊΠΎΠ²ΡƒΡŽ ΠΊΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡŽ ::class
call_user_func([MyClass::class, 'myCallbackMethod']);

// Π’ΠΈΠΏ 6: Π’Ρ‹Π·ΠΎΠ² статичСского ΠΌΠ΅Ρ‚ΠΎΠ΄Π° класса ΠΏΠΎ контСкстно-зависимой ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ ссылкС
class A
{
public static function
who()
{
echo
'A', PHP_EOL;
}
}

class
B extends A
{
public static function
who()
{
echo
'B', PHP_EOL;
}
}

call_user_func(['B', 'parent::who']); // Π’Ρ‹Π²ΠΎΠ΄ΠΈΡ‚: A.
// Начиная с PHP 8.2.0 callable-выраТСния
// с ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ названиями ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² устарСли

// Π’ΠΈΠΏ 7: ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, классы ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΡŽΡ‚ магичСский ΠΌΠ΅Ρ‚ΠΎΠ΄ __invoke(),
// доступны для Π²Ρ‹Π·ΠΎΠ²Π° ΠΊΠ°ΠΊ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ
class C
{
public function
__invoke($name)
{
echo
'ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ', $name;
}
}

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

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°:

ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!
ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!
ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!
ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!
ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠΈΡ€!

Deprecated: Callables of the form ["B", "parent::who"] are deprecated in script on line 51
A
ΠŸΡ€ΠΈΠ²Π΅Ρ‚, PHP!

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅: Callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ зарСгистрировали Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ call_user_func() ΠΈΠ»ΠΈ call_user_func_array(), Π½Π΅ вызовутся ΠΏΡ€ΠΈ Π½Π΅ΠΏΠΎΠΉΠΌΠ°Π½Π½ΠΎΠΌ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ выбросила прСдыдущая callback-функция.

οΌ‹Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ 12 notes

up
288
andrewbessa at gmail dot com ΒΆ
13 years ago
You can also use the $this variable to specify a callback:

<?php
class MyClass {

    public $property = 'Hello World!';

    public function MyMethod()
    {
        call_user_func(array($this, 'myCallbackMethod'));
    }

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

}
?>
up
211
steve at mrclay dot org ΒΆ
13 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.
up
193
computrius at gmail dot com ΒΆ
12 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:

<?php

class mc {
   public function go(array $arr) {
       array_walk($arr, array($this, "walkIt"));
   }

   private function walkIt($val) {
       echo $val . "<br />";
   }

    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<br />2<br />3<br />4<br />
Warning: array_walk() expects parameter 2 to be a valid callback, cannot access private method mc::walkIt() in /in/tfh7f on line 22
up
186
Riikka K ΒΆ
11 years ago
A note on differences when calling callbacks as "variable functions" without the use of call_user_func() (e.g. "<?php $callback = 'printf'; $callback('Hello World!') ?>"):

- 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":

<?php
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.
up
187
edanschwartz at gmail dot com ΒΆ
11 years ago
You can use 'self::methodName' as a callable, but this is dangerous. Consider this example:

<?php
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:
<?php
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`.
up
178
metamarkers at gmail dot com ΒΆ
13 years ago
you can pass an object as a callable if its class defines the __invoke() magic method..
up
119
mariano dot REMOVE dot perez dot rodriguez at gmail dot com ΒΆ
10 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:

<?php

/**
 * 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('~^(?<class>[a-z_][a-z0-9_]*)::(?<method>[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('~^(:?(?<reference>self|parent)::)?(?<method>[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.
up
13
InvisibleSmiley ΒΆ
5 years ago
If you pass a callable method to a function with a callable type declaration, the error message is misleading:

<?php
class X {
    protected function foo(): void {}
}

function bar(callable $c) {}

$x = new X;
$c = [$x, 'foo'];
bar($c);
?>

Error message will be something like "Argument #1 ($c) must be of type callable, array given" while the actual problem here is only the visibility of method "foo". All you need to do is changing it to public (or use a different approach, e.g. with a Closure).
up
23
bradyn at NOSPAM dot bradynpoulsen dot com ΒΆ
10 years ago
When trying to make a callable from a function name located in a namespace, you MUST give the fully qualified function name (regardless of the current namespace or use statements).

<?php

namespace MyNamespace;

function doSomethingFancy($arg1)
{
    // do something...
}

$values = [1, 2, 3];

array_map('doSomethingFancy', $values);
// array_map() expects parameter 1 to be a valid callback, function 'doSomethingFancy' not found or invalid function name

array_map('MyNamespace\doSomethingFancy', $values);
// => [..., ..., ...]
up
6
gulaschsuppe2 at gmail dot com ΒΆ
6 years ago
I tried many possible ways of calling functions by function name directly and assigned to a variable on 3v4l. Not mentioned yet, it is possible to use an array as a caller, at least since PHP 7.1.25. The following script contains all the information I gained:

<?php

// Call function via function name:
    // Basics:
        // A function can also be called by using its string name:
        function callbackFunc() {
            echo 'Hello World';
        }

        'callbackFunc'(); // Hello World
                            
        // A function can also be called if its name is assigned to a variable:
            function callbackFunc() {
                echo 'Hello World';
            }

            $funcName = 'callbackFunc';
            $funcName(); // Hello World

    // Static class method:
        // It is also possible to call a public static class method via 'ClassName::functioName' notation:
            class A {
                public static function callbackMethod() {
                    echo "Hello World\n";
                }
            }
            'A::callbackMethod'(); // Hello World

            $funcName = 'A::callbackMethod';
            $funcName(); // Hello World

    // Non static class method:
        // It is also possible to call non static class methods by creating an array which first element is the object the method should be called on and the second element is the non static method to be called. The array can directly be used as a caller:
            class A {
                private $prop = "Hello World\n";

                public function callbackMethod() {
                    echo $this->prop;
                }
            }

            $a = new A;
            [$a, 'callbackMethod']();
            $funcCallArr = [$a, 'callbackMethod'];
            $funcCallArr();

        // Of course this also works inside the class with '$this':
            class A {
                private function privCallback() {
                    echo 'Private';
                }

                public function privCallbackCaller($funcName) {
                    [$this, $funcName]();
                }
            }

            (new A)->privCallbackCaller('privCallback'); // Private

?>
up
1
chris dot rutledge at gmail dot com ΒΆ
7 years ago
Having read this line in the manual above, 

"A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1. Accessing protected and private methods from within a class is allowed."

I decided to do some testing to see if I could access private methods using the call_user_func methods. Thankfully not, but for completeness here is my test which also covers using static and object contexts

<?php
class foo {
    
    public static $isInstance = false;
    
    public function __construct() {
        self::$isInstance = true;
    }

    public function bar() {
        var_dump(self::$isInstance);
        echo __METHOD__;
    }
    
    private function baz() {
        var_dump(self::$isInstance);
        echo __METHOD__;
    }
    
    public function qux() {
        $this->baz();
    }
    
    public function quux() {
        self::baz();
    }
}

call_user_func(['foo','bar']);    //fase, foo:bar

call_user_func(['foo','baz']);  //warning, cannot access private method

call_user_func(['foo','quux']); //false, foo::baz

call_user_func(['foo','qux']);  //fatal, Using $this when not in object context 

$foo = new foo;

call_user_func([$foo,'bar']);    //true, foo::bar
call_user_func([$foo,'baz']);    //warning, cannot access private method
call_user_func([$foo,'qux']);    //true, foo::baz

call_user_func(['foo','bar']);  //true, foo::bar (static call, yet $isInstance is true)

?>
up
1
pawel dot tadeusz dot niedzielski at gmail dot com ΒΆ
10 years ago
@edanschwartz at gmail dot com

You can use ::class property to always indicate the class you're in when using static methods:

<?php
class Foo {
    public static function doAwesomeThings() {
        FunctionCaller::callIt(self::class . '::someAwesomeMethod');
    }

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

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

Foo::doAwesomeThings();
?>