Closure::fromCallable

(PHP 7 >= 7.1.0)

Closure::fromCallable β€” ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ callable Π² Π·Π°ΠΌΡ‹ΠΊΠ°Π½ΠΈΠ΅

ОписаниС

public static function Closure::fromCallable(callable $callback): Closure

Π‘ΠΎΠ·Π΄Π°Ρ‘Ρ‚ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π½ΠΎΠ²ΡƒΡŽ Π°Π½ΠΎΠ½ΠΈΠΌΠ½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈΠ· Π·Π°Π΄Π°Π½Π½ΠΎΠ³ΠΎ callback, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Ρ‚Π΅ΠΊΡƒΡ‰ΡƒΡŽ ΠΎΠ±Π»Π°ΡΡ‚ΡŒ видимости. Π­Ρ‚ΠΎΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ провСряСт, Ρ‡Ρ‚ΠΎ callback являСтся Ρ‚ΠΈΠΏΠΎΠΌ callable Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ области видимости ΠΈ выбрасываСт ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ TypeError, Ссли это Π½Π΅ Ρ‚Π°ΠΊ.

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:

Начиная с PHP 8.1.0, Ρƒ Callback-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ ΠΊΠ°ΠΊ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ класса Ρ‚Π° ΠΆΠ΅ сСмантика, Ρ‡Ρ‚ΠΎ ΠΈ Ρƒ этого ΠΌΠ΅Ρ‚ΠΎΠ΄Π°.

Бписок ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ²

callback

ΠžΠ±ΡŠΠ΅ΠΊΡ‚ Ρ‚ΠΈΠΏΠ° callable.

Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΡ‹Π΅ значСния

Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ класса Closure ΠΈΠ»ΠΈ выбрасываСт ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ TypeError, Ссли callback Π½Π΅ являСтся ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ Ρ‚ΠΈΠΏΠ° callable Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ области видимости.

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

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

up
13
igorchernin at yahoo dot com ΒΆ
9 years ago
It seems that the result of the "fromCallable" behaves a little bit different then an original Lambda function.

class A {
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
}

// test callable
function getName()
{
      return $this->name;
}
$bob = new A("Bob");

$cl1 = Closure::fromCallable("getName");
$cl1 = $cl1->bindTo($bob, 'A');

//This will retrieve: Uncaught Error: Cannot access private property A::$name 
$result = $cl1();
echo $result;

//But for a Lambda function
$cl2 = function() {
    return $this->name;
};
$cl2 = $cl2->bindTo($bob, 'A');
$result = $cl2();

// This will print Bob
echo $result;
up
5
nakerlund at gmail dot com ΒΆ
8 years ago
I have two points:

It is possible to use Closure::fromCallable to convert private/protected methods to closures and use them outside the class.

Closure::fromCallable accepts late dynamic bindings using the keyword static if provided as a string. 

My code below demonstrate how a private static method can be used as a callback in a function outside the class.

<?php
function myCustomMapper ( Callable $callable, string $str ): string {
  return join(' ', array_map( $callable, explode(' ', $str) ) );
}

class MyClass {

    public static function mapUCFirst ( string $str ): string {
        $privateMethod = 'static::mapper';
        $mapper = Closure::fromCallable( $privateMethod );
        return myCustomMapper( $mapper, $str );
    }
    private static function mapper ( string $str ): string {
        return ucfirst( $str );
    }

}

echo MyClass::mapUCFirst('four little uncapitalized words');
// Prints: Four Little Uncapitalized Words
?>
up
6
4-lom at live dot de ΒΆ
8 years ago
Sadly, your comparison is incorrect.

// The equivalent to
$cl1 = Closure::fromCallable("getName");
$cl1 = $cl1->bindTo($bob, 'A');

// is most likely this
$cl2 = function() {
    return call_user_func_array("getName", func_get_args());
};
$cl2 = $cl2->bindTo($bob, 'A');

Executing one or the other Closure should result in the same access violation error you already postet.

----
A simple PHP 7.0 polyfill could look like this:
----

namespace YourPackage;

/**
 * Class Closure
 *
 * @see \Closure
 */
class Closure
{
    /**
     * @see \Closure::fromCallable()
     * @param callable $callable
     * @return \Closure
     */
    public static function fromCallable(callable $callable)
    {
        // In case we've got it native, let's use that native one!
        if(method_exists(\Closure::class, 'fromCallable')) {
            return \Closure::fromCallable($callable);
        }

        return function () use ($callable) {
            return call_user_func_array($callable, func_get_args());
        };
    }
}